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/slab.h>
8 #include <linux/types.h>
9 #include <linux/kernel.h>
10 #include <linux/etherdevice.h>
11 #include <linux/if_arp.h>
12 #include <asm/unaligned.h>
13 #include <net/lib80211.h>
22 //! Approximate amount of data needed to pass a scan result back to iwlist
23 #define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \
24 + IEEE80211_MAX_SSID_LEN \
28 + IEEE80211_MAX_SSID_LEN \
30 + 40) /* 40 for WPAIE */
32 //! Memory needed to store a max sized channel List TLV for a firmware scan
33 #define CHAN_TLV_MAX_SIZE (sizeof(struct mrvl_ie_header) \
34 + (MRVDRV_MAX_CHANNELS_PER_SCAN \
35 * sizeof(struct chanscanparamset)))
37 //! Memory needed to store a max number/size SSID TLV for a firmware scan
38 #define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvl_ie_ssid_param_set))
40 //! Maximum memory needed for a cmd_ds_802_11_scan with all TLVs at max
41 #define MAX_SCAN_CFG_ALLOC (sizeof(struct cmd_ds_802_11_scan) \
42 + CHAN_TLV_MAX_SIZE + SSID_TLV_MAX_SIZE)
44 //! The maximum number of channels the firmware can scan per command
45 #define MRVDRV_MAX_CHANNELS_PER_SCAN 14
48 * @brief Number of channels to scan per firmware scan command issuance.
50 * Number restricted to prevent hitting the limit on the amount of scan data
51 * returned in a single firmware scan command.
53 #define MRVDRV_CHANNELS_PER_SCAN_CMD 4
55 //! Scan time specified in the channel TLV for each channel for passive scans
56 #define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100
58 //! Scan time specified in the channel TLV for each channel for active scans
59 #define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100
61 #define DEFAULT_MAX_SCAN_AGE (15 * HZ)
63 static int lbs_ret_80211_scan(struct lbs_private
*priv
, unsigned long dummy
,
64 struct cmd_header
*resp
);
66 /*********************************************************************/
68 /* Misc helper functions */
70 /*********************************************************************/
73 * @brief Unsets the MSB on basic rates
75 * Scan through an array and unset the MSB for basic data rates.
77 * @param rates buffer of data rates
78 * @param len size of buffer
80 static void lbs_unset_basic_rate_flags(u8
*rates
, size_t len
)
84 for (i
= 0; i
< len
; i
++)
89 static inline void clear_bss_descriptor(struct bss_descriptor
*bss
)
91 /* Don't blow away ->list, just BSS data */
92 memset(bss
, 0, offsetof(struct bss_descriptor
, list
));
96 * @brief Compare two SSIDs
98 * @param ssid1 A pointer to ssid to compare
99 * @param ssid2 A pointer to ssid to compare
101 * @return 0: ssid is same, otherwise is different
103 int lbs_ssid_cmp(uint8_t *ssid1
, uint8_t ssid1_len
, uint8_t *ssid2
,
106 if (ssid1_len
!= ssid2_len
)
109 return memcmp(ssid1
, ssid2
, ssid1_len
);
112 static inline int is_same_network(struct bss_descriptor
*src
,
113 struct bss_descriptor
*dst
)
115 /* A network is only a duplicate if the channel, BSSID, and ESSID
116 * all match. We treat all <hidden> with the same BSSID and channel
118 return ((src
->ssid_len
== dst
->ssid_len
) &&
119 (src
->channel
== dst
->channel
) &&
120 !compare_ether_addr(src
->bssid
, dst
->bssid
) &&
121 !memcmp(src
->ssid
, dst
->ssid
, src
->ssid_len
));
126 /*********************************************************************/
128 /* Region channel support */
130 /*********************************************************************/
132 #define LBS_TX_PWR_DEFAULT 20 /*100mW */
133 #define LBS_TX_PWR_US_DEFAULT 20 /*100mW */
134 #define LBS_TX_PWR_JP_DEFAULT 16 /*50mW */
135 #define LBS_TX_PWR_FR_DEFAULT 20 /*100mW */
136 #define LBS_TX_PWR_EMEA_DEFAULT 20 /*100mW */
138 /* Format { channel, frequency (MHz), maxtxpower } */
139 /* band: 'B/G', region: USA FCC/Canada IC */
140 static struct chan_freq_power channel_freq_power_US_BG
[] = {
141 {1, 2412, LBS_TX_PWR_US_DEFAULT
},
142 {2, 2417, LBS_TX_PWR_US_DEFAULT
},
143 {3, 2422, LBS_TX_PWR_US_DEFAULT
},
144 {4, 2427, LBS_TX_PWR_US_DEFAULT
},
145 {5, 2432, LBS_TX_PWR_US_DEFAULT
},
146 {6, 2437, LBS_TX_PWR_US_DEFAULT
},
147 {7, 2442, LBS_TX_PWR_US_DEFAULT
},
148 {8, 2447, LBS_TX_PWR_US_DEFAULT
},
149 {9, 2452, LBS_TX_PWR_US_DEFAULT
},
150 {10, 2457, LBS_TX_PWR_US_DEFAULT
},
151 {11, 2462, LBS_TX_PWR_US_DEFAULT
}
154 /* band: 'B/G', region: Europe ETSI */
155 static struct chan_freq_power channel_freq_power_EU_BG
[] = {
156 {1, 2412, LBS_TX_PWR_EMEA_DEFAULT
},
157 {2, 2417, LBS_TX_PWR_EMEA_DEFAULT
},
158 {3, 2422, LBS_TX_PWR_EMEA_DEFAULT
},
159 {4, 2427, LBS_TX_PWR_EMEA_DEFAULT
},
160 {5, 2432, LBS_TX_PWR_EMEA_DEFAULT
},
161 {6, 2437, LBS_TX_PWR_EMEA_DEFAULT
},
162 {7, 2442, LBS_TX_PWR_EMEA_DEFAULT
},
163 {8, 2447, LBS_TX_PWR_EMEA_DEFAULT
},
164 {9, 2452, LBS_TX_PWR_EMEA_DEFAULT
},
165 {10, 2457, LBS_TX_PWR_EMEA_DEFAULT
},
166 {11, 2462, LBS_TX_PWR_EMEA_DEFAULT
},
167 {12, 2467, LBS_TX_PWR_EMEA_DEFAULT
},
168 {13, 2472, LBS_TX_PWR_EMEA_DEFAULT
}
171 /* band: 'B/G', region: Spain */
172 static struct chan_freq_power channel_freq_power_SPN_BG
[] = {
173 {10, 2457, LBS_TX_PWR_DEFAULT
},
174 {11, 2462, LBS_TX_PWR_DEFAULT
}
177 /* band: 'B/G', region: France */
178 static struct chan_freq_power channel_freq_power_FR_BG
[] = {
179 {10, 2457, LBS_TX_PWR_FR_DEFAULT
},
180 {11, 2462, LBS_TX_PWR_FR_DEFAULT
},
181 {12, 2467, LBS_TX_PWR_FR_DEFAULT
},
182 {13, 2472, LBS_TX_PWR_FR_DEFAULT
}
185 /* band: 'B/G', region: Japan */
186 static struct chan_freq_power channel_freq_power_JPN_BG
[] = {
187 {1, 2412, LBS_TX_PWR_JP_DEFAULT
},
188 {2, 2417, LBS_TX_PWR_JP_DEFAULT
},
189 {3, 2422, LBS_TX_PWR_JP_DEFAULT
},
190 {4, 2427, LBS_TX_PWR_JP_DEFAULT
},
191 {5, 2432, LBS_TX_PWR_JP_DEFAULT
},
192 {6, 2437, LBS_TX_PWR_JP_DEFAULT
},
193 {7, 2442, LBS_TX_PWR_JP_DEFAULT
},
194 {8, 2447, LBS_TX_PWR_JP_DEFAULT
},
195 {9, 2452, LBS_TX_PWR_JP_DEFAULT
},
196 {10, 2457, LBS_TX_PWR_JP_DEFAULT
},
197 {11, 2462, LBS_TX_PWR_JP_DEFAULT
},
198 {12, 2467, LBS_TX_PWR_JP_DEFAULT
},
199 {13, 2472, LBS_TX_PWR_JP_DEFAULT
},
200 {14, 2484, LBS_TX_PWR_JP_DEFAULT
}
204 * the structure for channel, frequency and power
206 struct region_cfp_table
{
208 struct chan_freq_power
*cfp_BG
;
213 * the structure for the mapping between region and CFP
215 static struct region_cfp_table region_cfp_table
[] = {
217 channel_freq_power_US_BG
,
218 ARRAY_SIZE(channel_freq_power_US_BG
),
221 {0x20, /*CANADA IC */
222 channel_freq_power_US_BG
,
223 ARRAY_SIZE(channel_freq_power_US_BG
),
226 {0x30, /*EU*/ channel_freq_power_EU_BG
,
227 ARRAY_SIZE(channel_freq_power_EU_BG
),
230 {0x31, /*SPAIN*/ channel_freq_power_SPN_BG
,
231 ARRAY_SIZE(channel_freq_power_SPN_BG
),
234 {0x32, /*FRANCE*/ channel_freq_power_FR_BG
,
235 ARRAY_SIZE(channel_freq_power_FR_BG
),
238 {0x40, /*JAPAN*/ channel_freq_power_JPN_BG
,
239 ARRAY_SIZE(channel_freq_power_JPN_BG
),
242 /*Add new region here */
246 * @brief This function finds the CFP in
247 * region_cfp_table based on region and band parameter.
249 * @param region The region code
250 * @param band The band
251 * @param cfp_no A pointer to CFP number
252 * @return A pointer to CFP
254 static struct chan_freq_power
*lbs_get_region_cfp_table(u8 region
, int *cfp_no
)
258 lbs_deb_enter(LBS_DEB_MAIN
);
260 end
= ARRAY_SIZE(region_cfp_table
);
262 for (i
= 0; i
< end
; i
++) {
263 lbs_deb_main("region_cfp_table[i].region=%d\n",
264 region_cfp_table
[i
].region
);
265 if (region_cfp_table
[i
].region
== region
) {
266 *cfp_no
= region_cfp_table
[i
].cfp_no_BG
;
267 lbs_deb_leave(LBS_DEB_MAIN
);
268 return region_cfp_table
[i
].cfp_BG
;
272 lbs_deb_leave_args(LBS_DEB_MAIN
, "ret NULL");
276 int lbs_set_regiontable(struct lbs_private
*priv
, u8 region
, u8 band
)
281 struct chan_freq_power
*cfp
;
284 lbs_deb_enter(LBS_DEB_MAIN
);
286 memset(priv
->region_channel
, 0, sizeof(priv
->region_channel
));
288 cfp
= lbs_get_region_cfp_table(region
, &cfp_no
);
290 priv
->region_channel
[i
].nrcfp
= cfp_no
;
291 priv
->region_channel
[i
].CFP
= cfp
;
293 lbs_deb_main("wrong region code %#x in band B/G\n",
298 priv
->region_channel
[i
].valid
= 1;
299 priv
->region_channel
[i
].region
= region
;
300 priv
->region_channel
[i
].band
= band
;
303 lbs_deb_leave_args(LBS_DEB_MAIN
, "ret %d", ret
);
310 /*********************************************************************/
312 /* Main scanning support */
314 /*********************************************************************/
317 * @brief Create a channel list for the driver to scan based on region info
319 * Only used from lbs_scan_setup_scan_config()
321 * Use the driver region/band information to construct a comprehensive list
322 * of channels to scan. This routine is used for any scan that is not
323 * provided a specific channel list to scan.
325 * @param priv A pointer to struct lbs_private structure
326 * @param scanchanlist Output parameter: resulting channel list to scan
330 static int lbs_scan_create_channel_list(struct lbs_private
*priv
,
331 struct chanscanparamset
*scanchanlist
)
333 struct region_channel
*scanregion
;
334 struct chan_freq_power
*cfp
;
342 /* Set the default scan type to the user specified type, will later
343 * be changed to passive on a per channel basis if restricted by
344 * regulatory requirements (11d or 11h)
346 scantype
= CMD_SCAN_TYPE_ACTIVE
;
348 for (rgnidx
= 0; rgnidx
< ARRAY_SIZE(priv
->region_channel
); rgnidx
++) {
349 if (!priv
->region_channel
[rgnidx
].valid
)
351 scanregion
= &priv
->region_channel
[rgnidx
];
353 for (nextchan
= 0; nextchan
< scanregion
->nrcfp
; nextchan
++, chanidx
++) {
354 struct chanscanparamset
*chan
= &scanchanlist
[chanidx
];
356 cfp
= scanregion
->CFP
+ nextchan
;
358 if (scanregion
->band
== BAND_B
|| scanregion
->band
== BAND_G
)
359 chan
->radiotype
= CMD_SCAN_RADIO_TYPE_BG
;
361 if (scantype
== CMD_SCAN_TYPE_PASSIVE
) {
362 chan
->maxscantime
= cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME
);
363 chan
->chanscanmode
.passivescan
= 1;
365 chan
->maxscantime
= cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME
);
366 chan
->chanscanmode
.passivescan
= 0;
369 chan
->channumber
= cfp
->channel
;
376 * Add SSID TLV of the form:
380 * ssid 4d 4e 54 45 53 54
382 static int lbs_scan_add_ssid_tlv(struct lbs_private
*priv
, u8
*tlv
)
384 struct mrvl_ie_ssid_param_set
*ssid_tlv
= (void *)tlv
;
386 ssid_tlv
->header
.type
= cpu_to_le16(TLV_TYPE_SSID
);
387 ssid_tlv
->header
.len
= cpu_to_le16(priv
->scan_ssid_len
);
388 memcpy(ssid_tlv
->ssid
, priv
->scan_ssid
, priv
->scan_ssid_len
);
389 return sizeof(ssid_tlv
->header
) + priv
->scan_ssid_len
;
393 * Add CHANLIST TLV of the form
395 * TLV-ID CHANLIST 01 01
397 * channel 1 00 01 00 00 00 64 00
401 * min scan time 00 00
402 * max scan time 64 00
403 * channel 2 00 02 00 00 00 64 00
404 * channel 3 00 03 00 00 00 64 00
405 * channel 4 00 04 00 00 00 64 00
406 * channel 5 00 05 00 00 00 64 00
407 * channel 6 00 06 00 00 00 64 00
408 * channel 7 00 07 00 00 00 64 00
409 * channel 8 00 08 00 00 00 64 00
410 * channel 9 00 09 00 00 00 64 00
411 * channel 10 00 0a 00 00 00 64 00
412 * channel 11 00 0b 00 00 00 64 00
413 * channel 12 00 0c 00 00 00 64 00
414 * channel 13 00 0d 00 00 00 64 00
417 static int lbs_scan_add_chanlist_tlv(uint8_t *tlv
,
418 struct chanscanparamset
*chan_list
,
421 size_t size
= sizeof(struct chanscanparamset
) *chan_count
;
422 struct mrvl_ie_chanlist_param_set
*chan_tlv
= (void *)tlv
;
424 chan_tlv
->header
.type
= cpu_to_le16(TLV_TYPE_CHANLIST
);
425 memcpy(chan_tlv
->chanscanparam
, chan_list
, size
);
426 chan_tlv
->header
.len
= cpu_to_le16(size
);
427 return sizeof(chan_tlv
->header
) + size
;
431 * Add RATES TLV of the form
435 * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
437 * The rates are in lbs_bg_rates[], but for the 802.11b
438 * rates the high bit isn't set.
440 static int lbs_scan_add_rates_tlv(uint8_t *tlv
)
443 struct mrvl_ie_rates_param_set
*rate_tlv
= (void *)tlv
;
445 rate_tlv
->header
.type
= cpu_to_le16(TLV_TYPE_RATES
);
446 tlv
+= sizeof(rate_tlv
->header
);
447 for (i
= 0; i
< MAX_RATES
; i
++) {
448 *tlv
= lbs_bg_rates
[i
];
451 /* This code makes sure that the 802.11b rates (1 MBit/s, 2
452 MBit/s, 5.5 MBit/s and 11 MBit/s get's the high bit set.
453 Note that the values are MBit/s * 2, to mark them as
454 basic rates so that the firmware likes it better */
455 if (*tlv
== 0x02 || *tlv
== 0x04 ||
456 *tlv
== 0x0b || *tlv
== 0x16)
460 rate_tlv
->header
.len
= cpu_to_le16(i
);
461 return sizeof(rate_tlv
->header
) + i
;
465 * Generate the CMD_802_11_SCAN command with the proper tlv
466 * for a bunch of channels.
468 static int lbs_do_scan(struct lbs_private
*priv
, uint8_t bsstype
,
469 struct chanscanparamset
*chan_list
, int chan_count
)
472 struct cmd_ds_802_11_scan
*scan_cmd
;
473 uint8_t *tlv
; /* pointer into our current, growing TLV storage area */
475 lbs_deb_enter_args(LBS_DEB_SCAN
, "bsstype %d, chanlist[].chan %d, chan_count %d",
476 bsstype
, chan_list
? chan_list
[0].channumber
: -1,
479 /* create the fixed part for scan command */
480 scan_cmd
= kzalloc(MAX_SCAN_CFG_ALLOC
, GFP_KERNEL
);
481 if (scan_cmd
== NULL
)
484 tlv
= scan_cmd
->tlvbuffer
;
485 /* TODO: do we need to scan for a specific BSSID?
486 memcpy(scan_cmd->bssid, priv->scan_bssid, ETH_ALEN); */
487 scan_cmd
->bsstype
= bsstype
;
490 if (priv
->scan_ssid_len
)
491 tlv
+= lbs_scan_add_ssid_tlv(priv
, tlv
);
492 if (chan_list
&& chan_count
)
493 tlv
+= lbs_scan_add_chanlist_tlv(tlv
, chan_list
, chan_count
);
494 tlv
+= lbs_scan_add_rates_tlv(tlv
);
496 /* This is the final data we are about to send */
497 scan_cmd
->hdr
.size
= cpu_to_le16(tlv
- (uint8_t *)scan_cmd
);
498 lbs_deb_hex(LBS_DEB_SCAN
, "SCAN_CMD", (void *)scan_cmd
,
500 lbs_deb_hex(LBS_DEB_SCAN
, "SCAN_TLV", scan_cmd
->tlvbuffer
,
501 tlv
- scan_cmd
->tlvbuffer
);
503 ret
= __lbs_cmd(priv
, CMD_802_11_SCAN
, &scan_cmd
->hdr
,
504 le16_to_cpu(scan_cmd
->hdr
.size
),
505 lbs_ret_80211_scan
, 0);
509 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
514 * @brief Internal function used to start a scan based on an input config
516 * Use the input user scan configuration information when provided in
517 * order to send the appropriate scan commands to firmware to populate or
518 * update the internal driver scan table
520 * @param priv A pointer to struct lbs_private structure
521 * @param full_scan Do a full-scan (blocking)
523 * @return 0 or < 0 if error
525 int lbs_scan_networks(struct lbs_private
*priv
, int full_scan
)
528 struct chanscanparamset
*chan_list
;
529 struct chanscanparamset
*curr_chans
;
531 uint8_t bsstype
= CMD_BSS_TYPE_ANY
;
532 int numchannels
= MRVDRV_CHANNELS_PER_SCAN_CMD
;
533 union iwreq_data wrqu
;
534 #ifdef CONFIG_LIBERTAS_DEBUG
535 struct bss_descriptor
*iter
;
537 DECLARE_SSID_BUF(ssid
);
540 lbs_deb_enter_args(LBS_DEB_SCAN
, "full_scan %d", full_scan
);
542 /* Cancel any partial outstanding partial scans if this scan
545 if (full_scan
&& delayed_work_pending(&priv
->scan_work
))
546 cancel_delayed_work(&priv
->scan_work
);
548 /* User-specified bsstype or channel list
549 TODO: this can be implemented if some user-space application
550 need the feature. Formerly, it was accessible from debugfs,
551 but then nowhere used.
553 if (user_cfg->bsstype)
554 bsstype = user_cfg->bsstype;
557 lbs_deb_scan("numchannels %d, bsstype %d\n", numchannels
, bsstype
);
559 /* Create list of channels to scan */
560 chan_list
= kzalloc(sizeof(struct chanscanparamset
) *
561 LBS_IOCTL_USER_SCAN_CHAN_MAX
, GFP_KERNEL
);
563 lbs_pr_alert("SCAN: chan_list empty\n");
567 /* We want to scan all channels */
568 chan_count
= lbs_scan_create_channel_list(priv
, chan_list
);
570 netif_stop_queue(priv
->dev
);
572 netif_stop_queue(priv
->mesh_dev
);
574 /* Prepare to continue an interrupted scan */
575 lbs_deb_scan("chan_count %d, scan_channel %d\n",
576 chan_count
, priv
->scan_channel
);
577 curr_chans
= chan_list
;
578 /* advance channel list by already-scanned-channels */
579 if (priv
->scan_channel
> 0) {
580 curr_chans
+= priv
->scan_channel
;
581 chan_count
-= priv
->scan_channel
;
584 /* Send scan command(s)
585 * numchannels contains the number of channels we should maximally scan
586 * chan_count is the total number of channels to scan
590 int to_scan
= min(numchannels
, chan_count
);
591 lbs_deb_scan("scanning %d of %d channels\n",
592 to_scan
, chan_count
);
593 ret
= lbs_do_scan(priv
, bsstype
, curr_chans
,
596 lbs_pr_err("SCAN_CMD failed\n");
599 curr_chans
+= to_scan
;
600 chan_count
-= to_scan
;
602 /* somehow schedule the next part of the scan */
603 if (chan_count
&& !full_scan
&&
604 !priv
->surpriseremoved
) {
605 /* -1 marks just that we're currently scanning */
606 if (priv
->scan_channel
< 0)
607 priv
->scan_channel
= to_scan
;
609 priv
->scan_channel
+= to_scan
;
610 cancel_delayed_work(&priv
->scan_work
);
611 queue_delayed_work(priv
->work_thread
, &priv
->scan_work
,
612 msecs_to_jiffies(300));
613 /* skip over GIWSCAN event */
618 memset(&wrqu
, 0, sizeof(union iwreq_data
));
619 wireless_send_event(priv
->dev
, SIOCGIWSCAN
, &wrqu
, NULL
);
621 #ifdef CONFIG_LIBERTAS_DEBUG
622 /* Dump the scan table */
623 mutex_lock(&priv
->lock
);
624 lbs_deb_scan("scan table:\n");
625 list_for_each_entry(iter
, &priv
->network_list
, list
)
626 lbs_deb_scan("%02d: BSSID %pM, RSSI %d, SSID '%s'\n",
627 i
++, iter
->bssid
, iter
->rssi
,
628 print_ssid(ssid
, iter
->ssid
, iter
->ssid_len
));
629 mutex_unlock(&priv
->lock
);
633 priv
->scan_channel
= 0;
636 if (priv
->connect_status
== LBS_CONNECTED
&& !priv
->tx_pending_len
)
637 netif_wake_queue(priv
->dev
);
639 if (priv
->mesh_dev
&& lbs_mesh_connected(priv
) &&
640 !priv
->tx_pending_len
)
641 netif_wake_queue(priv
->mesh_dev
);
645 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
649 void lbs_scan_worker(struct work_struct
*work
)
651 struct lbs_private
*priv
=
652 container_of(work
, struct lbs_private
, scan_work
.work
);
654 lbs_deb_enter(LBS_DEB_SCAN
);
655 lbs_scan_networks(priv
, 0);
656 lbs_deb_leave(LBS_DEB_SCAN
);
660 /*********************************************************************/
662 /* Result interpretation */
664 /*********************************************************************/
667 * @brief Interpret a BSS scan response returned from the firmware
669 * Parse the various fixed fields and IEs passed back for a a BSS probe
670 * response or beacon from the scan command. Record information as needed
671 * in the scan table struct bss_descriptor for that entry.
673 * @param bss Output parameter: Pointer to the BSS Entry
677 static int lbs_process_bss(struct bss_descriptor
*bss
,
678 uint8_t **pbeaconinfo
, int *bytesleft
)
680 struct ieee_ie_fh_param_set
*fh
;
681 struct ieee_ie_ds_param_set
*ds
;
682 struct ieee_ie_cf_param_set
*cf
;
683 struct ieee_ie_ibss_param_set
*ibss
;
684 DECLARE_SSID_BUF(ssid
);
685 uint8_t *pos
, *end
, *p
;
686 uint8_t n_ex_rates
= 0, got_basic_rates
= 0, n_basic_rates
= 0;
687 uint16_t beaconsize
= 0;
690 lbs_deb_enter(LBS_DEB_SCAN
);
692 if (*bytesleft
>= sizeof(beaconsize
)) {
693 /* Extract & convert beacon size from the command buffer */
694 beaconsize
= get_unaligned_le16(*pbeaconinfo
);
695 *bytesleft
-= sizeof(beaconsize
);
696 *pbeaconinfo
+= sizeof(beaconsize
);
699 if (beaconsize
== 0 || beaconsize
> *bytesleft
) {
700 *pbeaconinfo
+= *bytesleft
;
706 /* Initialize the current working beacon pointer for this BSS iteration */
708 end
= pos
+ beaconsize
;
710 /* Advance the return beacon pointer past the current beacon */
711 *pbeaconinfo
+= beaconsize
;
712 *bytesleft
-= beaconsize
;
714 memcpy(bss
->bssid
, pos
, ETH_ALEN
);
715 lbs_deb_scan("process_bss: BSSID %pM\n", bss
->bssid
);
718 if ((end
- pos
) < 12) {
719 lbs_deb_scan("process_bss: Not enough bytes left\n");
725 * next 4 fields are RSSI, time stamp, beacon interval,
726 * and capability information
729 /* RSSI is 1 byte long */
731 lbs_deb_scan("process_bss: RSSI %d\n", *pos
);
734 /* time stamp is 8 bytes long */
737 /* beacon interval is 2 bytes long */
738 bss
->beaconperiod
= get_unaligned_le16(pos
);
741 /* capability information is 2 bytes long */
742 bss
->capability
= get_unaligned_le16(pos
);
743 lbs_deb_scan("process_bss: capabilities 0x%04x\n", bss
->capability
);
746 if (bss
->capability
& WLAN_CAPABILITY_PRIVACY
)
747 lbs_deb_scan("process_bss: WEP enabled\n");
748 if (bss
->capability
& WLAN_CAPABILITY_IBSS
)
749 bss
->mode
= IW_MODE_ADHOC
;
751 bss
->mode
= IW_MODE_INFRA
;
753 /* rest of the current buffer are IE's */
754 lbs_deb_scan("process_bss: IE len %zd\n", end
- pos
);
755 lbs_deb_hex(LBS_DEB_SCAN
, "process_bss: IE info", pos
, end
- pos
);
757 /* process variable IE */
758 while (pos
<= end
- 2) {
759 if (pos
+ pos
[1] > end
) {
760 lbs_deb_scan("process_bss: error in processing IE, "
761 "bytes left < IE length\n");
767 bss
->ssid_len
= min_t(int, IEEE80211_MAX_SSID_LEN
, pos
[1]);
768 memcpy(bss
->ssid
, pos
+ 2, bss
->ssid_len
);
769 lbs_deb_scan("got SSID IE: '%s', len %u\n",
770 print_ssid(ssid
, bss
->ssid
, bss
->ssid_len
),
774 case WLAN_EID_SUPP_RATES
:
775 n_basic_rates
= min_t(uint8_t, MAX_RATES
, pos
[1]);
776 memcpy(bss
->rates
, pos
+ 2, n_basic_rates
);
778 lbs_deb_scan("got RATES IE\n");
781 case WLAN_EID_FH_PARAMS
:
782 fh
= (struct ieee_ie_fh_param_set
*) pos
;
783 memcpy(&bss
->phy
.fh
, fh
, sizeof(*fh
));
784 lbs_deb_scan("got FH IE\n");
787 case WLAN_EID_DS_PARAMS
:
788 ds
= (struct ieee_ie_ds_param_set
*) pos
;
789 bss
->channel
= ds
->channel
;
790 memcpy(&bss
->phy
.ds
, ds
, sizeof(*ds
));
791 lbs_deb_scan("got DS IE, channel %d\n", bss
->channel
);
794 case WLAN_EID_CF_PARAMS
:
795 cf
= (struct ieee_ie_cf_param_set
*) pos
;
796 memcpy(&bss
->ss
.cf
, cf
, sizeof(*cf
));
797 lbs_deb_scan("got CF IE\n");
800 case WLAN_EID_IBSS_PARAMS
:
801 ibss
= (struct ieee_ie_ibss_param_set
*) pos
;
802 bss
->atimwindow
= ibss
->atimwindow
;
803 memcpy(&bss
->ss
.ibss
, ibss
, sizeof(*ibss
));
804 lbs_deb_scan("got IBSS IE\n");
807 case WLAN_EID_EXT_SUPP_RATES
:
808 /* only process extended supported rate if data rate is
809 * already found. Data rate IE should come before
810 * extended supported rate IE
812 lbs_deb_scan("got RATESEX IE\n");
813 if (!got_basic_rates
) {
814 lbs_deb_scan("... but ignoring it\n");
819 if (n_basic_rates
+ n_ex_rates
> MAX_RATES
)
820 n_ex_rates
= MAX_RATES
- n_basic_rates
;
822 p
= bss
->rates
+ n_basic_rates
;
823 memcpy(p
, pos
+ 2, n_ex_rates
);
826 case WLAN_EID_GENERIC
:
828 pos
[2] == 0x00 && pos
[3] == 0x50 &&
829 pos
[4] == 0xf2 && pos
[5] == 0x01) {
830 bss
->wpa_ie_len
= min(pos
[1] + 2, MAX_WPA_IE_LEN
);
831 memcpy(bss
->wpa_ie
, pos
, bss
->wpa_ie_len
);
832 lbs_deb_scan("got WPA IE\n");
833 lbs_deb_hex(LBS_DEB_SCAN
, "WPA IE", bss
->wpa_ie
,
835 } else if (pos
[1] >= MARVELL_MESH_IE_LENGTH
&&
836 pos
[2] == 0x00 && pos
[3] == 0x50 &&
837 pos
[4] == 0x43 && pos
[5] == 0x04) {
838 lbs_deb_scan("got mesh IE\n");
841 lbs_deb_scan("got generic IE: %02x:%02x:%02x:%02x, len %d\n",
849 lbs_deb_scan("got RSN IE\n");
850 bss
->rsn_ie_len
= min(pos
[1] + 2, MAX_WPA_IE_LEN
);
851 memcpy(bss
->rsn_ie
, pos
, bss
->rsn_ie_len
);
852 lbs_deb_hex(LBS_DEB_SCAN
, "process_bss: RSN_IE",
853 bss
->rsn_ie
, bss
->rsn_ie_len
);
857 lbs_deb_scan("got IE 0x%04x, len %d\n",
866 bss
->last_scanned
= jiffies
;
867 lbs_unset_basic_rate_flags(bss
->rates
, sizeof(bss
->rates
));
872 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
877 * @brief Send a scan command for all available channels filtered on a spec
879 * Used in association code and from debugfs
881 * @param priv A pointer to struct lbs_private structure
882 * @param ssid A pointer to the SSID to scan for
883 * @param ssid_len Length of the SSID
885 * @return 0-success, otherwise fail
887 int lbs_send_specific_ssid_scan(struct lbs_private
*priv
, uint8_t *ssid
,
890 DECLARE_SSID_BUF(ssid_buf
);
893 lbs_deb_enter_args(LBS_DEB_SCAN
, "SSID '%s'\n",
894 print_ssid(ssid_buf
, ssid
, ssid_len
));
899 memcpy(priv
->scan_ssid
, ssid
, ssid_len
);
900 priv
->scan_ssid_len
= ssid_len
;
902 lbs_scan_networks(priv
, 1);
903 if (priv
->surpriseremoved
) {
909 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);
916 /*********************************************************************/
918 /* Support for Wireless Extensions */
920 /*********************************************************************/
923 #define MAX_CUSTOM_LEN 64
925 static inline char *lbs_translate_scan(struct lbs_private
*priv
,
926 struct iw_request_info
*info
,
927 char *start
, char *stop
,
928 struct bss_descriptor
*bss
)
930 struct chan_freq_power
*cfp
;
931 char *current_val
; /* For rates */
932 struct iw_event iwe
; /* Temporary buffer */
934 #define PERFECT_RSSI ((uint8_t)50)
935 #define WORST_RSSI ((uint8_t)0)
936 #define RSSI_DIFF ((uint8_t)(PERFECT_RSSI - WORST_RSSI))
939 lbs_deb_enter(LBS_DEB_SCAN
);
941 cfp
= lbs_find_cfp_by_band_and_channel(priv
, 0, bss
->channel
);
943 lbs_deb_scan("Invalid channel number %d\n", bss
->channel
);
948 /* First entry *MUST* be the BSSID */
950 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
951 memcpy(iwe
.u
.ap_addr
.sa_data
, &bss
->bssid
, ETH_ALEN
);
952 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_ADDR_LEN
);
955 iwe
.cmd
= SIOCGIWESSID
;
956 iwe
.u
.data
.flags
= 1;
957 iwe
.u
.data
.length
= min((uint32_t) bss
->ssid_len
, (uint32_t) IEEE80211_MAX_SSID_LEN
);
958 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, bss
->ssid
);
961 iwe
.cmd
= SIOCGIWMODE
;
962 iwe
.u
.mode
= bss
->mode
;
963 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_UINT_LEN
);
966 iwe
.cmd
= SIOCGIWFREQ
;
967 iwe
.u
.freq
.m
= (long)cfp
->freq
* 100000;
969 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_FREQ_LEN
);
971 /* Add quality statistics */
973 iwe
.u
.qual
.updated
= IW_QUAL_ALL_UPDATED
;
974 iwe
.u
.qual
.level
= SCAN_RSSI(bss
->rssi
);
976 rssi
= iwe
.u
.qual
.level
- MRVDRV_NF_DEFAULT_SCAN_VALUE
;
978 (100 * RSSI_DIFF
* RSSI_DIFF
- (PERFECT_RSSI
- rssi
) *
979 (15 * (RSSI_DIFF
) + 62 * (PERFECT_RSSI
- rssi
))) /
980 (RSSI_DIFF
* RSSI_DIFF
);
981 if (iwe
.u
.qual
.qual
> 100)
982 iwe
.u
.qual
.qual
= 100;
984 if (priv
->NF
[TYPE_BEACON
][TYPE_NOAVG
] == 0) {
985 iwe
.u
.qual
.noise
= MRVDRV_NF_DEFAULT_SCAN_VALUE
;
987 iwe
.u
.qual
.noise
= CAL_NF(priv
->NF
[TYPE_BEACON
][TYPE_NOAVG
]);
990 /* Locally created ad-hoc BSSs won't have beacons if this is the
991 * only station in the adhoc network; so get signal strength
992 * from receive statistics.
994 if ((priv
->mode
== IW_MODE_ADHOC
) && priv
->adhoccreate
995 && !lbs_ssid_cmp(priv
->curbssparams
.ssid
,
996 priv
->curbssparams
.ssid_len
,
997 bss
->ssid
, bss
->ssid_len
)) {
999 snr
= priv
->SNR
[TYPE_RXPD
][TYPE_AVG
] / AVG_SCALE
;
1000 nf
= priv
->NF
[TYPE_RXPD
][TYPE_AVG
] / AVG_SCALE
;
1001 iwe
.u
.qual
.level
= CAL_RSSI(snr
, nf
);
1003 start
= iwe_stream_add_event(info
, start
, stop
, &iwe
, IW_EV_QUAL_LEN
);
1005 /* Add encryption capability */
1006 iwe
.cmd
= SIOCGIWENCODE
;
1007 if (bss
->capability
& WLAN_CAPABILITY_PRIVACY
) {
1008 iwe
.u
.data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
1010 iwe
.u
.data
.flags
= IW_ENCODE_DISABLED
;
1012 iwe
.u
.data
.length
= 0;
1013 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, bss
->ssid
);
1015 current_val
= start
+ iwe_stream_lcp_len(info
);
1017 iwe
.cmd
= SIOCGIWRATE
;
1018 iwe
.u
.bitrate
.fixed
= 0;
1019 iwe
.u
.bitrate
.disabled
= 0;
1020 iwe
.u
.bitrate
.value
= 0;
1022 for (j
= 0; j
< ARRAY_SIZE(bss
->rates
) && bss
->rates
[j
]; j
++) {
1023 /* Bit rate given in 500 kb/s units */
1024 iwe
.u
.bitrate
.value
= bss
->rates
[j
] * 500000;
1025 current_val
= iwe_stream_add_value(info
, start
, current_val
,
1026 stop
, &iwe
, IW_EV_PARAM_LEN
);
1028 if ((bss
->mode
== IW_MODE_ADHOC
) && priv
->adhoccreate
1029 && !lbs_ssid_cmp(priv
->curbssparams
.ssid
,
1030 priv
->curbssparams
.ssid_len
,
1031 bss
->ssid
, bss
->ssid_len
)) {
1032 iwe
.u
.bitrate
.value
= 22 * 500000;
1033 current_val
= iwe_stream_add_value(info
, start
, current_val
,
1034 stop
, &iwe
, IW_EV_PARAM_LEN
);
1036 /* Check if we added any event */
1037 if ((current_val
- start
) > iwe_stream_lcp_len(info
))
1038 start
= current_val
;
1040 memset(&iwe
, 0, sizeof(iwe
));
1041 if (bss
->wpa_ie_len
) {
1042 char buf
[MAX_WPA_IE_LEN
];
1043 memcpy(buf
, bss
->wpa_ie
, bss
->wpa_ie_len
);
1044 iwe
.cmd
= IWEVGENIE
;
1045 iwe
.u
.data
.length
= bss
->wpa_ie_len
;
1046 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, buf
);
1049 memset(&iwe
, 0, sizeof(iwe
));
1050 if (bss
->rsn_ie_len
) {
1051 char buf
[MAX_WPA_IE_LEN
];
1052 memcpy(buf
, bss
->rsn_ie
, bss
->rsn_ie_len
);
1053 iwe
.cmd
= IWEVGENIE
;
1054 iwe
.u
.data
.length
= bss
->rsn_ie_len
;
1055 start
= iwe_stream_add_point(info
, start
, stop
, &iwe
, buf
);
1059 char custom
[MAX_CUSTOM_LEN
];
1062 iwe
.cmd
= IWEVCUSTOM
;
1063 p
+= snprintf(p
, MAX_CUSTOM_LEN
, "mesh-type: olpc");
1064 iwe
.u
.data
.length
= p
- custom
;
1065 if (iwe
.u
.data
.length
)
1066 start
= iwe_stream_add_point(info
, start
, stop
,
1071 lbs_deb_leave_args(LBS_DEB_SCAN
, "start %p", start
);
1077 * @brief Handle Scan Network ioctl
1079 * @param dev A pointer to net_device structure
1080 * @param info A pointer to iw_request_info structure
1081 * @param vwrq A pointer to iw_param structure
1082 * @param extra A pointer to extra data buf
1084 * @return 0 --success, otherwise fail
1086 int lbs_set_scan(struct net_device
*dev
, struct iw_request_info
*info
,
1087 union iwreq_data
*wrqu
, char *extra
)
1089 DECLARE_SSID_BUF(ssid
);
1090 struct lbs_private
*priv
= dev
->ml_priv
;
1093 lbs_deb_enter(LBS_DEB_WEXT
);
1095 if (!priv
->radio_on
) {
1100 if (!netif_running(dev
)) {
1105 /* mac80211 does this:
1106 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
1107 if (sdata->type != IEEE80211_IF_TYPE_xxx) {
1113 if (wrqu
->data
.length
== sizeof(struct iw_scan_req
) &&
1114 wrqu
->data
.flags
& IW_SCAN_THIS_ESSID
) {
1115 struct iw_scan_req
*req
= (struct iw_scan_req
*)extra
;
1116 priv
->scan_ssid_len
= req
->essid_len
;
1117 memcpy(priv
->scan_ssid
, req
->essid
, priv
->scan_ssid_len
);
1118 lbs_deb_wext("set_scan, essid '%s'\n",
1119 print_ssid(ssid
, priv
->scan_ssid
, priv
->scan_ssid_len
));
1121 priv
->scan_ssid_len
= 0;
1124 if (!delayed_work_pending(&priv
->scan_work
))
1125 queue_delayed_work(priv
->work_thread
, &priv
->scan_work
,
1126 msecs_to_jiffies(50));
1127 /* set marker that currently a scan is taking place */
1128 priv
->scan_channel
= -1;
1130 if (priv
->surpriseremoved
)
1134 lbs_deb_leave_args(LBS_DEB_WEXT
, "ret %d", ret
);
1140 * @brief Handle Retrieve scan table ioctl
1142 * @param dev A pointer to net_device structure
1143 * @param info A pointer to iw_request_info structure
1144 * @param dwrq A pointer to iw_point structure
1145 * @param extra A pointer to extra data buf
1147 * @return 0 --success, otherwise fail
1149 int lbs_get_scan(struct net_device
*dev
, struct iw_request_info
*info
,
1150 struct iw_point
*dwrq
, char *extra
)
1152 #define SCAN_ITEM_SIZE 128
1153 struct lbs_private
*priv
= dev
->ml_priv
;
1156 char *stop
= ev
+ dwrq
->length
;
1157 struct bss_descriptor
*iter_bss
;
1158 struct bss_descriptor
*safe
;
1160 lbs_deb_enter(LBS_DEB_WEXT
);
1162 /* iwlist should wait until the current scan is finished */
1163 if (priv
->scan_channel
)
1166 /* Update RSSI if current BSS is a locally created ad-hoc BSS */
1167 if ((priv
->mode
== IW_MODE_ADHOC
) && priv
->adhoccreate
) {
1168 err
= lbs_prepare_and_send_command(priv
, CMD_802_11_RSSI
, 0,
1169 CMD_OPTION_WAITFORRSP
, 0, NULL
);
1174 mutex_lock(&priv
->lock
);
1175 list_for_each_entry_safe (iter_bss
, safe
, &priv
->network_list
, list
) {
1177 unsigned long stale_time
;
1179 if (stop
- ev
< SCAN_ITEM_SIZE
) {
1184 /* For mesh device, list only mesh networks */
1185 if (dev
== priv
->mesh_dev
&& !iter_bss
->mesh
)
1188 /* Prune old an old scan result */
1189 stale_time
= iter_bss
->last_scanned
+ DEFAULT_MAX_SCAN_AGE
;
1190 if (time_after(jiffies
, stale_time
)) {
1191 list_move_tail(&iter_bss
->list
, &priv
->network_free_list
);
1192 clear_bss_descriptor(iter_bss
);
1196 /* Translate to WE format this entry */
1197 next_ev
= lbs_translate_scan(priv
, info
, ev
, stop
, iter_bss
);
1198 if (next_ev
== NULL
)
1202 mutex_unlock(&priv
->lock
);
1204 dwrq
->length
= (ev
- extra
);
1207 lbs_deb_leave_args(LBS_DEB_WEXT
, "ret %d", err
);
1214 /*********************************************************************/
1216 /* Command execution */
1218 /*********************************************************************/
1222 * @brief This function handles the command response of scan
1224 * Called from handle_cmd_response() in cmdrespc.
1226 * The response buffer for the scan command has the following
1229 * .-----------------------------------------------------------.
1230 * | header (4 * sizeof(u16)): Standard command response hdr |
1231 * .-----------------------------------------------------------.
1232 * | bufsize (u16) : sizeof the BSS Description data |
1233 * .-----------------------------------------------------------.
1234 * | NumOfSet (u8) : Number of BSS Descs returned |
1235 * .-----------------------------------------------------------.
1236 * | BSSDescription data (variable, size given in bufsize) |
1237 * .-----------------------------------------------------------.
1238 * | TLV data (variable, size calculated using header->size, |
1239 * | bufsize and sizeof the fixed fields above) |
1240 * .-----------------------------------------------------------.
1242 * @param priv A pointer to struct lbs_private structure
1243 * @param resp A pointer to cmd_ds_command
1247 static int lbs_ret_80211_scan(struct lbs_private
*priv
, unsigned long dummy
,
1248 struct cmd_header
*resp
)
1250 struct cmd_ds_802_11_scan_rsp
*scanresp
= (void *)resp
;
1251 struct bss_descriptor
*iter_bss
;
1252 struct bss_descriptor
*safe
;
1254 uint16_t scanrespsize
;
1260 lbs_deb_enter(LBS_DEB_SCAN
);
1262 /* Prune old entries from scan table */
1263 list_for_each_entry_safe (iter_bss
, safe
, &priv
->network_list
, list
) {
1264 unsigned long stale_time
= iter_bss
->last_scanned
+ DEFAULT_MAX_SCAN_AGE
;
1265 if (time_before(jiffies
, stale_time
))
1267 list_move_tail (&iter_bss
->list
, &priv
->network_free_list
);
1268 clear_bss_descriptor(iter_bss
);
1271 if (scanresp
->nr_sets
> MAX_NETWORK_COUNT
) {
1272 lbs_deb_scan("SCAN_RESP: too many scan results (%d, max %d)\n",
1273 scanresp
->nr_sets
, MAX_NETWORK_COUNT
);
1278 bytesleft
= get_unaligned_le16(&scanresp
->bssdescriptsize
);
1279 lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft
);
1281 scanrespsize
= le16_to_cpu(resp
->size
);
1282 lbs_deb_scan("SCAN_RESP: scan results %d\n", scanresp
->nr_sets
);
1284 bssinfo
= scanresp
->bssdesc_and_tlvbuffer
;
1286 /* The size of the TLV buffer is equal to the entire command response
1287 * size (scanrespsize) minus the fixed fields (sizeof()'s), the
1288 * BSS Descriptions (bssdescriptsize as bytesLef) and the command
1289 * response header (sizeof(struct cmd_header))
1291 tlvbufsize
= scanrespsize
- (bytesleft
+ sizeof(scanresp
->bssdescriptsize
)
1292 + sizeof(scanresp
->nr_sets
)
1293 + sizeof(struct cmd_header
));
1296 * Process each scan response returned (scanresp->nr_sets). Save
1297 * the information in the newbssentry and then insert into the
1298 * driver scan table either as an update to an existing entry
1299 * or as an addition at the end of the table
1301 for (idx
= 0; idx
< scanresp
->nr_sets
&& bytesleft
; idx
++) {
1302 struct bss_descriptor
new;
1303 struct bss_descriptor
*found
= NULL
;
1304 struct bss_descriptor
*oldest
= NULL
;
1306 /* Process the data fields and IEs returned for this BSS */
1307 memset(&new, 0, sizeof (struct bss_descriptor
));
1308 if (lbs_process_bss(&new, &bssinfo
, &bytesleft
) != 0) {
1309 /* error parsing the scan response, skipped */
1310 lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n");
1314 /* Try to find this bss in the scan table */
1315 list_for_each_entry (iter_bss
, &priv
->network_list
, list
) {
1316 if (is_same_network(iter_bss
, &new)) {
1321 if ((oldest
== NULL
) ||
1322 (iter_bss
->last_scanned
< oldest
->last_scanned
))
1327 /* found, clear it */
1328 clear_bss_descriptor(found
);
1329 } else if (!list_empty(&priv
->network_free_list
)) {
1330 /* Pull one from the free list */
1331 found
= list_entry(priv
->network_free_list
.next
,
1332 struct bss_descriptor
, list
);
1333 list_move_tail(&found
->list
, &priv
->network_list
);
1334 } else if (oldest
) {
1335 /* If there are no more slots, expire the oldest */
1337 clear_bss_descriptor(found
);
1338 list_move_tail(&found
->list
, &priv
->network_list
);
1343 lbs_deb_scan("SCAN_RESP: BSSID %pM\n", new.bssid
);
1345 /* Copy the locally created newbssentry to the scan table */
1346 memcpy(found
, &new, offsetof(struct bss_descriptor
, list
));
1352 lbs_deb_leave_args(LBS_DEB_SCAN
, "ret %d", ret
);