WIP: new scanning framework

Holger Schurig hs4233 at mail.mn-solutions.de
Mon Jun 25 09:34:21 EDT 2007


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()
   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
   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()
   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?




/*********************************************************************/
/*                                                                   */
/*  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;
		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
        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
	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;
}





More information about the libertas-dev mailing list