[RFC] libertas: iwpriv commands to configure fine grained wake-on-(w)lan

Dan Williams dcbw at redhat.com
Fri Oct 24 17:04:10 EDT 2008


On Mon, 2008-10-20 at 16:49 -0700, Anna Neal wrote:
> View README for new API.
> 
> This patch implements the userspace interface for fine-grained configuration of
> wake-on-(w)lan.  We are aware that iwpriv's are discouraged, but this is a
> vendor-specific feature that's currently being used in the OLPC project.
> 
> We are aware that all iwprivs were removed from this driver.  These used the old
> API for iwprivs.  We've implemented this iwpriv as a private handler which relies
> on wireless extensions to do bounds checking and copying to/from user memory.
> Specific suggestions on how to make this more palatable for upstream inclusion
> are welcome.  If iwprivs are completely unacceptable then this can serve as a
> public reference for interfacing with these features.

One thought I just had.  For multicast, why can't libertas use
dev->mc_list as the list of multicast addresses you'd want to WOL on?
Why would you want to have a separate list of WOL multicast addresses,
which may or may not intersect with dev->mc_list?

Dan

> Signed-off-by: Anna Neal <anna at cozybit.com>
> Signed-off-by: Javier Cardona <javier at cozybit.com>
> ---
>  drivers/net/wireless/libertas/README |   71 ++++++++
>  drivers/net/wireless/libertas/wext.c |  323 ++++++++++++++++++++++++++++++++++
>  2 files changed, 394 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
> index d860fc3..6eb30a3 100644
> --- a/drivers/net/wireless/libertas/README
> +++ b/drivers/net/wireless/libertas/README
> @@ -28,6 +28,77 @@ DRIVER LOADING
>  
>  		insmod usb8388.ko [fw_name=usb8388.bin]
>  
> +=====================
> +IWPRIV COMMAND
> +=====================
> +
> +Wake On Lan Commands:
> +
> +	The Wake On Lan (wol) commands are used to configure wol rules outside
> +	of the constraints of ethtool. the following commands are supported:
> +
> +
> +	iwpriv ethX set_wol_rule
> +	iwpriv ethX get_wol_rule
> +	iwpriv ethX reset_wol_rule
> +
> +
> +set_wol_rule
> +
> +	Usage:
> +
> +	    $iwpriv msh0 set_wol_rule "<b|m|u> 0x<signature>{.<mask>}@<offset>"
> +
> +
> +	1. The first digit is the traffic type:
> +		b - broadcast
> +		m - multicast
> +		u - unicast
> +
> +	2. The pattern signature used for comparison to the incoming frame.
> +	   This can be from 1-4 bytes and must be in the standard hex format
> +	   with a leading '0x'.
> +
> +	3. An optional '.' may be added to specify a mask you wish to use. By
> +	   default it will be 0xf for the length of your signature.
> +
> +	4. An offset after the '@' is mandatory this will specify the offset
> +	   into the payload of an 802.3 frame at which the signature will be
> +	   compared.
> +
> +	5. In addition, you may 'and' multiple rules separated by '&&' in the
> +	   same call.
> +
> +	Note:
> +		Every iwpriv, as shown above, will add new wol rules and 'or'
> +		them to any previous rules entered.
> +		At most 16 rules may be applied.
> +
> +	Examples:
> +
> +	1. Wake from an arp request received over the mesh at 192.168.0.1.
> +
> +		iwpriv msh0 set_wol_rule "b 0x0806 at 06 && 0xC0A80001 at 16"
> +
> +	2. Set to wake from any arp request or any multicast traffic from
> +	   192.168.0.1.
> +
> +		iwpriv eth0 set_wol_rule "b 0x0806 at 06"
> +		iwpriv eth0 set_wol_rule "m 0xC0A80001 at 16"
> +
> +	3. Set to wake from an arp or IPv4 broadcast (ethertypes 0x0806 and
> +	   0x0806)
> +
> +		iwpriv eth0 set_wol_rule "b 0x0800.fff9 at 06"
> +
> +get_wol_rule
> +
> +	This will return a list of all the wol rules set.
> +
> +reset_wol_rule
> +
> +        This will delete/reset any rules entered using this interface.
> +
>  =========================
>  ETHTOOL
>  =========================
> diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
> index 82c3e5a..90032a9 100644
> --- a/drivers/net/wireless/libertas/wext.c
> +++ b/drivers/net/wireless/libertas/wext.c
> @@ -2135,6 +2135,301 @@ static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
>  	return ret;
>  }
>  
> +/**
> + * ascii_to_be32() - Convert an hex ascii string into a be32 integer.
> + * @dst:        Pointer to destination big endiand 32 bit integer.
> + * @pptr:       Input string. On exit points past the last converted character.
> +*
> + * Returns the number of converted characters.
> + **/
> +static int ascii_to_be32(__be32 *dst, unsigned char **pptr)
> +{
> +	uint32_t value;
> +	unsigned char *start;
> +	int char_count;
> +
> +	start = *pptr;
> +	if (**pptr == '0' && tolower(*(*pptr+1)) == 'x')
> +		start += 2;
> +	value = simple_strtoul((char *)*pptr, (char **) pptr, 16);
> +	*dst = cpu_to_be32(value);
> +
> +	char_count = (*pptr - start);
> +
> +	return (char_count > 8) ? -EINVAL : char_count;
> +}
> +
> +static int set_wol_rule_type(unsigned char *ptr, struct wol_config
> +		*wol_rule)
> +{
> +	if (tolower(*ptr) == 'b')
> +		wol_rule->pattern |= WOL_RULE_ADDR_TYPE_BCAST;
> +	else if (tolower(*ptr) == 'm')
> +		wol_rule->pattern |= WOL_RULE_ADDR_TYPE_MCAST;
> +	else if (tolower(*ptr) == 'u')
> +		wol_rule->pattern |= WOL_RULE_ADDR_TYPE_UCAST;
> +	else
> +		return -EINVAL;
> +	return 0;
> +}
> +
> +static int config_set_wol_rule(unsigned char *ptr, struct wol_config *rules)
> +{
> +	unsigned short sig_offset;
> +	int sig_len, msk_len;
> +	int n, ret;
> +
> +	ptr = strstrip(ptr);
> +	if (*ptr == '\0')
> +		return -EINVAL;
> +
> +	ret = set_wol_rule_type(ptr, rules);
> +	if (ret)
> +		return ret;
> +
> +	lbs_deb_ioctl("Received WOL pattern %X\n", rules->pattern);
> +	ptr++;
> +
> +	for (n = 0; *ptr != '\0' || n < MAX_WOL_RULES; ptr++, n++) {
> +
> +		ptr = strstrip(ptr);
> +		if (*ptr == '\0')
> +			return -EINVAL;
> +
> +		rules->rule[n].rule_no = n;
> +		sig_len = ascii_to_be32(&rules->rule[n].signature, &ptr);
> +
> +		if (sig_len <= 0)
> +			return -EINVAL;
> +
> +		/* No signature mask, build a default one */
> +		if (*ptr == '@') {
> +			uint32_t defmask;
> +			defmask = (0xffffffff >> 4*(8-sig_len));
> +			rules->rule[n].sig_mask = cpu_to_be32(defmask);
> +			msk_len = sig_len;
> +		} else if (*ptr == '.') {
> +			ptr++;
> +			msk_len = ascii_to_be32(&rules->rule[n].sig_mask, &ptr);
> +			if (msk_len <= 0)
> +				return -EINVAL;
> +		} else
> +			return -EINVAL;
> +
> +		if (*ptr == '@')
> +			ptr++;
> +		else
> +			return -EINVAL;
> +
> +		/* assume a prefix of zeroes if mask is bigger than signature */
> +		if (sig_len < msk_len)
> +			sig_len = msk_len;
> +
> +		rules->rule[n].sig_length = cpu_to_le16((__u16)
> +				(sig_len + 1)/2);
> +		sig_offset = (uint16_t) simple_strtoul(ptr, (char **)&ptr, 16);
> +
> +		if (sig_offset > (IEEE80211_DATA_LEN - 4))
> +			return -EINVAL;
> +
> +		rules->rule[n].sig_offset = cpu_to_le16(sig_offset);
> +
> +		ptr = strstrip(ptr);
> +		if (*ptr == '\0') {
> +			rules->rule[n].rule_ops = WOL_RULE_OP_INVALID;
> +			n++;
> +			break;
> +		}
> +
> +		if ((*ptr == '&') && (*(ptr+1) == '&')) {
> +			rules->rule[n].rule_ops = WOL_RULE_OP_AND;
> +			ptr += 2;
> +		} else
> +			return -EINVAL;
> +
> +	}
> +	rules->no_rules_in_cmd = n;
> +	return 0;
> +}
> +
> +
> +static int lbs_wol_set_config_ioctl(struct net_device *dev,
> +				    struct iw_request_info *info,
> +				    union iwreq_data *u, char *data)
> +{
> +	int ret;
> +	struct wol_config wol_rule;
> +	struct wol_config *wol_ptr;
> +	struct lbs_private *priv = dev->priv;
> +
> +	wol_ptr = &wol_rule;
> +	memset(&wol_rule, 0, sizeof(struct wol_config));
> +
> +	lbs_deb_enter(LBS_DEB_IOCTL);
> +
> +	if (dev == priv->mesh_dev)
> +		wol_rule.pattern |= WOL_RULE_NET_TYPE_MESH;
> +	else
> +		wol_rule.pattern |= WOL_RULE_NET_TYPE_INFRA_OR_IBSS;
> +
> +	ret = config_set_wol_rule(data, wol_ptr);
> +	if (ret)
> +		goto error;
> +
> +	wol_rule.action = CMD_ACT_SET_WOL_RULE;
> +	lbs_deb_ioctl("Sending wol_set_rule for pattern %02x with %03d rules\n",
> +			wol_rule.pattern, wol_rule.no_rules_in_cmd);
> +	ret = lbs_host_sleep_cfg(priv, 0, &wol_rule);
> +	switch (wol_rule.result) {
> +	case WOL_RESULT_VALID_CMD:
> +		break;
> +	case WOL_RESULT_NOSPC_ERR:
> +		ret = -ENOSPC;
> +		break;
> +	case WOL_RESULT_EEXIST_ERR:
> +		ret = -EEXIST;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +error:
> +	lbs_deb_leave(LBS_DEB_IOCTL);
> +	return ret;
> +}
> +
> +static unsigned char *sprint_get_wol_result(unsigned char *ptr,
> +				struct wol_config *wol_rule, int ret)
> +{
> +	struct host_wol_rule *p_rule;
> +	int i;
> +	unsigned short sig_len;
> +
> +	if (wol_rule) {
> +		switch (wol_rule->pattern & 0x0F) {
> +		case WOL_RULE_ADDR_TYPE_BCAST:
> +			ptr += sprintf(ptr, "\nBroadcast Rules\n");
> +			break;
> +		case WOL_RULE_ADDR_TYPE_MCAST:
> +			ptr += sprintf(ptr, "\nMulticast Rules\n");
> +			break;
> +		case WOL_RULE_ADDR_TYPE_UCAST:
> +			ptr += sprintf(ptr, "\nUnicast Rules\n");
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	if (wol_rule->result) {
> +		ptr += sprintf(ptr, " No rules found.\n");
> +		return ptr;
> +	}
> +
> +	ptr += sprintf(ptr, "Total %d Rules  found for",
> +			wol_rule->no_rules_in_cmd);
> +	switch (wol_rule->pattern & 0xF0) {
> +	case WOL_RULE_NET_TYPE_INFRA_OR_IBSS:
> +		ptr += sprintf(ptr, " Infra or Ibss\n");
> +		break;
> +	case WOL_RULE_NET_TYPE_MESH:
> +		ptr += sprintf(ptr, " Mesh\n");
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	ptr += sprintf(ptr, "Signature\tSig Mask\t");
> +	ptr += sprintf(ptr, "Offset\tRule Op\n");
> +	wol_rule->no_rules_in_cmd = min(wol_rule->no_rules_in_cmd,
> +			(uint8_t) MAX_WOL_RULES);
> +
> +	for (i = 0; i < wol_rule->no_rules_in_cmd; i++) {
> +		p_rule = &wol_rule->rule[i];
> +		sig_len = le16_to_cpu(p_rule->sig_length);
> +		ptr += sprintf(ptr, "0x%08x", be32_to_cpu(p_rule->signature));
> +		ptr += sprintf(ptr, "\t");
> +		ptr += sprintf(ptr, "0x%08x", be32_to_cpu(p_rule->sig_mask));
> +		ptr += sprintf(ptr, "\t0x%03x\t",
> +				le16_to_cpu(p_rule->sig_offset));
> +
> +		if (p_rule->rule_ops == 2)
> +			ptr += sprintf(ptr, "OR\n");
> +		else if (p_rule->rule_ops == 1)
> +			ptr += sprintf(ptr, "AND\n");
> +		else
> +			ptr += sprintf(ptr, "LAST\n");
> +	}
> +	return ptr;
> +}
> +
> +static int lbs_wol_get_config_ioctl(struct net_device *dev,
> +				    struct iw_request_info *info,
> +				    union iwreq_data *u, char *data)
> +{
> +	int ret;
> +	struct wol_config wol_rule;
> +	struct wol_config *wol_ptr;
> +	struct lbs_private *priv = dev->priv;
> +	char *ptr = data;
> +
> +	wol_ptr = &wol_rule;
> +	memset(&wol_rule, 0, sizeof(struct wol_config));
> +
> +	if (dev == priv->mesh_dev)
> +		wol_rule.pattern |= WOL_RULE_NET_TYPE_MESH;
> +	else
> +		wol_rule.pattern |= WOL_RULE_NET_TYPE_INFRA_OR_IBSS;
> +
> +	wol_rule.action = CMD_ACT_GET_WOL_RULE;
> +	wol_rule.pattern |= WOL_RULE_ADDR_TYPE_BCAST;
> +	lbs_deb_ioctl("Sending get_wol_rule, pattern %02x\n", wol_rule.pattern);
> +	ret = lbs_host_sleep_cfg(priv, 0, &wol_rule);
> +	ptr = sprint_get_wol_result(ptr, wol_ptr, ret);
> +	if (ret)
> +		goto done;
> +
> +	wol_rule.pattern &= ~WOL_RULE_ADDR_TYPE_BCAST;
> +	wol_rule.pattern |= WOL_RULE_ADDR_TYPE_MCAST;
> +	lbs_deb_ioctl("Sending get_wol_rule, pattern %02x\n", wol_rule.pattern);
> +	ret = lbs_host_sleep_cfg(priv, 0, &wol_rule);
> +	ptr = sprint_get_wol_result(ptr, wol_ptr, ret);
> +	if (ret)
> +		goto done;
> +
> +	wol_rule.pattern &= ~WOL_RULE_ADDR_TYPE_MCAST;
> +	wol_rule.pattern |= WOL_RULE_ADDR_TYPE_UCAST;
> +	lbs_deb_ioctl("Sending get_wol_rule, pattern %02x\n", wol_rule.pattern);
> +	ret = lbs_host_sleep_cfg(priv, 0, &wol_rule);
> +	ptr = sprint_get_wol_result(ptr, wol_ptr, ret);
> +
> +	u->data.length = strlen(data);
> +done:
> +	lbs_deb_leave(LBS_DEB_IOCTL);
> +	return ret;
> +}
> +
> +static int lbs_wol_reset_config_ioctl(struct net_device *dev,
> +				      struct iw_request_info *info,
> +				      struct iw_point *u, char *data)
> +{
> +	int ret;
> +	struct wol_config wol_rule;
> +	struct lbs_private *priv = dev->priv;
> +
> +	memset(&wol_rule, 0, sizeof(struct wol_config));
> +
> +	lbs_deb_enter(LBS_DEB_IOCTL);
> +	wol_rule.action = CMD_ACT_RESET_WOL_RULE;
> +	lbs_deb_ioctl("Sending wol_reset command\n");
> +	ret = lbs_host_sleep_cfg(priv, 0, &wol_rule);
> +	if (!ret && wol_rule.result)
> +		ret = -EIO;
> +
> +	lbs_deb_leave(LBS_DEB_IOCTL);
> +	return ret;
> +}
> +
>  /*
>   * iwconfig settable callbacks
>   */
> @@ -2253,14 +2548,42 @@ static const iw_handler mesh_wlan_handler[] = {
>  	(iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
>  	(iw_handler) NULL,		/* SIOCSIWPMKSA */
>  };
> +
> +#define LBS_SET_WOL_RULE 	SIOCIWFIRSTPRIV
> +#define LBS_GET_WOL_RULE 	(SIOCIWFIRSTPRIV+1)
> +#define LBS_RESET_WOL_RULE 	(SIOCIWFIRSTPRIV+2)
> +
> +static const iw_handler lbs_private_handler[] = {
> +	(iw_handler) lbs_wol_set_config_ioctl,   /* LBS_SET_WOL_RULE */
> +	(iw_handler) lbs_wol_get_config_ioctl,	 /* LBS_GET_WOL_RULE */
> +	(iw_handler) lbs_wol_reset_config_ioctl, /* LBS_RESET_WOL_RULE */
> +
> +};
> +
> +#define CHAR1024_PARAM		   (IW_PRIV_TYPE_CHAR | 1024)
> +static const struct iw_priv_args lbs_private_args[] = {
> +	/* { cmd, set_args, get_args, name } */
> +	{ LBS_SET_WOL_RULE, CHAR1024_PARAM, 0, "set_wol_rule"},
> +	{ LBS_GET_WOL_RULE, 0, CHAR1024_PARAM, "get_wol_rule"},
> +	{ LBS_RESET_WOL_RULE, 0, 0, "reset_wol_rule"},
> +};
> +
>  struct iw_handler_def lbs_handler_def = {
>  	.num_standard	= ARRAY_SIZE(lbs_handler),
> +	.num_private	= ARRAY_SIZE(lbs_private_handler),
>  	.standard	= (iw_handler *) lbs_handler,
> +	.private	= (iw_handler *) lbs_private_handler,
>  	.get_wireless_stats = lbs_get_wireless_stats,
> +	.num_private_args = ARRAY_SIZE(lbs_private_args),
> +	.private_args	= lbs_private_args,
>  };
>  
>  struct iw_handler_def mesh_handler_def = {
>  	.num_standard	= ARRAY_SIZE(mesh_wlan_handler),
> +	.num_private	= ARRAY_SIZE(lbs_private_handler),
>  	.standard	= (iw_handler *) mesh_wlan_handler,
> +	.private	= (iw_handler *) lbs_private_handler,
>  	.get_wireless_stats = lbs_get_wireless_stats,
> +	.num_private_args = ARRAY_SIZE(lbs_private_args),
> +	.private_args	= lbs_private_args,
>  };




More information about the libertas-dev mailing list