2 * Functions implementing wlan scan IOCTL and firmware command APIs
4 * IOCTL handlers as well as command preperation and response routines
5 * for sending scan commands to the firmware.
7 #include <linux/types.h>
8 #include <linux/kernel.h>
9 #include <linux/etherdevice.h>
10 #include <linux/if_arp.h>
11 #include <asm/unaligned.h>
12 #include <net/lib80211.h>
21 //! Approximate amount of data needed to pass a scan result back to iwlist
22 #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \
23 + IEEE80211_MAX_SSID_LEN \
27 + IEEE80211_MAX_SSID_LEN \
29 + 40) /* 40 for WPAIE */
31 //! Memory needed to store a max sized channel List TLV for a firmware scan
32 #define CHAN_TLV_MAX_SIZE (sizeof(struct mrvl_ie_header) \
33 + (MRVDRV_MAX_CHANNELS_PER_SCAN \
34 * sizeof(struct chanscanparamset)))
36 //! Memory needed to store a max number/size SSID TLV for a firmware scan
37 #define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvl_ie_ssid_param_set))
39 //! Maximum memory needed for a cmd_ds_802_11_scan with all TLVs at max
40 #define MAX_SCAN_CFG_ALLOC (sizeof(struct cmd_ds_802_11_scan) \
41 + CHAN_TLV_MAX_SIZE + SSID_TLV_MAX_SIZE)
43 //! The maximum number of channels the firmware can scan per command
44 #define MRVDRV_MAX_CHANNELS_PER_SCAN 14
47 * @brief Number of channels to scan per firmware scan command issuance.
49 * Number restricted to prevent hitting the limit on the amount of scan data
50 * returned in a single firmware scan command.
52 #define MRVDRV_CHANNELS_PER_SCAN_CMD 4
54 //! Scan time specified in the channel TLV for each channel for passive scans
55 #define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100
57 //! Scan time specified in the channel TLV for each channel for active scans
58 #define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100
60 #define DEFAULT_MAX_SCAN_AGE (15 * HZ)
62 static int lbs_ret_80211_scan(struct lbs_private
*priv
, unsigned long dummy
,
63 struct cmd_header
*resp
);
65 /*********************************************************************/
67 /* Misc helper functions */
69 /*********************************************************************/
72 * @brief Unsets the MSB on basic rates
74 * Scan through an array and unset the MSB for basic data rates.
76 * @param rates buffer of data rates
77 * @param len size of buffer
79 static void lbs_unset_basic_rate_flags(u8
*rates
, size_t len
)
83 for (i
= 0; i
< len
; i
++)
88 static inline void clear_bss_descriptor(struct bss_descriptor
*bss
)
90 /* Don't blow away ->list, just BSS data */
91 memset(bss
, 0, offsetof(struct bss_descriptor
, list
));
95 * @brief Compare two SSIDs
97 * @param ssid1 A pointer to ssid to compare
98 * @param ssid2 A pointer to ssid to compare
100 * @return 0: ssid is same, otherwise is different
102 int lbs_ssid_cmp(uint8_t *ssid1
, uint8_t ssid1_len
, uint8_t *ssid2
,
105 if (ssid1_len
!= ssid2_len
)
108 return memcmp(ssid1
, ssid2
, ssid1_len
);
111 static inline int is_same_network(struct bss_descriptor
*src
,
112 struct bss_descriptor
*dst
)
114 /* A network is only a duplicate if the channel, BSSID, and ESSID
115 * all match. We treat all <hidden> with the same BSSID and channel
117 return ((src
->ssid_len
== dst
->ssid_len
) &&
118 (src
->channel
== dst
->channel
) &&
119 !compare_ether_addr(src
->bssid
, dst
->bssid
) &&
120 !memcmp(src
->ssid
, dst
->ssid
, src
->ssid_len
));
125 /*********************************************************************/
127 /* Region channel support */
129 /*********************************************************************/
131 #define LBS_TX_PWR_DEFAULT 20 /*100mW */
132 #define LBS_TX_PWR_US_DEFAULT 20 /*100mW */
133 #define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */
134 #define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */
135 #define LBS_TX_PWR_EMEA_DEFAULT 20 /*100mW */
137 /* Format { channel, frequency (MHz), maxtxpower } */
138 /* band: 'B/G', region: USA FCC/Canada IC */
139 static struct chan_freq_power channel_freq_power_US_BG
[] = {
140 {1, 2412, LBS_TX_PWR_US_DEFAULT
},
141 {2, 2417, LBS_TX_PWR_US_DEFAULT
},
142 {3, 2422, LBS_TX_PWR_US_DEFAULT
},
143 {4, 2427, LBS_TX_PWR_US_DEFAULT
},
144 {5, 2432, LBS_TX_PWR_US_DEFAULT
},
145 {6, 2437, LBS_TX_PWR_US_DEFAULT
},
146 {7, 2442, LBS_TX_PWR_US_DEFAULT
},
147 {8, 2447, LBS_TX_PWR_US_DEFAULT
},
148 {9, 2452, LBS_TX_PWR_US_DEFAULT
},
149 {10, 2457, LBS_TX_PWR_US_DEFAULT
},
150 {11, 2462, LBS_TX_PWR_US_DEFAULT
}
153 /* band: 'B/G', region: Europe ETSI */
154 static struct chan_freq_power channel_freq_power_EU_BG
[] = {
155 {1, 2412, LBS_TX_PWR_EMEA_DEFAULT
},
156 {2, 2417, LBS_TX_PWR_EMEA_DEFAULT
},
157 {3, 2422, LBS_TX_PWR_EMEA_DEFAULT
},
158 {4, 2427, LBS_TX_PWR_EMEA_DEFAULT
},
159 {5, 2432, LBS_TX_PWR_EMEA_DEFAULT
},
160 {6, 2437, LBS_TX_PWR_EMEA_DEFAULT
},
161 {7, 2442, LBS_TX_PWR_EMEA_DEFAULT
},
162 {8, 2447, LBS_TX_PWR_EMEA_DEFAULT
},
163 {9, 2452, LBS_TX_PWR_EMEA_DEFAULT
},
164 {10, 2457, LBS_TX_PWR_EMEA_DEFAULT
},
165 {11, 2462, LBS_TX_PWR_EMEA_DEFAULT
},
166 {12, 2467, LBS_TX_PWR_EMEA_DEFAULT
},
167 {13, 2472, LBS_TX_PWR_EMEA_DEFAULT
}
170 /* band: 'B/G', region: Spain */
171 static struct chan_freq_power channel_freq_power_SPN_BG
[] = {
172 {10, 2457, LBS_TX_PWR_DEFAULT
},
173 {11, 2462, LBS_TX_PWR_DEFAULT
}
176 /* band: 'B/G', region: France */
177 static struct chan_freq_power channel_freq_power_FR_BG
[] = {
178 {10, 2457, LBS_TX_PWR_FR_DEFAULT
},
179 {11, 2462, LBS_TX_PWR_FR_DEFAULT
},
180 {12, 2467, LBS_TX_PWR_FR_DEFAULT
},
181 {13, 2472, LBS_TX_PWR_FR_DEFAULT
}
184 /* band: 'B/G', region: Japan */
185 static struct chan_freq_power channel_freq_power_JPN_BG
[] = {
186 {1, 2412, LBS_TX_PWR_JP_DEFAULT
},
187 {2, 2417, LBS_TX_PWR_JP_DEFAULT
},
188 {3, 2422, LBS_TX_PWR_JP_DEFAULT
},
189 {4, 2427, LBS_TX_PWR_JP_DEFAULT
},
190 {5, 2432, LBS_TX_PWR_JP_DEFAULT
},
191 {6, 2437, LBS_TX_PWR_JP_DEFAULT
},
192 {7, 2442, LBS_TX_PWR_JP_DEFAULT
},
193 {8, 2447, LBS_TX_PWR_JP_DEFAULT
},
194 {9, 2452, LBS_TX_PWR_JP_DEFAULT
},
195 {10, 2457, LBS_TX_PWR_JP_DEFAULT
},
196 {11, 2462, LBS_TX_PWR_JP_DEFAULT
},
197 {12, 2467, LBS_TX_PWR_JP_DEFAULT
},
198 {13, 2472, LBS_TX_PWR_JP_DEFAULT
},
199 {14, 2484, LBS_TX_PWR_JP_DEFAULT
}
203 * the structure for channel, frequency and power
205 struct region_cfp_table
{
207 struct chan_freq_power
*cfp_BG
;
212 * the structure for the mapping between region and CFP
214 static struct region_cfp_table region_cfp_table
[] = {
216 channel_freq_power_US_BG
,
217 ARRAY_SIZE(channel_freq_power_US_BG
),
220 {0x20, /*CANADA IC */
221 channel_freq_power_US_BG
,
222 ARRAY_SIZE(channel_freq_power_US_BG
),
225 {0x30, /*EU*/ channel_freq_power_EU_BG
,
226 ARRAY_SIZE(channel_freq_power_EU_BG
),
229 {0x31, /*SPAIN*/ channel_freq_power_SPN_BG
,
230 ARRAY_SIZE(channel_freq_power_SPN_BG
),
233 {0x32, /*FRANCE*/ channel_freq_power_FR_BG
,
234 ARRAY_SIZE(channel_freq_power_FR_BG
),
237 {0x40, /*JAPAN*/ channel_freq_power_JPN_BG
,
238 ARRAY_SIZE(channel_freq_power_JPN_BG
),
241 /*Add new region here */
245 * @brief This function finds the CFP in
246 * region_cfp_table based on region and band parameter.
248 * @param region The region code
249 * @param band The band
250 * @param cfp_no A pointer to CFP number
251 * @return A pointer to CFP
253 static struct chan_freq_power
*lbs_get_region_cfp_table(u8 region
, int *cfp_no
)
257 lbs_deb_enter(LBS_DEB_MAIN
);
259 end
= ARRAY_SIZE(region_cfp_table
);
261 for (i
= 0; i
< end
; i
++) {
262 lbs_deb_main("region_cfp_table[i].region=%d\n",
263 region_cfp_table
[i
].region
);
264 if (region_cfp_table
[i
].region
== region
) {
265 *cfp_no
= region_cfp_table
[i
].cfp_no_BG
;
266 lbs_deb_leave(LBS_DEB_MAIN
);
267 return region_cfp_table
[i
].cfp_BG
;
271 lbs_deb_leave_args(LBS_DEB_MAIN
, "ret NULL");
275 int lbs_set_regiontable(struct lbs_private
*priv
, u8 region
, u8 band
)
280 struct chan_freq_power
*cfp
;
283 lbs_deb_enter(LBS_DEB_MAIN
);
285 memset(priv
->region_channel
, 0, sizeof(priv
->region_channel
));
287 cfp
= lbs_get_region_cfp_table(region
, &cfp_no
);
289 priv
->region_channel
[i
].nrcfp
= cfp_no
;
290 priv
->region_channel
[i
].CFP
= cfp
;
292 lbs_deb_main("wrong region code %#x in band B/G\n",
297 priv
->region_channel
[i
].valid
= 1;
298 priv
->region_channel
[i
].region
= region
;
299 priv
->region_channel
[i
].band
= band
;
302 lbs_deb_leave_args(LBS_DEB_MAIN
, "ret %d", ret
);
309 /*********************************************************************/
311 /* Main scanning support */
313 /*********************************************************************/
316 * @brief Create a channel list for the driver to scan based on region info
318 * Only used from lbs_scan_setup_scan_config()
320 * Use the driver region/band information to construct a comprehensive list
321 * of channels to scan. This routine is used for any scan that is not
322 * provided a specific channel list to scan.
324 * @param priv A pointer to struct lbs_private structure
325 * @param scanchanlist Output parameter: resulting channel list to scan
329 static int lbs_scan_create_channel_list(struct lbs_private
*priv
,
330 struct chanscanparamset
*scanchanlist
)
332 struct region_channel
*scanregion
;
333 struct chan_freq_power
*cfp
;
341 /* Set the default scan type to the user specified type, will later
342 * be changed to passive on a per channel basis if restricted by
343 * regulatory requirements (11d or 11h)
345 scantype
= CMD_SCAN_TYPE_ACTIVE
;
347 for (rgnidx
= 0; rgnidx
< ARRAY_SIZE(priv
->region_channel
); rgnidx
++) {
348 if (!priv
->region_channel
[rgnidx
].valid
)
350 scanregion
= &priv
->region_channel
[rgnidx
];
352 for (nextchan
= 0; nextchan
< scanregion
->nrcfp
; nextchan
++, chanidx
++) {
353 struct chanscanparamset
*chan
= &scanchanlist
[chanidx
];
355 cfp
= scanregion
->CFP
+ nextchan
;
357 if (scanregion
->band
== BAND_B
|| scanregion
->band
== BAND_G
)
358 chan
->radiotype
= CMD_SCAN_RADIO_TYPE_BG
;
360 if (scantype
== CMD_SCAN_TYPE_PASSIVE
) {
361 chan
->maxscantime
= cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME
);
362 chan
->chanscanmode
.passivescan
= 1;
364 chan
->maxscantime
= cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME
);
365 chan
->chanscanmode
.passivescan
= 0;
368 chan
->channumber
= cfp
->channel
;
375 * Add SSID TLV of the form:
379 * ssid 4d 4e 54 45 53 54
381 static int lbs_scan_add_ssid_tlv(struct lbs_private
*priv
, u8
*tlv
)
383 struct mrvl_ie_ssid_param_set
*ssid_tlv
= (void *)tlv
;
385 ssid_tlv
->header
.type
= cpu_to_le16(TLV_TYPE_SSID
);
386 ssid_tlv
->header
.len
= cpu_to_le16(priv
->scan_ssid_len
);
387 memcpy(ssid_tlv
->ssid
, priv
->scan_ssid
, priv
->scan_ssid_len
);
388 return sizeof(ssid_tlv
->header
) + priv
->scan_ssid_len
;
392 * Add CHANLIST TLV of the form
394 * TLV-ID CHANLIST 01 01
396 * channel 1 00 01 00 00 00 64 00
400 * min scan time 00 00
401 * max scan time 64 00
402 * channel 2 00 02 00 00 00 64 00
403 * channel 3 00 03 00 00 00 64 00
404 * channel 4 00 04 00 00 00 64 00
405 * channel 5 00 05 00 00 00 64 00
406 * channel 6 00 06 00 00 00 64 00
407 * channel 7 00 07 00 00 00 64 00
408 * channel 8 00 08 00 00 00 64 00
409 * channel 9 00 09 00 00 00 64 00
410 * channel 10 00 0a 00 00 00 64 00
411 * channel 11 00 0b 00 00 00 64 00
412 * channel 12 00 0c 00 00 00 64 00
413 * channel 13 00 0d 00 00 00 64 00
416 static int lbs_scan_add_chanlist_tlv(uint8_t *tlv
,
417 struct chanscanparamset
*chan_list
,
420 size_t size
= sizeof(struct chanscanparamset
) *chan_count
;
421 struct mrvl_ie_chanlist_param_set
*chan_tlv
= (void *)tlv
;
423 chan_tlv
->header
.type
= cpu_to_le16(TLV_TYPE_CHANLIST
);
424 memcpy(chan_tlv
->chanscanparam
, chan_list
, size
);
425 chan_tlv
->header
.len
= cpu_to_le16(size
);
426 return sizeof(chan_tlv
->header
) + size
;
430 * Add RATES TLV of the form
434 * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
436 * The rates are in lbs_bg_rates[], but for the 802.11b
437 * rates the high bit isn't set.
439 static int lbs_scan_add_rates_tlv(uint8_t *tlv
)
442 struct mrvl_ie_rates_param_set
*rate_tlv
= (void *)tlv
;
444 rate_tlv
->header
.type
= cpu_to_le16(TLV_TYPE_RATES
);
445 tlv
+= sizeof(rate_tlv
->header
);
446 for (i
= 0; i
< MAX_RATES
; i
++) {
447 *tlv
= lbs_bg_rates
[i
];
450 /* This code makes sure that the 802.11b rates (1 MBit/s, 2
451 MBit/s, 5.5 MBit/s and 11 MBit/s get's the high bit set.
452 Note that the values are MBit/s * 2, to mark them as
453 basic rates so that the firmware likes it better */
454 if (*tlv
== 0x02 || *tlv
== 0x04 ||
455 *tlv
== 0x0b || *tlv
== 0x16)
459 rate_tlv
->header
.len
= cpu_to_le16(i
);
460 return sizeof(rate_tlv
->header
) + i
;
464 * Generate the CMD_802_11_SCAN command with the proper tlv
465 * for a bunch of channels.
467 static int lbs_do_scan(struct lbs_private
*priv
, uint8_t bsstype
,
468 struct chanscanparamset
*chan_list
, int chan_count
)
471 struct cmd_ds_802_11_scan
*scan_cmd
;
472 uint8_t *tlv
; /* pointer into our current, growing TLV storage area */
474 lbs_deb_enter_args(LBS_DEB_SCAN
, "bsstype %d, chanlist[].chan %d, chan_count %d",
475 bsstype
, chan_list
? chan_list
[0].channumber
: -1,
478 /* create the fixed part for scan command */
479 scan_cmd
= kzalloc(MAX_SCAN_CFG_ALLOC
, GFP_KERNEL
);
480 if (scan_cmd
== NULL
)
483 tlv
= scan_cmd
->tlvbuffer
;
484 /* TODO: do we need to scan for a specific BSSID?
485 memcpy(scan_cmd->bssid, priv->scan_bssid, ETH_ALEN); */
486 scan_cmd
->bsstype
= bsstype
;
489 if (priv
->scan_ssid_len
)
490 tlv
+= lbs_scan_add_ssid_tlv(priv
, tlv
);
491 if (chan_list
&& chan_count
)
492 tlv
+= lbs_scan_add_chanlist_tlv(tlv
, chan_list
, chan_count
);
493 tlv
+= lbs_scan_add_rates_tlv(tlv
);
495 /* This is the final data we are about to send */
496 scan_cmd
->hdr
.size
= cpu_to_le16(tlv
- (uint8_t *)scan_cmd
);
497 lbs_deb_hex(LBS_DEB_SCAN
, "SCAN_CMD", (void *)scan_cmd
,
499 lbs_deb_hex(LBS_DEB_SCAN
, "SCAN_TLV", scan_cmd
->tlvbuffer
,
500 tlv
- scan_cmd
->tlvbuffer
);
502 ret
= __lbs_cmd(priv
, CMD_802_11_SCAN
, &scan_cmd
->hdr
,
503 le16_to_cpu(scan_cmd
->hdr
.size
),
504 lbs_ret_80211_scan
, 0);
508 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
513 * @brief Internal function used to start a scan based on an input config
515 * Use the input user scan configuration information when provided in
516 * order to send the appropriate scan commands to firmware to populate or
517 * update the internal driver scan table
519 * @param priv A pointer to struct lbs_private structure
520 * @param full_scan Do a full-scan (blocking)
522 * @return 0 or < 0 if error
524 int lbs_scan_networks(struct lbs_private
*priv
, int full_scan
)
527 struct chanscanparamset
*chan_list
;
528 struct chanscanparamset
*curr_chans
;
530 uint8_t bsstype
= CMD_BSS_TYPE_ANY
;
531 int numchannels
= MRVDRV_CHANNELS_PER_SCAN_CMD
;
532 union iwreq_data wrqu
;
533 #ifdef CONFIG_LIBERTAS_DEBUG
534 struct bss_descriptor
*iter
;
536 DECLARE_SSID_BUF(ssid
);
539 lbs_deb_enter_args(LBS_DEB_SCAN
, "full_scan %d", full_scan
);
541 /* Cancel any partial outstanding partial scans if this scan
544 if (full_scan
&& delayed_work_pending(&priv
->scan_work
))
545 cancel_delayed_work(&priv
->scan_work
);
547 /* User-specified bsstype or channel list
548 TODO: this can be implemented if some user-space application
549 need the feature. Formerly, it was accessible from debugfs,
550 but then nowhere used.
552 if (user_cfg->bsstype)
553 bsstype = user_cfg->bsstype;
556 lbs_deb_scan("numchannels %d, bsstype %d\n", numchannels
, bsstype
);
558 /* Create list of channels to scan */
559 chan_list
= kzalloc(sizeof(struct chanscanparamset
) *
560 LBS_IOCTL_USER_SCAN_CHAN_MAX
, GFP_KERNEL
);
562 lbs_pr_alert("SCAN: chan_list empty\n");
566 /* We want to scan all channels */
567 chan_count
= lbs_scan_create_channel_list(priv
, chan_list
);
569 netif_stop_queue(priv
->dev
);
571 netif_stop_queue(priv
->mesh_dev
);
573 /* Prepare to continue an interrupted scan */
574 lbs_deb_scan("chan_count %d, scan_channel %d\n",
575 chan_count
, priv
->scan_channel
);
576 curr_chans
= chan_list
;
577 /* advance channel list by already-scanned-channels */
578 if (priv
->scan_channel
> 0) {
579 curr_chans
+= priv
->scan_channel
;
580 chan_count
-= priv
->scan_channel
;
583 /* Send scan command(s)
584 * numchannels contains the number of channels we should maximally scan
585 * chan_count is the total number of channels to scan
589 int to_scan
= min(numchannels
, chan_count
);
590 lbs_deb_scan("scanning %d of %d channels\n",
591 to_scan
, chan_count
);
592 ret
= lbs_do_scan(priv
, bsstype
, curr_chans
,
595 lbs_pr_err("SCAN_CMD failed\n");
598 curr_chans
+= to_scan
;
599 chan_count
-= to_scan
;
601 /* somehow schedule the next part of the scan */
602 if (chan_count
&& !full_scan
&&
603 !priv
->surpriseremoved
) {
604 /* -1 marks just that we're currently scanning */
605 if (priv
->scan_channel
< 0)
606 priv
->scan_channel
= to_scan
;
608 priv
->scan_channel
+= to_scan
;
609 cancel_delayed_work(&priv
->scan_work
);
610 queue_delayed_work(priv
->work_thread
, &priv
->scan_work
,
611 msecs_to_jiffies(300));
612 /* skip over GIWSCAN event */
617 memset(&wrqu
, 0, sizeof(union iwreq_data
));
618 wireless_send_event(priv
->dev
, SIOCGIWSCAN
, &wrqu
, NULL
);
620 #ifdef CONFIG_LIBERTAS_DEBUG
621 /* Dump the scan table */
622 mutex_lock(&priv
->lock
);
623 lbs_deb_scan("scan table:\n");
624 list_for_each_entry(iter
, &priv
->network_list
, list
)
625 lbs_deb_scan("%02d: BSSID %pM, RSSI %d, SSID '%s'\n",
626 i
++, iter
->bssid
, iter
->rssi
,
627 print_ssid(ssid
, iter
->ssid
, iter
->ssid_len
));
628 mutex_unlock(&priv
->lock
);
632 priv
->scan_channel
= 0;
635 if (priv
->connect_status
== LBS_CONNECTED
&& !priv
->tx_pending_len
)
636 netif_wake_queue(priv
->dev
);
638 if (priv
->mesh_dev
&& (priv
->mesh_connect_status
== LBS_CONNECTED
) &&
639 !priv
->tx_pending_len
)
640 netif_wake_queue(priv
->mesh_dev
);
644 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
648 void lbs_scan_worker(struct work_struct
*work
)
650 struct lbs_private
*priv
=
651 container_of(work
, struct lbs_private
, scan_work
.work
);
653 lbs_deb_enter(LBS_DEB_SCAN
);
654 lbs_scan_networks(priv
, 0);
655 lbs_deb_leave(LBS_DEB_SCAN
);
659 /*********************************************************************/
661 /* Result interpretation */
663 /*********************************************************************/
666 * @brief Interpret a BSS scan response returned from the firmware
668 * Parse the various fixed fields and IEs passed back for a a BSS probe
669 * response or beacon from the scan command. Record information as needed
670 * in the scan table struct bss_descriptor for that entry.
672 * @param bss Output parameter: Pointer to the BSS Entry
676 static int lbs_process_bss(struct bss_descriptor
*bss
,
677 uint8_t **pbeaconinfo
, int *bytesleft
)
679 struct ieee_ie_fh_param_set
*fh
;
680 struct ieee_ie_ds_param_set
*ds
;
681 struct ieee_ie_cf_param_set
*cf
;
682 struct ieee_ie_ibss_param_set
*ibss
;
683 DECLARE_SSID_BUF(ssid
);
684 uint8_t *pos
, *end
, *p
;
685 uint8_t n_ex_rates
= 0, got_basic_rates
= 0, n_basic_rates
= 0;
686 uint16_t beaconsize
= 0;
689 lbs_deb_enter(LBS_DEB_SCAN
);
691 if (*bytesleft
>= sizeof(beaconsize
)) {
692 /* Extract & convert beacon size from the command buffer */
693 beaconsize
= get_unaligned_le16(*pbeaconinfo
);
694 *bytesleft
-= sizeof(beaconsize
);
695 *pbeaconinfo
+= sizeof(beaconsize
);
698 if (beaconsize
== 0 || beaconsize
> *bytesleft
) {
699 *pbeaconinfo
+= *bytesleft
;
705 /* Initialize the current working beacon pointer for this BSS iteration */
707 end
= pos
+ beaconsize
;
709 /* Advance the return beacon pointer past the current beacon */
710 *pbeaconinfo
+= beaconsize
;
711 *bytesleft
-= beaconsize
;
713 memcpy(bss
->bssid
, pos
, ETH_ALEN
);
714 lbs_deb_scan("process_bss: BSSID %pM\n", bss
->bssid
);
717 if ((end
- pos
) < 12) {
718 lbs_deb_scan("process_bss: Not enough bytes left\n");
724 * next 4 fields are RSSI, time stamp, beacon interval,
725 * and capability information
728 /* RSSI is 1 byte long */
730 lbs_deb_scan("process_bss: RSSI %d\n", *pos
);
733 /* time stamp is 8 bytes long */
736 /* beacon interval is 2 bytes long */
737 bss
->beaconperiod
= get_unaligned_le16(pos
);
740 /* capability information is 2 bytes long */
741 bss
->capability
= get_unaligned_le16(pos
);
742 lbs_deb_scan("process_bss: capabilities 0x%04x\n", bss
->capability
);
745 if (bss
->capability
& WLAN_CAPABILITY_PRIVACY
)
746 lbs_deb_scan("process_bss: WEP enabled\n");
747 if (bss
->capability
& WLAN_CAPABILITY_IBSS
)
748 bss
->mode
= IW_MODE_ADHOC
;
750 bss
->mode
= IW_MODE_INFRA
;
752 /* rest of the current buffer are IE's */
753 lbs_deb_scan("process_bss: IE len %zd\n", end
- pos
);
754 lbs_deb_hex(LBS_DEB_SCAN
, "process_bss: IE info", pos
, end
- pos
);
756 /* process variable IE */
757 while (pos
<= end
- 2) {
758 if (pos
+ pos
[1] > end
) {
759 lbs_deb_scan("process_bss: error in processing IE, "
760 "bytes left < IE length\n");
766 bss
->ssid_len
= min_t(int, IEEE80211_MAX_SSID_LEN
, pos
[1]);
767 memcpy(bss
->ssid
, pos
+ 2, bss
->ssid_len
);
768 lbs_deb_scan("got SSID IE: '%s', len %u\n",
769 print_ssid(ssid
, bss
->ssid
, bss
->ssid_len
),
773 case WLAN_EID_SUPP_RATES
:
774 n_basic_rates
= min_t(uint8_t, MAX_RATES
, pos
[1]);
775 memcpy(bss
->rates
, pos
+ 2, n_basic_rates
);
777 lbs_deb_scan("got RATES IE\n");
780 case WLAN_EID_FH_PARAMS
:
781 fh
= (struct ieee_ie_fh_param_set
*) pos
;
782 memcpy(&bss
->phy
.fh
, fh
, sizeof(*fh
));
783 lbs_deb_scan("got FH IE\n");
786 case WLAN_EID_DS_PARAMS
:
787 ds
= (struct ieee_ie_ds_param_set
*) pos
;
788 bss
->channel
= ds
->channel
;
789 memcpy(&bss
->phy
.ds
, ds
, sizeof(*ds
));
790 lbs_deb_scan("got DS IE, channel %d\n", bss
->channel
);
793 case WLAN_EID_CF_PARAMS
:
794 cf
= (struct ieee_ie_cf_param_set
*) pos
;
795 memcpy(&bss
->ss
.cf
, cf
, sizeof(*cf
));
796 lbs_deb_scan("got CF IE\n");
799 case WLAN_EID_IBSS_PARAMS
:
800 ibss
= (struct ieee_ie_ibss_param_set
*) pos
;
801 bss
->atimwindow
= ibss
->atimwindow
;
802 memcpy(&bss
->ss
.ibss
, ibss
, sizeof(*ibss
));
803 lbs_deb_scan("got IBSS IE\n");
806 case WLAN_EID_EXT_SUPP_RATES
:
807 /* only process extended supported rate if data rate is
808 * already found. Data rate IE should come before
809 * extended supported rate IE
811 lbs_deb_scan("got RATESEX IE\n");
812 if (!got_basic_rates
) {
813 lbs_deb_scan("... but ignoring it\n");
818 if (n_basic_rates
+ n_ex_rates
> MAX_RATES
)
819 n_ex_rates
= MAX_RATES
- n_basic_rates
;
821 p
= bss
->rates
+ n_basic_rates
;
822 memcpy(p
, pos
+ 2, n_ex_rates
);
825 case WLAN_EID_GENERIC
:
827 pos
[2] == 0x00 && pos
[3] == 0x50 &&
828 pos
[4] == 0xf2 && pos
[5] == 0x01) {
829 bss
->wpa_ie_len
= min(pos
[1] + 2, MAX_WPA_IE_LEN
);
830 memcpy(bss
->wpa_ie
, pos
, bss
->wpa_ie_len
);
831 lbs_deb_scan("got WPA IE\n");
832 lbs_deb_hex(LBS_DEB_SCAN
, "WPA IE", bss
->wpa_ie
,
834 } else if (pos
[1] >= MARVELL_MESH_IE_LENGTH
&&
835 pos
[2] == 0x00 && pos
[3] == 0x50 &&
836 pos
[4] == 0x43 && pos
[5] == 0x04) {
837 lbs_deb_scan("got mesh IE\n");
840 lbs_deb_scan("got generic IE: %02x:%02x:%02x:%02x, len %d\n",
848 lbs_deb_scan("got RSN IE\n");
849 bss
->rsn_ie_len
= min(pos
[1] + 2, MAX_WPA_IE_LEN
);
850 memcpy(bss
->rsn_ie
, pos
, bss
->rsn_ie_len
);
851 lbs_deb_hex(LBS_DEB_SCAN
, "process_bss: RSN_IE",
852 bss
->rsn_ie
, bss
->rsn_ie_len
);
856 lbs_deb_scan("got IE 0x%04x, len %d\n",
865 bss
->last_scanned
= jiffies
;
866 lbs_unset_basic_rate_flags(bss
->rates
, sizeof(bss
->rates
));
871 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
876 * @brief Send a scan command for all available channels filtered on a spec
878 * Used in association code and from debugfs
880 * @param priv A pointer to struct lbs_private structure
881 * @param ssid A pointer to the SSID to scan for
882 * @param ssid_len Length of the SSID
884 * @return 0-success, otherwise fail
886 int lbs_send_specific_ssid_scan(struct lbs_private
*priv
, uint8_t *ssid
,
889 DECLARE_SSID_BUF(ssid_buf
);
892 lbs_deb_enter_args(LBS_DEB_SCAN
, "SSID '%s'\n",
893 print_ssid(ssid_buf
, ssid
, ssid_len
));
898 memcpy(priv
->scan_ssid
, ssid
, ssid_len
);
899 priv
->scan_ssid_len
= ssid_len
;
901 lbs_scan_networks(priv
, 1);
902 if (priv
->surpriseremoved
) {
908 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
915 /*********************************************************************/
917 /* Support for Wireless Extensions */
919 /*********************************************************************/
922 #define MAX_CUSTOM_LEN 64
924 static inline char *lbs_translate_scan(struct lbs_private
*priv
,
925 struct iw_request_info
*info
,
926 char *start
, char *stop
,
927 struct bss_descriptor
*bss
)
929 struct chan_freq_power
*cfp
;
930 char *current_val
; /* For rates */
931 struct iw_event iwe
; /* Temporary buffer */
933 #define PERFECT_RSSI ((uint8_t)50)
934 #define WORST_RSSI ((uint8_t)0)
935 #define RSSI_DIFF ((uint8_t)(PERFECT_RSSI - WORST_RSSI))
938 lbs_deb_enter(LBS_DEB_SCAN
);
940 cfp
= lbs_find_cfp_by_band_and_channel(priv
, 0, bss
->channel
);
942 lbs_deb_scan("Invalid channel number %d\n", bss
->channel
);
947 /* First entry *MUST* be the BSSID */
949 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
950 memcpy(iwe
.u
.ap_addr
.sa_data
, &bss
->bssid
, ETH_ALEN
);
951 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_ADDR_LEN
);
954 iwe
.cmd
= SIOCGIWESSID
;
955 iwe
.u
.data
.flags
= 1;
956 iwe
.u
.data
.length
= min((uint32_t) bss
->ssid_len
, (uint32_t) IEEE80211_MAX_SSID_LEN
);
957 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, bss
->ssid
);
960 iwe
.cmd
= SIOCGIWMODE
;
961 iwe
.u
.mode
= bss
->mode
;
962 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_UINT_LEN
);
965 iwe
.cmd
= SIOCGIWFREQ
;
966 iwe
.u
.freq
.m
= (long)cfp
->freq
* 100000;
968 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_FREQ_LEN
);
970 /* Add quality statistics */
972 iwe
.u
.qual
.updated
= IW_QUAL_ALL_UPDATED
;
973 iwe
.u
.qual
.level
= SCAN_RSSI(bss
->rssi
);
975 rssi
= iwe
.u
.qual
.level
- MRVDRV_NF_DEFAULT_SCAN_VALUE
;
977 (100 * RSSI_DIFF
* RSSI_DIFF
- (PERFECT_RSSI
- rssi
) *
978 (15 * (RSSI_DIFF
) + 62 * (PERFECT_RSSI
- rssi
))) /
979 (RSSI_DIFF
* RSSI_DIFF
);
980 if (iwe
.u
.qual
.qual
> 100)
981 iwe
.u
.qual
.qual
= 100;
983 if (priv
->NF
[TYPE_BEACON
][TYPE_NOAVG
] == 0) {
984 iwe
.u
.qual
.noise
= MRVDRV_NF_DEFAULT_SCAN_VALUE
;
986 iwe
.u
.qual
.noise
= CAL_NF(priv
->NF
[TYPE_BEACON
][TYPE_NOAVG
]);
989 /* Locally created ad-hoc BSSs won't have beacons if this is the
990 * only station in the adhoc network; so get signal strength
991 * from receive statistics.
993 if ((priv
->mode
== IW_MODE_ADHOC
) && priv
->adhoccreate
994 && !lbs_ssid_cmp(priv
->curbssparams
.ssid
,
995 priv
->curbssparams
.ssid_len
,
996 bss
->ssid
, bss
->ssid_len
)) {
998 snr
= priv
->SNR
[TYPE_RXPD
][TYPE_AVG
] / AVG_SCALE
;
999 nf
= priv
->NF
[TYPE_RXPD
][TYPE_AVG
] / AVG_SCALE
;
1000 iwe
.u
.qual
.level
= CAL_RSSI(snr
, nf
);
1002 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_QUAL_LEN
);
1004 /* Add encryption capability */
1005 iwe
.cmd
= SIOCGIWENCODE
;
1006 if (bss
->capability
& WLAN_CAPABILITY_PRIVACY
) {
1007 iwe
.u
.data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
1009 iwe
.u
.data
.flags
= IW_ENCODE_DISABLED
;
1011 iwe
.u
.data
.length
= 0;
1012 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, bss
->ssid
);
1014 current_val
= start
+ iwe_stream_lcp_len(info
);
1016 iwe
.cmd
= SIOCGIWRATE
;
1017 iwe
.u
.bitrate
.fixed
= 0;
1018 iwe
.u
.bitrate
.disabled
= 0;
1019 iwe
.u
.bitrate
.value
= 0;
1021 for (j
= 0; j
< ARRAY_SIZE(bss
->rates
) && bss
->rates
[j
]; j
++) {
1022 /* Bit rate given in 500 kb/s units */
1023 iwe
.u
.bitrate
.value
= bss
->rates
[j
] * 500000;
1024 current_val
= iwe_stream_add_value(info
, start
, current_val
,
1025 stop
, &iwe
, IW_EV_PARAM_LEN
);
1027 if ((bss
->mode
== IW_MODE_ADHOC
) && priv
->adhoccreate
1028 && !lbs_ssid_cmp(priv
->curbssparams
.ssid
,
1029 priv
->curbssparams
.ssid_len
,
1030 bss
->ssid
, bss
->ssid_len
)) {
1031 iwe
.u
.bitrate
.value
= 22 * 500000;
1032 current_val
= iwe_stream_add_value(info
, start
, current_val
,
1033 stop
, &iwe
, IW_EV_PARAM_LEN
);
1035 /* Check if we added any event */
1036 if ((current_val
- start
) > iwe_stream_lcp_len(info
))
1037 start
= current_val
;
1039 memset(&iwe
, 0, sizeof(iwe
));
1040 if (bss
->wpa_ie_len
) {
1041 char buf
[MAX_WPA_IE_LEN
];
1042 memcpy(buf
, bss
->wpa_ie
, bss
->wpa_ie_len
);
1043 iwe
.cmd
= IWEVGENIE
;
1044 iwe
.u
.data
.length
= bss
->wpa_ie_len
;
1045 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, buf
);
1048 memset(&iwe
, 0, sizeof(iwe
));
1049 if (bss
->rsn_ie_len
) {
1050 char buf
[MAX_WPA_IE_LEN
];
1051 memcpy(buf
, bss
->rsn_ie
, bss
->rsn_ie_len
);
1052 iwe
.cmd
= IWEVGENIE
;
1053 iwe
.u
.data
.length
= bss
->rsn_ie_len
;
1054 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, buf
);
1058 char custom
[MAX_CUSTOM_LEN
];
1061 iwe
.cmd
= IWEVCUSTOM
;
1062 p
+= snprintf(p
, MAX_CUSTOM_LEN
, "mesh-type: olpc");
1063 iwe
.u
.data
.length
= p
- custom
;
1064 if (iwe
.u
.data
.length
)
1065 start
= iwe_stream_add_point(info
, start
, stop
,
1070 lbs_deb_leave_args(LBS_DEB_SCAN
, "start %p", start
);
1076 * @brief Handle Scan Network ioctl
1078 * @param dev A pointer to net_device structure
1079 * @param info A pointer to iw_request_info structure
1080 * @param vwrq A pointer to iw_param structure
1081 * @param extra A pointer to extra data buf
1083 * @return 0 --success, otherwise fail
1085 int lbs_set_scan(struct net_device
*dev
, struct iw_request_info
*info
,
1086 union iwreq_data
*wrqu
, char *extra
)
1088 DECLARE_SSID_BUF(ssid
);
1089 struct lbs_private
*priv
= dev
->ml_priv
;
1092 lbs_deb_enter(LBS_DEB_WEXT
);
1094 if (!priv
->radio_on
) {
1099 if (!netif_running(dev
)) {
1104 /* mac80211 does this:
1105 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1106 if (sdata->type != IEEE80211_IF_TYPE_xxx) {
1112 if (wrqu
->data
.length
== sizeof(struct iw_scan_req
) &&
1113 wrqu
->data
.flags
& IW_SCAN_THIS_ESSID
) {
1114 struct iw_scan_req
*req
= (struct iw_scan_req
*)extra
;
1115 priv
->scan_ssid_len
= req
->essid_len
;
1116 memcpy(priv
->scan_ssid
, req
->essid
, priv
->scan_ssid_len
);
1117 lbs_deb_wext("set_scan, essid '%s'\n",
1118 print_ssid(ssid
, priv
->scan_ssid
, priv
->scan_ssid_len
));
1120 priv
->scan_ssid_len
= 0;
1123 if (!delayed_work_pending(&priv
->scan_work
))
1124 queue_delayed_work(priv
->work_thread
, &priv
->scan_work
,
1125 msecs_to_jiffies(50));
1126 /* set marker that currently a scan is taking place */
1127 priv
->scan_channel
= -1;
1129 if (priv
->surpriseremoved
)
1133 lbs_deb_leave_args(LBS_DEB_WEXT
, "ret %d", ret
);
1139 * @brief Handle Retrieve scan table ioctl
1141 * @param dev A pointer to net_device structure
1142 * @param info A pointer to iw_request_info structure
1143 * @param dwrq A pointer to iw_point structure
1144 * @param extra A pointer to extra data buf
1146 * @return 0 --success, otherwise fail
1148 int lbs_get_scan(struct net_device
*dev
, struct iw_request_info
*info
,
1149 struct iw_point
*dwrq
, char *extra
)
1151 #define SCAN_ITEM_SIZE 128
1152 struct lbs_private
*priv
= dev
->ml_priv
;
1155 char *stop
= ev
+ dwrq
->length
;
1156 struct bss_descriptor
*iter_bss
;
1157 struct bss_descriptor
*safe
;
1159 lbs_deb_enter(LBS_DEB_WEXT
);
1161 /* iwlist should wait until the current scan is finished */
1162 if (priv
->scan_channel
)
1165 /* Update RSSI if current BSS is a locally created ad-hoc BSS */
1166 if ((priv
->mode
== IW_MODE_ADHOC
) && priv
->adhoccreate
) {
1167 err
= lbs_prepare_and_send_command(priv
, CMD_802_11_RSSI
, 0,
1168 CMD_OPTION_WAITFORRSP
, 0, NULL
);
1173 mutex_lock(&priv
->lock
);
1174 list_for_each_entry_safe (iter_bss
, safe
, &priv
->network_list
, list
) {
1176 unsigned long stale_time
;
1178 if (stop
- ev
< SCAN_ITEM_SIZE
) {
1183 /* For mesh device, list only mesh networks */
1184 if (dev
== priv
->mesh_dev
&& !iter_bss
->mesh
)
1187 /* Prune old an old scan result */
1188 stale_time
= iter_bss
->last_scanned
+ DEFAULT_MAX_SCAN_AGE
;
1189 if (time_after(jiffies
, stale_time
)) {
1190 list_move_tail(&iter_bss
->list
, &priv
->network_free_list
);
1191 clear_bss_descriptor(iter_bss
);
1195 /* Translate to WE format this entry */
1196 next_ev
= lbs_translate_scan(priv
, info
, ev
, stop
, iter_bss
);
1197 if (next_ev
== NULL
)
1201 mutex_unlock(&priv
->lock
);
1203 dwrq
->length
= (ev
- extra
);
1206 lbs_deb_leave_args(LBS_DEB_WEXT
, "ret %d", err
);
1213 /*********************************************************************/
1215 /* Command execution */
1217 /*********************************************************************/
1221 * @brief This function handles the command response of scan
1223 * Called from handle_cmd_response() in cmdrespc.
1225 * The response buffer for the scan command has the following
1228 * .-----------------------------------------------------------.
1229 * | header (4 * sizeof(u16)): Standard command response hdr |
1230 * .-----------------------------------------------------------.
1231 * | bufsize (u16) : sizeof the BSS Description data |
1232 * .-----------------------------------------------------------.
1233 * | NumOfSet (u8) : Number of BSS Descs returned |
1234 * .-----------------------------------------------------------.
1235 * | BSSDescription data (variable, size given in bufsize) |
1236 * .-----------------------------------------------------------.
1237 * | TLV data (variable, size calculated using header->size, |
1238 * | bufsize and sizeof the fixed fields above) |
1239 * .-----------------------------------------------------------.
1241 * @param priv A pointer to struct lbs_private structure
1242 * @param resp A pointer to cmd_ds_command
1246 static int lbs_ret_80211_scan(struct lbs_private
*priv
, unsigned long dummy
,
1247 struct cmd_header
*resp
)
1249 struct cmd_ds_802_11_scan_rsp
*scanresp
= (void *)resp
;
1250 struct bss_descriptor
*iter_bss
;
1251 struct bss_descriptor
*safe
;
1253 uint16_t scanrespsize
;
1259 lbs_deb_enter(LBS_DEB_SCAN
);
1261 /* Prune old entries from scan table */
1262 list_for_each_entry_safe (iter_bss
, safe
, &priv
->network_list
, list
) {
1263 unsigned long stale_time
= iter_bss
->last_scanned
+ DEFAULT_MAX_SCAN_AGE
;
1264 if (time_before(jiffies
, stale_time
))
1266 list_move_tail (&iter_bss
->list
, &priv
->network_free_list
);
1267 clear_bss_descriptor(iter_bss
);
1270 if (scanresp
->nr_sets
> MAX_NETWORK_COUNT
) {
1271 lbs_deb_scan("SCAN_RESP: too many scan results (%d, max %d)\n",
1272 scanresp
->nr_sets
, MAX_NETWORK_COUNT
);
1277 bytesleft
= get_unaligned_le16(&scanresp
->bssdescriptsize
);
1278 lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft
);
1280 scanrespsize
= le16_to_cpu(resp
->size
);
1281 lbs_deb_scan("SCAN_RESP: scan results %d\n", scanresp
->nr_sets
);
1283 bssinfo
= scanresp
->bssdesc_and_tlvbuffer
;
1285 /* The size of the TLV buffer is equal to the entire command response
1286 * size (scanrespsize) minus the fixed fields (sizeof()'s), the
1287 * BSS Descriptions (bssdescriptsize as bytesLef) and the command
1288 * response header (sizeof(struct cmd_header))
1290 tlvbufsize
= scanrespsize
- (bytesleft
+ sizeof(scanresp
->bssdescriptsize
)
1291 + sizeof(scanresp
->nr_sets
)
1292 + sizeof(struct cmd_header
));
1295 * Process each scan response returned (scanresp->nr_sets). Save
1296 * the information in the newbssentry and then insert into the
1297 * driver scan table either as an update to an existing entry
1298 * or as an addition at the end of the table
1300 for (idx
= 0; idx
< scanresp
->nr_sets
&& bytesleft
; idx
++) {
1301 struct bss_descriptor
new;
1302 struct bss_descriptor
*found
= NULL
;
1303 struct bss_descriptor
*oldest
= NULL
;
1305 /* Process the data fields and IEs returned for this BSS */
1306 memset(&new, 0, sizeof (struct bss_descriptor
));
1307 if (lbs_process_bss(&new, &bssinfo
, &bytesleft
) != 0) {
1308 /* error parsing the scan response, skipped */
1309 lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n");
1313 /* Try to find this bss in the scan table */
1314 list_for_each_entry (iter_bss
, &priv
->network_list
, list
) {
1315 if (is_same_network(iter_bss
, &new)) {
1320 if ((oldest
== NULL
) ||
1321 (iter_bss
->last_scanned
< oldest
->last_scanned
))
1326 /* found, clear it */
1327 clear_bss_descriptor(found
);
1328 } else if (!list_empty(&priv
->network_free_list
)) {
1329 /* Pull one from the free list */
1330 found
= list_entry(priv
->network_free_list
.next
,
1331 struct bss_descriptor
, list
);
1332 list_move_tail(&found
->list
, &priv
->network_list
);
1333 } else if (oldest
) {
1334 /* If there are no more slots, expire the oldest */
1336 clear_bss_descriptor(found
);
1337 list_move_tail(&found
->list
, &priv
->network_list
);
1342 lbs_deb_scan("SCAN_RESP: BSSID %pM\n", new.bssid
);
1344 /* Copy the locally created newbssentry to the scan table */
1345 memcpy(found
, &new, offsetof(struct bss_descriptor
, list
));
1351 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);