diff options
author | Dan Williams <dcbw@redhat.com> | 2007-10-10 23:56:25 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:03:32 -0500 |
commit | 1e3428e9e327a6e4163b3674b260dbd719f59839 (patch) | |
tree | 98cb9a4e11e72db5da6070c6ff94311097020a13 /drivers/net/wireless | |
parent | 40faacc4078d0fef6daaf6f5d1d332d08631bdd8 (diff) |
orinoco: more reliable scan handling
Bring scan result handling more in line with drivers like ipw. Scan
results are aggregated and a BSS dropped after 15 seconds if no beacon
is received. This allows the driver to interact better with userspace
where more than one process may request scans or results at any time.
Signed-off-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/orinoco.c | 541 | ||||
-rw-r--r-- | drivers/net/wireless/orinoco.h | 12 |
2 files changed, 313 insertions, 240 deletions
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index ca6c2da7bc5d..100ae333df4d 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c | |||
@@ -270,6 +270,37 @@ static inline void set_port_type(struct orinoco_private *priv) | |||
270 | } | 270 | } |
271 | } | 271 | } |
272 | 272 | ||
273 | #define ORINOCO_MAX_BSS_COUNT 64 | ||
274 | static int orinoco_bss_data_allocate(struct orinoco_private *priv) | ||
275 | { | ||
276 | if (priv->bss_data) | ||
277 | return 0; | ||
278 | |||
279 | priv->bss_data = | ||
280 | kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(bss_element), GFP_KERNEL); | ||
281 | if (!priv->bss_data) { | ||
282 | printk(KERN_WARNING "Out of memory allocating beacons"); | ||
283 | return -ENOMEM; | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static void orinoco_bss_data_free(struct orinoco_private *priv) | ||
289 | { | ||
290 | kfree(priv->bss_data); | ||
291 | priv->bss_data = NULL; | ||
292 | } | ||
293 | |||
294 | static void orinoco_bss_data_init(struct orinoco_private *priv) | ||
295 | { | ||
296 | int i; | ||
297 | |||
298 | INIT_LIST_HEAD(&priv->bss_free_list); | ||
299 | INIT_LIST_HEAD(&priv->bss_list); | ||
300 | for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) | ||
301 | list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list); | ||
302 | } | ||
303 | |||
273 | /********************************************************************/ | 304 | /********************************************************************/ |
274 | /* Device methods */ | 305 | /* Device methods */ |
275 | /********************************************************************/ | 306 | /********************************************************************/ |
@@ -1083,6 +1114,121 @@ static void orinoco_send_wevents(struct work_struct *work) | |||
1083 | orinoco_unlock(priv, &flags); | 1114 | orinoco_unlock(priv, &flags); |
1084 | } | 1115 | } |
1085 | 1116 | ||
1117 | |||
1118 | static inline void orinoco_clear_scan_results(struct orinoco_private *priv, | ||
1119 | unsigned long scan_age) | ||
1120 | { | ||
1121 | bss_element *bss; | ||
1122 | bss_element *tmp_bss; | ||
1123 | |||
1124 | /* Blow away current list of scan results */ | ||
1125 | list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { | ||
1126 | if (!scan_age || | ||
1127 | time_after(jiffies, bss->last_scanned + scan_age)) { | ||
1128 | list_move_tail(&bss->list, &priv->bss_free_list); | ||
1129 | /* Don't blow away ->list, just BSS data */ | ||
1130 | memset(bss, 0, sizeof(bss->bss)); | ||
1131 | bss->last_scanned = 0; | ||
1132 | } | ||
1133 | } | ||
1134 | } | ||
1135 | |||
1136 | static int orinoco_process_scan_results(struct net_device *dev, | ||
1137 | unsigned char *buf, | ||
1138 | int len) | ||
1139 | { | ||
1140 | struct orinoco_private *priv = netdev_priv(dev); | ||
1141 | int offset; /* In the scan data */ | ||
1142 | union hermes_scan_info *atom; | ||
1143 | int atom_len; | ||
1144 | |||
1145 | switch (priv->firmware_type) { | ||
1146 | case FIRMWARE_TYPE_AGERE: | ||
1147 | atom_len = sizeof(struct agere_scan_apinfo); | ||
1148 | offset = 0; | ||
1149 | break; | ||
1150 | case FIRMWARE_TYPE_SYMBOL: | ||
1151 | /* Lack of documentation necessitates this hack. | ||
1152 | * Different firmwares have 68 or 76 byte long atoms. | ||
1153 | * We try modulo first. If the length divides by both, | ||
1154 | * we check what would be the channel in the second | ||
1155 | * frame for a 68-byte atom. 76-byte atoms have 0 there. | ||
1156 | * Valid channel cannot be 0. */ | ||
1157 | if (len % 76) | ||
1158 | atom_len = 68; | ||
1159 | else if (len % 68) | ||
1160 | atom_len = 76; | ||
1161 | else if (len >= 1292 && buf[68] == 0) | ||
1162 | atom_len = 76; | ||
1163 | else | ||
1164 | atom_len = 68; | ||
1165 | offset = 0; | ||
1166 | break; | ||
1167 | case FIRMWARE_TYPE_INTERSIL: | ||
1168 | offset = 4; | ||
1169 | if (priv->has_hostscan) { | ||
1170 | atom_len = le16_to_cpup((__le16 *)buf); | ||
1171 | /* Sanity check for atom_len */ | ||
1172 | if (atom_len < sizeof(struct prism2_scan_apinfo)) { | ||
1173 | printk(KERN_ERR "%s: Invalid atom_len in scan " | ||
1174 | "data: %d\n", dev->name, atom_len); | ||
1175 | return -EIO; | ||
1176 | } | ||
1177 | } else | ||
1178 | atom_len = offsetof(struct prism2_scan_apinfo, atim); | ||
1179 | break; | ||
1180 | default: | ||
1181 | return -EOPNOTSUPP; | ||
1182 | } | ||
1183 | |||
1184 | /* Check that we got an whole number of atoms */ | ||
1185 | if ((len - offset) % atom_len) { | ||
1186 | printk(KERN_ERR "%s: Unexpected scan data length %d, " | ||
1187 | "atom_len %d, offset %d\n", dev->name, len, | ||
1188 | atom_len, offset); | ||
1189 | return -EIO; | ||
1190 | } | ||
1191 | |||
1192 | orinoco_clear_scan_results(priv, msecs_to_jiffies(15000)); | ||
1193 | |||
1194 | /* Read the entries one by one */ | ||
1195 | for (; offset + atom_len <= len; offset += atom_len) { | ||
1196 | int found = 0; | ||
1197 | bss_element *bss; | ||
1198 | |||
1199 | /* Get next atom */ | ||
1200 | atom = (union hermes_scan_info *) (buf + offset); | ||
1201 | |||
1202 | /* Try to update an existing bss first */ | ||
1203 | list_for_each_entry(bss, &priv->bss_list, list) { | ||
1204 | if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid)) | ||
1205 | continue; | ||
1206 | if (le16_to_cpu(bss->bss.a.essid_len) != | ||
1207 | le16_to_cpu(atom->a.essid_len)) | ||
1208 | continue; | ||
1209 | if (memcmp(bss->bss.a.essid, atom->a.essid, | ||
1210 | le16_to_cpu(atom->a.essid_len))) | ||
1211 | continue; | ||
1212 | bss->last_scanned = jiffies; | ||
1213 | found = 1; | ||
1214 | break; | ||
1215 | } | ||
1216 | |||
1217 | /* Grab a bss off the free list */ | ||
1218 | if (!found && !list_empty(&priv->bss_free_list)) { | ||
1219 | bss = list_entry(priv->bss_free_list.next, | ||
1220 | bss_element, list); | ||
1221 | list_del(priv->bss_free_list.next); | ||
1222 | |||
1223 | memcpy(bss, atom, sizeof(bss->bss)); | ||
1224 | bss->last_scanned = jiffies; | ||
1225 | list_add_tail(&bss->list, &priv->bss_list); | ||
1226 | } | ||
1227 | } | ||
1228 | |||
1229 | return 0; | ||
1230 | } | ||
1231 | |||
1086 | static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | 1232 | static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) |
1087 | { | 1233 | { |
1088 | struct orinoco_private *priv = netdev_priv(dev); | 1234 | struct orinoco_private *priv = netdev_priv(dev); |
@@ -1208,6 +1354,9 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | |||
1208 | union iwreq_data wrqu; | 1354 | union iwreq_data wrqu; |
1209 | unsigned char *buf; | 1355 | unsigned char *buf; |
1210 | 1356 | ||
1357 | /* Scan is no longer in progress */ | ||
1358 | priv->scan_inprogress = 0; | ||
1359 | |||
1211 | /* Sanity check */ | 1360 | /* Sanity check */ |
1212 | if (len > 4096) { | 1361 | if (len > 4096) { |
1213 | printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", | 1362 | printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", |
@@ -1215,15 +1364,6 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | |||
1215 | break; | 1364 | break; |
1216 | } | 1365 | } |
1217 | 1366 | ||
1218 | /* We are a strict producer. If the previous scan results | ||
1219 | * have not been consumed, we just have to drop this | ||
1220 | * frame. We can't remove the previous results ourselves, | ||
1221 | * that would be *very* racy... Jean II */ | ||
1222 | if (priv->scan_result != NULL) { | ||
1223 | printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name); | ||
1224 | break; | ||
1225 | } | ||
1226 | |||
1227 | /* Allocate buffer for results */ | 1367 | /* Allocate buffer for results */ |
1228 | buf = kmalloc(len, GFP_ATOMIC); | 1368 | buf = kmalloc(len, GFP_ATOMIC); |
1229 | if (buf == NULL) | 1369 | if (buf == NULL) |
@@ -1248,18 +1388,17 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) | |||
1248 | } | 1388 | } |
1249 | #endif /* ORINOCO_DEBUG */ | 1389 | #endif /* ORINOCO_DEBUG */ |
1250 | 1390 | ||
1251 | /* Allow the clients to access the results */ | 1391 | if (orinoco_process_scan_results(dev, buf, len) == 0) { |
1252 | priv->scan_len = len; | 1392 | /* Send an empty event to user space. |
1253 | priv->scan_result = buf; | 1393 | * We don't send the received data on the event because |
1254 | 1394 | * it would require us to do complex transcoding, and | |
1255 | /* Send an empty event to user space. | 1395 | * we want to minimise the work done in the irq handler |
1256 | * We don't send the received data on the event because | 1396 | * Use a request to extract the data - Jean II */ |
1257 | * it would require us to do complex transcoding, and | 1397 | wrqu.data.length = 0; |
1258 | * we want to minimise the work done in the irq handler | 1398 | wrqu.data.flags = 0; |
1259 | * Use a request to extract the data - Jean II */ | 1399 | wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); |
1260 | wrqu.data.length = 0; | 1400 | } |
1261 | wrqu.data.flags = 0; | 1401 | kfree(buf); |
1262 | wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); | ||
1263 | } | 1402 | } |
1264 | break; | 1403 | break; |
1265 | case HERMES_INQ_SEC_STAT_AGERE: | 1404 | case HERMES_INQ_SEC_STAT_AGERE: |
@@ -1896,8 +2035,7 @@ static void orinoco_reset(struct work_struct *work) | |||
1896 | orinoco_unlock(priv, &flags); | 2035 | orinoco_unlock(priv, &flags); |
1897 | 2036 | ||
1898 | /* Scanning support: Cleanup of driver struct */ | 2037 | /* Scanning support: Cleanup of driver struct */ |
1899 | kfree(priv->scan_result); | 2038 | orinoco_clear_scan_results(priv, 0); |
1900 | priv->scan_result = NULL; | ||
1901 | priv->scan_inprogress = 0; | 2039 | priv->scan_inprogress = 0; |
1902 | 2040 | ||
1903 | if (priv->hard_reset) { | 2041 | if (priv->hard_reset) { |
@@ -2412,6 +2550,10 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2412 | else | 2550 | else |
2413 | priv->card = NULL; | 2551 | priv->card = NULL; |
2414 | 2552 | ||
2553 | if (orinoco_bss_data_allocate(priv)) | ||
2554 | goto err_out_free; | ||
2555 | orinoco_bss_data_init(priv); | ||
2556 | |||
2415 | /* Setup / override net_device fields */ | 2557 | /* Setup / override net_device fields */ |
2416 | dev->init = orinoco_init; | 2558 | dev->init = orinoco_init; |
2417 | dev->hard_start_xmit = orinoco_xmit; | 2559 | dev->hard_start_xmit = orinoco_xmit; |
@@ -2447,13 +2589,16 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2447 | 2589 | ||
2448 | return dev; | 2590 | return dev; |
2449 | 2591 | ||
2592 | err_out_free: | ||
2593 | free_netdev(dev); | ||
2594 | return NULL; | ||
2450 | } | 2595 | } |
2451 | 2596 | ||
2452 | void free_orinocodev(struct net_device *dev) | 2597 | void free_orinocodev(struct net_device *dev) |
2453 | { | 2598 | { |
2454 | struct orinoco_private *priv = netdev_priv(dev); | 2599 | struct orinoco_private *priv = netdev_priv(dev); |
2455 | 2600 | ||
2456 | kfree(priv->scan_result); | 2601 | orinoco_bss_data_free(priv); |
2457 | free_netdev(dev); | 2602 | free_netdev(dev); |
2458 | } | 2603 | } |
2459 | 2604 | ||
@@ -3841,23 +3986,10 @@ static int orinoco_ioctl_setscan(struct net_device *dev, | |||
3841 | * we access scan variables in priv is critical. | 3986 | * we access scan variables in priv is critical. |
3842 | * o scan_inprogress : not touched by irq handler | 3987 | * o scan_inprogress : not touched by irq handler |
3843 | * o scan_mode : not touched by irq handler | 3988 | * o scan_mode : not touched by irq handler |
3844 | * o scan_result : irq is strict producer, non-irq is strict | ||
3845 | * consumer. | ||
3846 | * o scan_len : synchronised with scan_result | 3989 | * o scan_len : synchronised with scan_result |
3847 | * Before modifying anything on those variables, please think hard ! | 3990 | * Before modifying anything on those variables, please think hard ! |
3848 | * Jean II */ | 3991 | * Jean II */ |
3849 | 3992 | ||
3850 | /* If there is still some left-over scan results, get rid of it */ | ||
3851 | if (priv->scan_result != NULL) { | ||
3852 | /* What's likely is that a client did crash or was killed | ||
3853 | * between triggering the scan request and reading the | ||
3854 | * results, so we need to reset everything. | ||
3855 | * Some clients that are too slow may suffer from that... | ||
3856 | * Jean II */ | ||
3857 | kfree(priv->scan_result); | ||
3858 | priv->scan_result = NULL; | ||
3859 | } | ||
3860 | |||
3861 | /* Save flags */ | 3993 | /* Save flags */ |
3862 | priv->scan_mode = srq->flags; | 3994 | priv->scan_mode = srq->flags; |
3863 | 3995 | ||
@@ -3905,169 +4037,125 @@ static int orinoco_ioctl_setscan(struct net_device *dev, | |||
3905 | return err; | 4037 | return err; |
3906 | } | 4038 | } |
3907 | 4039 | ||
4040 | #define MAX_CUSTOM_LEN 64 | ||
4041 | |||
3908 | /* Translate scan data returned from the card to a card independant | 4042 | /* Translate scan data returned from the card to a card independant |
3909 | * format that the Wireless Tools will understand - Jean II | 4043 | * format that the Wireless Tools will understand - Jean II |
3910 | * Return message length or -errno for fatal errors */ | 4044 | * Return message length or -errno for fatal errors */ |
3911 | static inline int orinoco_translate_scan(struct net_device *dev, | 4045 | static inline char *orinoco_translate_scan(struct net_device *dev, |
3912 | char *buffer, | 4046 | char *current_ev, |
3913 | char *scan, | 4047 | char *end_buf, |
3914 | int scan_len) | 4048 | union hermes_scan_info *bss, |
4049 | unsigned int last_scanned) | ||
3915 | { | 4050 | { |
3916 | struct orinoco_private *priv = netdev_priv(dev); | 4051 | struct orinoco_private *priv = netdev_priv(dev); |
3917 | int offset; /* In the scan data */ | ||
3918 | union hermes_scan_info *atom; | ||
3919 | int atom_len; | ||
3920 | u16 capabilities; | 4052 | u16 capabilities; |
3921 | u16 channel; | 4053 | u16 channel; |
3922 | struct iw_event iwe; /* Temporary buffer */ | 4054 | struct iw_event iwe; /* Temporary buffer */ |
3923 | char * current_ev = buffer; | 4055 | char *p; |
3924 | char * end_buf = buffer + IW_SCAN_MAX_DATA; | 4056 | char custom[MAX_CUSTOM_LEN]; |
3925 | 4057 | ||
3926 | switch (priv->firmware_type) { | 4058 | /* First entry *MUST* be the AP MAC address */ |
3927 | case FIRMWARE_TYPE_AGERE: | 4059 | iwe.cmd = SIOCGIWAP; |
3928 | atom_len = sizeof(struct agere_scan_apinfo); | 4060 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; |
3929 | offset = 0; | 4061 | memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); |
3930 | break; | 4062 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); |
3931 | case FIRMWARE_TYPE_SYMBOL: | 4063 | |
3932 | /* Lack of documentation necessitates this hack. | 4064 | /* Other entries will be displayed in the order we give them */ |
3933 | * Different firmwares have 68 or 76 byte long atoms. | 4065 | |
3934 | * We try modulo first. If the length divides by both, | 4066 | /* Add the ESSID */ |
3935 | * we check what would be the channel in the second | 4067 | iwe.u.data.length = le16_to_cpu(bss->a.essid_len); |
3936 | * frame for a 68-byte atom. 76-byte atoms have 0 there. | 4068 | if (iwe.u.data.length > 32) |
3937 | * Valid channel cannot be 0. */ | 4069 | iwe.u.data.length = 32; |
3938 | if (scan_len % 76) | 4070 | iwe.cmd = SIOCGIWESSID; |
3939 | atom_len = 68; | 4071 | iwe.u.data.flags = 1; |
3940 | else if (scan_len % 68) | 4072 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->a.essid); |
3941 | atom_len = 76; | 4073 | |
3942 | else if (scan_len >= 1292 && scan[68] == 0) | 4074 | /* Add mode */ |
3943 | atom_len = 76; | 4075 | iwe.cmd = SIOCGIWMODE; |
4076 | capabilities = le16_to_cpu(bss->a.capabilities); | ||
4077 | if (capabilities & 0x3) { | ||
4078 | if (capabilities & 0x1) | ||
4079 | iwe.u.mode = IW_MODE_MASTER; | ||
3944 | else | 4080 | else |
3945 | atom_len = 68; | 4081 | iwe.u.mode = IW_MODE_ADHOC; |
3946 | offset = 0; | 4082 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); |
3947 | break; | 4083 | } |
3948 | case FIRMWARE_TYPE_INTERSIL: | 4084 | |
3949 | offset = 4; | 4085 | channel = bss->s.channel; |
3950 | if (priv->has_hostscan) { | 4086 | if ((channel >= 1) && (channel <= NUM_CHANNELS)) { |
3951 | atom_len = le16_to_cpup((__le16 *)scan); | 4087 | /* Add frequency */ |
3952 | /* Sanity check for atom_len */ | 4088 | iwe.cmd = SIOCGIWFREQ; |
3953 | if (atom_len < sizeof(struct prism2_scan_apinfo)) { | 4089 | iwe.u.freq.m = channel_frequency[channel-1] * 100000; |
3954 | printk(KERN_ERR "%s: Invalid atom_len in scan data: %d\n", | 4090 | iwe.u.freq.e = 1; |
3955 | dev->name, atom_len); | 4091 | current_ev = iwe_stream_add_event(current_ev, end_buf, |
3956 | return -EIO; | 4092 | &iwe, IW_EV_FREQ_LEN); |
3957 | } | 4093 | } |
3958 | } else | 4094 | |
3959 | atom_len = offsetof(struct prism2_scan_apinfo, atim); | 4095 | /* Add quality statistics */ |
3960 | break; | 4096 | iwe.cmd = IWEVQUAL; |
3961 | default: | 4097 | iwe.u.qual.updated = 0x10; /* no link quality */ |
3962 | return -EOPNOTSUPP; | 4098 | iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95; |
3963 | } | 4099 | iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95; |
3964 | 4100 | /* Wireless tools prior to 27.pre22 will show link quality | |
3965 | /* Check that we got an whole number of atoms */ | 4101 | * anyway, so we provide a reasonable value. */ |
3966 | if ((scan_len - offset) % atom_len) { | 4102 | if (iwe.u.qual.level > iwe.u.qual.noise) |
3967 | printk(KERN_ERR "%s: Unexpected scan data length %d, " | 4103 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; |
3968 | "atom_len %d, offset %d\n", dev->name, scan_len, | 4104 | else |
3969 | atom_len, offset); | 4105 | iwe.u.qual.qual = 0; |
3970 | return -EIO; | 4106 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); |
3971 | } | ||
3972 | |||
3973 | /* Read the entries one by one */ | ||
3974 | for (; offset + atom_len <= scan_len; offset += atom_len) { | ||
3975 | /* Get next atom */ | ||
3976 | atom = (union hermes_scan_info *) (scan + offset); | ||
3977 | |||
3978 | /* First entry *MUST* be the AP MAC address */ | ||
3979 | iwe.cmd = SIOCGIWAP; | ||
3980 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
3981 | memcpy(iwe.u.ap_addr.sa_data, atom->a.bssid, ETH_ALEN); | ||
3982 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); | ||
3983 | |||
3984 | /* Other entries will be displayed in the order we give them */ | ||
3985 | |||
3986 | /* Add the ESSID */ | ||
3987 | iwe.u.data.length = le16_to_cpu(atom->a.essid_len); | ||
3988 | if (iwe.u.data.length > 32) | ||
3989 | iwe.u.data.length = 32; | ||
3990 | iwe.cmd = SIOCGIWESSID; | ||
3991 | iwe.u.data.flags = 1; | ||
3992 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid); | ||
3993 | |||
3994 | /* Add mode */ | ||
3995 | iwe.cmd = SIOCGIWMODE; | ||
3996 | capabilities = le16_to_cpu(atom->a.capabilities); | ||
3997 | if (capabilities & 0x3) { | ||
3998 | if (capabilities & 0x1) | ||
3999 | iwe.u.mode = IW_MODE_MASTER; | ||
4000 | else | ||
4001 | iwe.u.mode = IW_MODE_ADHOC; | ||
4002 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN); | ||
4003 | } | ||
4004 | |||
4005 | channel = atom->s.channel; | ||
4006 | if ( (channel >= 1) && (channel <= NUM_CHANNELS) ) { | ||
4007 | /* Add frequency */ | ||
4008 | iwe.cmd = SIOCGIWFREQ; | ||
4009 | iwe.u.freq.m = channel_frequency[channel-1] * 100000; | ||
4010 | iwe.u.freq.e = 1; | ||
4011 | current_ev = iwe_stream_add_event(current_ev, end_buf, | ||
4012 | &iwe, IW_EV_FREQ_LEN); | ||
4013 | } | ||
4014 | 4107 | ||
4015 | /* Add quality statistics */ | 4108 | /* Add encryption capability */ |
4016 | iwe.cmd = IWEVQUAL; | 4109 | iwe.cmd = SIOCGIWENCODE; |
4017 | iwe.u.qual.updated = 0x10; /* no link quality */ | 4110 | if (capabilities & 0x10) |
4018 | iwe.u.qual.level = (__u8) le16_to_cpu(atom->a.level) - 0x95; | 4111 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; |
4019 | iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.noise) - 0x95; | 4112 | else |
4020 | /* Wireless tools prior to 27.pre22 will show link quality | 4113 | iwe.u.data.flags = IW_ENCODE_DISABLED; |
4021 | * anyway, so we provide a reasonable value. */ | 4114 | iwe.u.data.length = 0; |
4022 | if (iwe.u.qual.level > iwe.u.qual.noise) | 4115 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->a.essid); |
4023 | iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; | 4116 | |
4024 | else | 4117 | /* Add EXTRA: Age to display seconds since last beacon/probe response |
4025 | iwe.u.qual.qual = 0; | 4118 | * for given network. */ |
4026 | current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); | 4119 | iwe.cmd = IWEVCUSTOM; |
4120 | p = custom; | ||
4121 | p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), | ||
4122 | " Last beacon: %dms ago", | ||
4123 | jiffies_to_msecs(jiffies - last_scanned)); | ||
4124 | iwe.u.data.length = p - custom; | ||
4125 | if (iwe.u.data.length) | ||
4126 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, custom); | ||
4127 | |||
4128 | /* Bit rate is not available in Lucent/Agere firmwares */ | ||
4129 | if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { | ||
4130 | char *current_val = current_ev + IW_EV_LCP_LEN; | ||
4131 | int i; | ||
4132 | int step; | ||
4027 | 4133 | ||
4028 | /* Add encryption capability */ | 4134 | if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) |
4029 | iwe.cmd = SIOCGIWENCODE; | 4135 | step = 2; |
4030 | if (capabilities & 0x10) | ||
4031 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
4032 | else | 4136 | else |
4033 | iwe.u.data.flags = IW_ENCODE_DISABLED; | 4137 | step = 1; |
4034 | iwe.u.data.length = 0; | 4138 | |
4035 | current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid); | 4139 | iwe.cmd = SIOCGIWRATE; |
4036 | 4140 | /* Those two flags are ignored... */ | |
4037 | /* Bit rate is not available in Lucent/Agere firmwares */ | 4141 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; |
4038 | if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { | 4142 | /* Max 10 values */ |
4039 | char * current_val = current_ev + IW_EV_LCP_LEN; | 4143 | for (i = 0; i < 10; i += step) { |
4040 | int i; | 4144 | /* NULL terminated */ |
4041 | int step; | 4145 | if (bss->p.rates[i] == 0x0) |
4042 | 4146 | break; | |
4043 | if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) | 4147 | /* Bit rate given in 500 kb/s units (+ 0x80) */ |
4044 | step = 2; | 4148 | iwe.u.bitrate.value = ((bss->p.rates[i] & 0x7f) * 500000); |
4045 | else | 4149 | current_val = iwe_stream_add_value(current_ev, current_val, |
4046 | step = 1; | 4150 | end_buf, &iwe, |
4047 | 4151 | IW_EV_PARAM_LEN); | |
4048 | iwe.cmd = SIOCGIWRATE; | ||
4049 | /* Those two flags are ignored... */ | ||
4050 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
4051 | /* Max 10 values */ | ||
4052 | for (i = 0; i < 10; i += step) { | ||
4053 | /* NULL terminated */ | ||
4054 | if (atom->p.rates[i] == 0x0) | ||
4055 | break; | ||
4056 | /* Bit rate given in 500 kb/s units (+ 0x80) */ | ||
4057 | iwe.u.bitrate.value = ((atom->p.rates[i] & 0x7f) * 500000); | ||
4058 | current_val = iwe_stream_add_value(current_ev, current_val, | ||
4059 | end_buf, &iwe, | ||
4060 | IW_EV_PARAM_LEN); | ||
4061 | } | ||
4062 | /* Check if we added any event */ | ||
4063 | if ((current_val - current_ev) > IW_EV_LCP_LEN) | ||
4064 | current_ev = current_val; | ||
4065 | } | 4152 | } |
4066 | 4153 | /* Check if we added any event */ | |
4067 | /* The other data in the scan result are not really | 4154 | if ((current_val - current_ev) > IW_EV_LCP_LEN) |
4068 | * interesting, so for now drop it - Jean II */ | 4155 | current_ev = current_val; |
4069 | } | 4156 | } |
4070 | return current_ev - buffer; | 4157 | |
4158 | return current_ev; | ||
4071 | } | 4159 | } |
4072 | 4160 | ||
4073 | /* Return results of a scan */ | 4161 | /* Return results of a scan */ |
@@ -4077,68 +4165,45 @@ static int orinoco_ioctl_getscan(struct net_device *dev, | |||
4077 | char *extra) | 4165 | char *extra) |
4078 | { | 4166 | { |
4079 | struct orinoco_private *priv = netdev_priv(dev); | 4167 | struct orinoco_private *priv = netdev_priv(dev); |
4168 | bss_element *bss; | ||
4080 | int err = 0; | 4169 | int err = 0; |
4081 | unsigned long flags; | 4170 | unsigned long flags; |
4171 | char *current_ev = extra; | ||
4082 | 4172 | ||
4083 | if (orinoco_lock(priv, &flags) != 0) | 4173 | if (orinoco_lock(priv, &flags) != 0) |
4084 | return -EBUSY; | 4174 | return -EBUSY; |
4085 | 4175 | ||
4086 | /* If no results yet, ask to try again later */ | 4176 | if (priv->scan_inprogress) { |
4087 | if (priv->scan_result == NULL) { | 4177 | /* Important note : we don't want to block the caller |
4088 | if (priv->scan_inprogress) | 4178 | * until results are ready for various reasons. |
4089 | /* Important note : we don't want to block the caller | 4179 | * First, managing wait queues is complex and racy. |
4090 | * until results are ready for various reasons. | 4180 | * Second, we grab some rtnetlink lock before comming |
4091 | * First, managing wait queues is complex and racy. | 4181 | * here (in dev_ioctl()). |
4092 | * Second, we grab some rtnetlink lock before comming | 4182 | * Third, we generate an Wireless Event, so the |
4093 | * here (in dev_ioctl()). | 4183 | * caller can wait itself on that - Jean II */ |
4094 | * Third, we generate an Wireless Event, so the | 4184 | err = -EAGAIN; |
4095 | * caller can wait itself on that - Jean II */ | 4185 | goto out; |
4096 | err = -EAGAIN; | 4186 | } |
4097 | else | ||
4098 | /* Client error, no scan results... | ||
4099 | * The caller need to restart the scan. */ | ||
4100 | err = -ENODATA; | ||
4101 | } else { | ||
4102 | /* We have some results to push back to user space */ | ||
4103 | |||
4104 | /* Translate to WE format */ | ||
4105 | int ret = orinoco_translate_scan(dev, extra, | ||
4106 | priv->scan_result, | ||
4107 | priv->scan_len); | ||
4108 | |||
4109 | if (ret < 0) { | ||
4110 | err = ret; | ||
4111 | kfree(priv->scan_result); | ||
4112 | priv->scan_result = NULL; | ||
4113 | } else { | ||
4114 | srq->length = ret; | ||
4115 | 4187 | ||
4116 | /* Return flags */ | 4188 | list_for_each_entry(bss, &priv->bss_list, list) { |
4117 | srq->flags = (__u16) priv->scan_mode; | 4189 | /* Translate to WE format this entry */ |
4190 | current_ev = orinoco_translate_scan(dev, current_ev, | ||
4191 | extra + srq->length, | ||
4192 | &bss->bss, | ||
4193 | bss->last_scanned); | ||
4118 | 4194 | ||
4119 | /* In any case, Scan results will be cleaned up in the | 4195 | /* Check if there is space for one more entry */ |
4120 | * reset function and when exiting the driver. | 4196 | if ((extra + srq->length - current_ev) <= IW_EV_ADDR_LEN) { |
4121 | * The person triggering the scanning may never come to | 4197 | /* Ask user space to try again with a bigger buffer */ |
4122 | * pick the results, so we need to do it in those places. | 4198 | err = -E2BIG; |
4123 | * Jean II */ | 4199 | goto out; |
4124 | |||
4125 | #ifdef SCAN_SINGLE_READ | ||
4126 | /* If you enable this option, only one client (the first | ||
4127 | * one) will be able to read the result (and only one | ||
4128 | * time). If there is multiple concurent clients that | ||
4129 | * want to read scan results, this behavior is not | ||
4130 | * advisable - Jean II */ | ||
4131 | kfree(priv->scan_result); | ||
4132 | priv->scan_result = NULL; | ||
4133 | #endif /* SCAN_SINGLE_READ */ | ||
4134 | /* Here, if too much time has elapsed since last scan, | ||
4135 | * we may want to clean up scan results... - Jean II */ | ||
4136 | } | 4200 | } |
4137 | |||
4138 | /* Scan is no longer in progress */ | ||
4139 | priv->scan_inprogress = 0; | ||
4140 | } | 4201 | } |
4141 | 4202 | ||
4203 | srq->length = (current_ev - extra); | ||
4204 | srq->flags = (__u16) priv->scan_mode; | ||
4205 | |||
4206 | out: | ||
4142 | orinoco_unlock(priv, &flags); | 4207 | orinoco_unlock(priv, &flags); |
4143 | return err; | 4208 | return err; |
4144 | } | 4209 | } |
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h index 4720fb20d66d..c6b1858abde8 100644 --- a/drivers/net/wireless/orinoco.h +++ b/drivers/net/wireless/orinoco.h | |||
@@ -36,6 +36,12 @@ typedef enum { | |||
36 | FIRMWARE_TYPE_SYMBOL | 36 | FIRMWARE_TYPE_SYMBOL |
37 | } fwtype_t; | 37 | } fwtype_t; |
38 | 38 | ||
39 | typedef struct { | ||
40 | union hermes_scan_info bss; | ||
41 | unsigned long last_scanned; | ||
42 | struct list_head list; | ||
43 | } bss_element; | ||
44 | |||
39 | struct orinoco_private { | 45 | struct orinoco_private { |
40 | void *card; /* Pointer to card dependent structure */ | 46 | void *card; /* Pointer to card dependent structure */ |
41 | int (*hard_reset)(struct orinoco_private *); | 47 | int (*hard_reset)(struct orinoco_private *); |
@@ -105,10 +111,12 @@ struct orinoco_private { | |||
105 | int promiscuous, mc_count; | 111 | int promiscuous, mc_count; |
106 | 112 | ||
107 | /* Scanning support */ | 113 | /* Scanning support */ |
114 | struct list_head bss_list; | ||
115 | struct list_head bss_free_list; | ||
116 | bss_element *bss_data; | ||
117 | |||
108 | int scan_inprogress; /* Scan pending... */ | 118 | int scan_inprogress; /* Scan pending... */ |
109 | u32 scan_mode; /* Type of scan done */ | 119 | u32 scan_mode; /* Type of scan done */ |
110 | char * scan_result; /* Result of previous scan */ | ||
111 | int scan_len; /* Lenght of result */ | ||
112 | }; | 120 | }; |
113 | 121 | ||
114 | #ifdef ORINOCO_DEBUG | 122 | #ifdef ORINOCO_DEBUG |