WIP: new scanning framework

Dan Williams dcbw at redhat.com
Mon Jun 25 18:36:55 EDT 2007


On Mon, 2007-06-25 at 15:34 +0200, Holger Schurig wrote:
> This is a work-in-progress
> 
> This is my attempt to simplify scan.c a bit. The new code is basically
> organized this way:
> 
> wlan_scan_networks()

Lets rename this to libertas_ instead of wlan_

>    determines scan parameters, e.g. bsstype, numprobes
>    determines how many channels to scan at once, put that
>       into "maxchannels"
>    determines list of channels to scan
>    stops queue
>    send powersave packet to AP

Yeah, this is the key.  As long as we can send powersave packets to the
AP, we don't need to jump through any hoops with breaking up the scan.

>    sends n SCAN-commands to firmware via wlan_do_scan(),
>       each scanning for "numchannels" channels
>    send "I'm awake" packet to AP
>    starts queue
> 
> wlan_do_scan()

Again, libertas_ here too.

>    create the fixed part of the SCAN command
>    calls wlan_scan_add_ssid_tlv() if needed
>    calls wlan_scan_add_chanlist_tlv() if needed
>    calls wlan_scan_add_rates_tlv()
>    calls wlan_scan_add_numprobes_tlv() if needed
>    sends the command to the firmware
>    waits for response
> 
> As you can see, the code is still littered with TODO comments. Still it
> basically works with both my USB dongle and CF card.
> 
> Comments?

More below...

> 
> 
> 
> /*********************************************************************/
> /*                                                                   */
> /*  Main scanning support                                            */
> /*                                                                   */
> /*********************************************************************/
> 
> 
> /*
>  * Add SSID TLV of the form:
>  *
>  * TLV-ID SSID     00 00
>  * length          06 00
>  * ssid            4d 4e 54 45 53 54
>  */
> static int wlan_scan_add_ssid_tlv(u8 *tlv,
> 	const struct wlan_ioctl_user_scan_cfg *user_cfg)
> {
> 	struct mrvlietypes_ssidparamset *ssid_tlv = (struct mrvlietypes_ssidparamset *)tlv;
> 	ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
> 	ssid_tlv->header.len = cpu_to_le16(user_cfg->ssid_len);
> 	memcpy(ssid_tlv->ssid, user_cfg->ssid, user_cfg->ssid_len);
> 	return sizeof(ssid_tlv->header) + user_cfg->ssid_len;
> }
> 
> /*
>  * Add CHANLIST TLV of the form
>  *
>  * TLV-ID CHANLIST 01 01
>  * length          5b 00
>  * channel 1       00 01 00 00 00 64 00
>  *   radio type    00
>  *   channel          01
>  *   scan type           00
>  *   min scan time          00 00
>  *   max scan time                64 00
>  * channel 2       00 02 00 00 00 64 00
>  * channel 3       00 03 00 00 00 64 00
>  * channel 4       00 04 00 00 00 64 00
>  * channel 5       00 05 00 00 00 64 00
>  * channel 6       00 06 00 00 00 64 00
>  * channel 7       00 07 00 00 00 64 00
>  * channel 8       00 08 00 00 00 64 00
>  * channel 9       00 09 00 00 00 64 00
>  * channel 10      00 0a 00 00 00 64 00
>  * channel 11      00 0b 00 00 00 64 00
>  * channel 12      00 0c 00 00 00 64 00
>  * channel 13      00 0d 00 00 00 64 00
>  *
>  */
> static int wlan_scan_add_chanlist_tlv(u8 *tlv,
> 	struct chanscanparamset *chan_list,
> 	int chan_count)
> {
> 	size_t size = sizeof(struct chanscanparamset) * chan_count;
> 	struct mrvlietypes_chanlistparamset *chan_tlv =
> 		(struct mrvlietypes_chanlistparamset *) tlv;
> 
> 	chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
>         // The channel list in chan_list is already in the format
>         // needed by the firmware. Just copy it.
> 	memcpy(chan_tlv->chanscanparam, chan_list, size);
> 	chan_tlv->header.len = cpu_to_le16(size);
> 	return sizeof(chan_tlv->header) + size;
> }
> 
> /*
>  * Add RATES TLV of the form
>  *
>  * TLV-ID RATES    01 00
>  * length          0e 00
>  * rates           82 84 8b 96 0c 12 18 24 30 48 60 6c
>  *
>  * The rates are in libertas_bg_rates[], but for the 802.11b
>  * rates the high bit isn't set.
>  */
> static int wlan_scan_add_rates_tlv(u8 *tlv)
> {
> 	int i;
> 	struct mrvlietypes_ratesparamset *rate_tlv =
> 		(struct mrvlietypes_ratesparamset *) tlv;
> 
> 	rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
> 	tlv += sizeof(rate_tlv->header);
> 	for (i=0; i<MAX_RATES; i++) {
> 		*tlv = libertas_bg_rates[i];
> 		if (*tlv == 0)
> 			break;
> 		if (*tlv==0x02 || *tlv == 0x04 || *tlv == 0x0b || *tlv == 0x16)
> 			*tlv |= 0x80;

Might want to add a  note here ^^^ that the |= 0x80 is because the
firmware requires basic rates to have the MSB set.

> 		tlv++;
> 	}
> 	rate_tlv->header.len = i;
> 	return sizeof(rate_tlv->header) + i;
> }
> 
> 
> /*
>  * Actually creates a SCAN_CMD with TLVs and sends it to the firmware
>  */
> int wlan_do_scan(wlan_private *priv,
> 	u8 bsstype,
> 	struct chanscanparamset *chan_list,
> 	int chan_count,
> 	const struct wlan_ioctl_user_scan_cfg *user_cfg)
> {
> 	int ret = -ENOMEM;
> 
> 	struct wlan_scan_cmd_config *scan_cmd;
> 	u8 *tlv;    /* pointer into our current, growing TLV storage area */
> 
> 	/* create the fixed part for scan command */
> 	scan_cmd = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL);
> 	if (scan_cmd == NULL)
> 		goto out;
> 	tlv = scan_cmd->tlvbuffer;
> 	if (user_cfg)
> 		memcpy(scan_cmd->bssid, user_cfg->bssid, ETH_ALEN);
> 	scan_cmd->bsstype = bsstype;
> 
> 	/* add TLVs */
> 	if (user_cfg && user_cfg->ssid_len)
> 		tlv += wlan_scan_add_ssid_tlv(tlv, user_cfg);
> 	if (chan_list && chan_count)
> 		tlv += wlan_scan_add_chanlist_tlv(tlv, chan_list, chan_count);
> 	tlv += wlan_scan_add_rates_tlv(tlv);
> 	// Schurig TODO: numprobes
> 
> 	/* This is the final data we are about to send */
> 	scan_cmd->tlvbufferlen = tlv - scan_cmd->tlvbuffer;
> 	lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void*)scan_cmd, 1+6);
> 	lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer, scan_cmd->tlvbufferlen);
> 
> 	ret = libertas_prepare_and_send_command(priv, CMD_802_11_SCAN, 0,
> 		CMD_OPTION_WAITFORRSP, 0, scan_cmd);
> out:
> 	kfree(scan_cmd);
> 	return ret;
> }
> 
> /*
>  * Send an empty data frame to the AP, with just the powermanegament
>  * bit's set to our needs
>  */
> int libertas_send_null_packet(wlan_private *priv, u8 pwr_mgmt)
> {
>         wlan_adapter *adapter = priv->adapter;
>         struct txpd nulltxpd;
>         int ret = -1;
> 
>         lbs_deb_enter_args(LBS_DEB_SCAN, "pwr_mgmt %02x", pwr_mgmt);
> 
>         if (adapter->surpriseremoved)
>                 goto out;
> 
>         memset(&nulltxpd, 0, sizeof(nulltxpd));
>         nulltxpd.tx_control = cpu_to_le32(adapter->pkttxctrl);
>         nulltxpd.powermgmt = pwr_mgmt &0xf;
> 
>         memcpy(priv->adapter->tmptxbuf, &nulltxpd, sizeof(nulltxpd));
> 
>         // TODO: the txpd is sent to the firmware, but nothing happens

I guess we'll have to check on this with CozyBit and/or Marvell.

>         ret = priv->hw_host_to_card(priv, MVMS_DAT,
>                                     priv->adapter->tmptxbuf,
>                                     sizeof(nulltxpd));
> 
> out:
>         lbs_deb_enter_args(LBS_DEB_SCAN, "ret %d", ret);
>         return ret;
> }
> 
> /**
>  *  @brief Used to start a scan based on an input config
>  *
>  *  Also used from debugfs
>  *
>  *  Use the input user scan configuration information when provided in
>  *  order to send the appropriate scan commands to firmware to populate or
>  *  update the internal driver scan table
>  *
>  *  @param priv          A pointer to wlan_private structure
>  *  @param puserscanin   Pointer to the input configuration for the requested
>  *                       scan.
>  *
>  *  @return              0 or < 0 if error
>  */
> int wlan_scan_networks(wlan_private *priv,
> 	const struct wlan_ioctl_user_scan_cfg *user_cfg)
> {
> 	int ret = -ENOMEM;
> 	wlan_adapter * adapter = priv->adapter;
> 	struct chanscanparamset *chan_list;
> 	int chan_count;
> 	u16 numprobes = adapter->scanprobes;
> 	u8 bsstype = adapter->scanmode;
> 	int numchannels = MRVDRV_CHANNELS_PER_SCAN_CMD;
> 	int filteredscan = 0;
> 
> 	/* Determine same scan parameters */
> 	if (user_cfg) {
> 		if (user_cfg->bsstype)
> 			bsstype = user_cfg->bsstype;
> 		if (user_cfg->numprobes)
> 			numprobes = user_cfg->numprobes;
> 		if (compare_ether_addr(user_cfg->bssid, &zeromac[0]) != 0) {
> 			numchannels = MRVDRV_MAX_CHANNELS_PER_SCAN;
> 			filteredscan = 1;
> 		}
> 	}
> 	lbs_deb_scan("numchannels %d, numprobes %d, bsstype %d\n",
> 		numchannels, numprobes, bsstype);
> 
> 	/* Create list of channels to scan */
> 	chan_list = kzalloc(sizeof(struct chanscanparamset) *
> 				WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL);
> 	if (!chan_list)
> 		goto out;
> 	if (user_cfg && user_cfg->chanlist[0].channumber) {
> 		/* User wants to scan specific channels */
> 		printk("##HS TODO %s:%d\n", __FUNCTION__, __LINE__);
> 		//TODO Check if we are only scanning the current channel
> 		//TODO pscancurrentonly
> 		chan_count = 0;
> 	} else {
> 		/* We want to scan all channels */
> 		chan_count = wlan_scan_create_channel_list(priv, chan_list, filteredscan);
> 	}
> 
> 	netif_stop_queue(priv->dev);
> 	if (priv->mesh_dev)
> 		netif_stop_queue(priv->mesh_dev);
> 
>         //TODO is this needed?
> 	//wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
> 	// Bit 0: null packet
> 	// Bit 1: override firmware power management (activates Bit 2)
> 	// Bit 2: power management bit on
> 	ret = libertas_send_null_packet(priv, 0x7);
> 	if (ret) {
> 		lbs_deb_scan("could not turn on powersafe mode on AP\n");
> 		goto out;
> 	}
> 
> 	// I'm not convinced that we need netif_carrier_off() as well,
> 	// net/mac80211/ieee80211_sta.c doesn't do this
> 		netif_stop_queue(priv->dev);
> 	if (priv->mesh_dev)
> 			netif_stop_queue(priv->mesh_dev);
> 
> 	/* Send scan command(s) */
> 	{
> 		struct chanscanparamset *curr_chans = chan_list;
> 		while (chan_count) {
> 			int to_scan = min(numchannels, chan_count);
> 			lbs_deb_scan("scanning next %d of %d channels\n", to_scan, chan_count);
> 			ret = wlan_do_scan(priv, bsstype, curr_chans, to_scan, user_cfg);
> 			if (ret) {
> 				lbs_pr_err("SCAN_CMD failed\n");
> 				goto sendwake;
> 			}
> 			chan_count -= to_scan;
> 			curr_chans += to_scan;
> 		}
> 	}
> 
> sendwake:
>         //TODO is this needed?
> 	wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
>         //TODO
> 	// Bit 0: null packet
> 	// Bit 1: override firmware power management (activates Bit 2)
> 	// Bit 2: power management bit off

The firmware spec seemed to indicate that we could get away with not
sending the wake null frame if there was data to send, since the PS mode
bit in the data frame would be 1 already and therefore signal to the AP
that we are leaving power save mode.  However, then we have two cases,
one where there's no data queued and one where there is.  Perhaps as a
further optimization later on we can just check the netdev's queue and
if there's data, just skip the wake null frame.

> 	ret = libertas_send_null_packet(priv, 0x3);
> 	if (ret) {
> 		lbs_deb_scan("could not turn off powersafe mode on AP\n");
> 	}
> 
> out:
> 	netif_start_queue(priv->dev);
> 	if (priv->mesh_dev)
> 		netif_start_queue(priv->mesh_dev);
> 	kfree(chan_list);
> 
> 	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
> 	return ret;
> }

All in all, it's a lot cleaner than what's there, I think, as long as we
can get the null packet stuff working.

Dan





More information about the libertas-dev mailing list