This is some preliminary code how I'm currently thinking (and that might change radically :) ) configuration might look like.
It uses the patch I previously posted to make genetlink attributes custom-definable. --- wireless-dev.orig/include/linux/nl80211.h 2006-09-13 22:06:10.539647141 +0200 +++ wireless-dev/include/linux/nl80211.h 2006-09-13 22:06:11.919647141 +0200 @@ -45,6 +45,47 @@ enum { /* get list of all interfaces belonging to a wiphy */ NL80211_CMD_NEW_INTERFACES, + /* configure device */ + NL80211_CMD_CONFIGURE, + + /* request configuration */ + NL80211_CMD_GET_CONFIG, + + /* configuration sent from kernel */ + NL80211_CMD_CONFIGURATION, + + /* initiate scan */ + NL80211_CMD_INITIATE_SCAN, + + /* scan result (kernel -> userspace) */ + NL80211_CMD_SCAN_RESULT, + + /* change roaming control */ + NL80211_CMD_SET_ROAMING_CONTROL, + + /* get roaming control setting */ + NL80211_CMD_GET_ROAMING_CONTROL, + + /* set access point BSSID for userspace roaming */ + NL80211_CMD_SET_BSSID, + + /* get current association information, if not associated then + * the BSSID attribute is not present in response */ + NL80211_CMD_GET_ASSOCIATION, + + /* association notification/response to GET_BSSID */ + NL80211_CMD_ASSOCIATION_CHANGED, + + /* disassociate from current AP */ + NL80211_CMD_DISASSOCIATE, + + /* deauth from current AP */ + NL80211_CMD_DEAUTH, + + /* re-associate with current settings + * (SSID and BSSID if roaming control in userspace) */ + NL80211_CMD_REASSOCIATE, + /* add commands here */ /* used to define NL80211_CMD_MAX below */ @@ -88,6 +129,36 @@ enum { /* wiphy list */ NL80211_ATTR_WIPHY_LIST, + /* attributes used for configuration */ + /* network ID (pre 802.11 HW) */ + NL80211_ATTR_NETWORK_ID, + + /* channel, 1-14 are B/G */ + NL80211_ATTR_CHANNEL, + + /* receiver sensitivity in dBm */ + NL80211_ATTR_RX_SENSITIVITY, + + /* BSSID to associate to, only used when roaming control + * is in userspace */ + NL80211_ATTR_BSSID, + + /* SSID of ESS to associate to */ + NL80211_ATTR_SSID, + + /* transmit power in mW */ + NL80211_ATTR_TRANSMIT_POWER, + + /* fragmentation threshold in bytes */ + NL80211_ATTR_FRAG_THRESHOLD, + + /* one or more information elements */ + NL80211_ATTR_INFORMATION_ELEMENT, + + NL80211_ATTR_ROAMING_CONTROL, + + NL80211_ATTR_SCAN_TYPE, + /* add attributes here */ /* used to define NL80211_ATTR_MAX below */ @@ -134,4 +205,13 @@ enum { }; #define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1) +enum { + NL80211_ROAMING_CONTROL_KERNEL, + NL80211_ROAMING_CONTROL_USERSPACE, + + /* keep last */ + __NL80211_ROAMING_CONTROL_AFTER_LAST +}; +#define NL80211_ROAMING_CONTROL_MAX (__NL80211_ROAMING_CONTROL_AFTER_LAST-1) + #endif /* __LINUX_NL80211_H */ --- wireless-dev.orig/include/net/cfg80211.h 2006-09-13 22:06:10.539647141 +0200 +++ wireless-dev/include/net/cfg80211.h 2006-09-13 22:06:11.919647141 +0200 @@ -14,6 +14,30 @@ */ /** + * struct cfg80211_config - description of a configuration (request) + */ +struct cfg80211_config { + /* first fields with 'internal' validity */ + + /* SSID to use, valid if not NULL. change forces reassociation */ + u8 *ssid; + + /* now fields with explicit validity */ +#define CFG80211_CFG_VALID_NWID (1<<0) +#define CFG80211_CFG_VALID_RX_SENSITIVITY (1<<1) +#define CFG80211_CFG_VALID_TRANSMIT_POWER (1<<2) +#define CFG80211_CFG_VALID_FRAG_THRESHOLD (1<<3) +#define CFG80211_CFG_VALID_CHANNEL (1<<4) + unsigned int valid; + + u16 network_id; + s32 rx_sensitivity; + u32 transmit_power; + u32 fragmentation_threshold; + u32 channel; +}; + +/** * struct cfg80211_ops - backend description for wireless configuration * * This struct is registered by fullmac card drivers and/or wireless stacks @@ -35,6 +59,26 @@ * @add_virtual_intf: create a new virtual interface with the given name * * @del_virtual_intf: remove the virtual interface determined by ifindex. + * + * @configure: configure the given interface as requested in the config struct. + * must not ignore any configuration item, if something is + * is requested that cannot be fulfilled return an error + * + * @get_config: fill the given config structure with the current configuration + * + * @reassociate: reassociate with current settings (SSID, BSSID if + * userspace roaming is enabled) + * + * @disassociate: disassociate from current AP + * + * @deauth: deauth from current AP + * + * @initiate_scan: ... + * + * @set_roaming: set who gets to control roaming + * + * @set_bssid: only valid with userspace roaming control, sets + * BSSID to use, forces reassociation if changing */ struct cfg80211_ops { int (*list_interfaces)(void *priv, void *data, @@ -46,14 +90,24 @@ struct cfg80211_ops { unsigned int type); int (*del_virtual_intf)(void *priv, int ifindex); - /* more things to be added... - * - * for a (*configure)(...) call I'd probably guess that the - * best bet would be to have one call that returns all - * possible options, one that sets them based on the - * struct genl_info *info, and one for that optimised - * set-at-once thing. - */ + int (*configure)(void *priv, struct net_device *dev, + struct cfg80211_config *cfg); + + void (*get_config)(void *priv, struct net_device *dev, + struct cfg80211_config *cfg); + int (*reassociate)(void *priv, struct net_device *dev); + int (*disassociate)(void *priv, struct net_device *dev); + int (*deauth)(void *priv, struct net_device *dev); + + /* + int (*initiate_scan)(void *priv, struct net_device *dev, TBD); + TBD: which channels to scan, passive/active, background, ...? + */ + + int (*set_roaming)(void *priv, struct net_device *dev, + int roaming_control); + + int (*set_bssid)(void *priv, struct net_device *dev, u8 *bssid); }; /* --- wireless-dev.orig/net/wireless/nl80211.c 2006-09-13 22:06:10.529647141 +0200 +++ wireless-dev/net/wireless/nl80211.c 2006-09-13 22:06:11.919647141 +0200 @@ -24,6 +24,22 @@ static struct genl_family nl80211_fam = }; /* policy for the attributes */ + +static int check_information_element(struct nlattr *nla) +{ + int len = nla_len(nla); + u8 *data = nla_data(nla); + int elementlen; + + while (len >= 2) { + /* 1 byte ID, 1 byte len, `len' bytes data */ + elementlen = *(data+1) + 2; + data += elementlen; + len -= elementlen; + } + return len ? -EINVAL : 0; +} + static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, @@ -33,6 +49,17 @@ static struct nla_policy nl80211_policy[ .len = NL80211_MAX_FRAME_LEN }, [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_NETWORK_ID] = { .type = NLA_U16 }, + [NL80211_ATTR_CHANNEL] = { .type = NLA_U32 }, + [NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 }, + [NL80211_ATTR_BSSID] = { .len = 6 }, + [NL80211_ATTR_SSID] = { .type = NLA_STRING, .len = 32 }, + [NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 }, + [NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_CUSTOM_CHECK, + .check = check_information_element }, + [NL80211_ATTR_ROAMING_CONTROL] = { .type = NLA_U32 }, + [NL80211_ATTR_SCAN_TYPE] = { .type = NLA_U32 }, }; /* netlink command implementations */ @@ -303,6 +330,77 @@ static int nl80211_del_virt_intf(struct return err; } +static int nl80211_configure(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_driver *drv; + int ifindex, err; + struct net_device *dev; + struct cfg80211_config config; + struct nlattr *attr; + + if (!info->attrs[NL80211_ATTR_IFINDEX]) + return -EINVAL; + + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + dev = dev_get_by_index(ifindex); + if (!dev) + return -ENODEV; + + drv = cfg80211_get_drv_from_info(info); + if (IS_ERR(drv)) { + err = PTR_ERR(drv); + goto out_nodrv; + } + + if (!drv->ops->configure) { + err = -EOPNOTSUPP; + goto out; + } + + memset(&config, 0, sizeof(config)); + + attr = info->attrs[NL80211_ATTR_SSID]; + if (attr) + config.ssid = nla_data(attr); + + attr = info->attrs[NL80211_ATTR_NETWORK_ID]; + if (attr) { + config.valid |= CFG80211_CFG_VALID_NWID; + config.network_id = nla_get_u16(attr); + } + + attr = info->attrs[NL80211_ATTR_RX_SENSITIVITY]; + if (attr) { + config.valid |= CFG80211_CFG_VALID_RX_SENSITIVITY; + config.rx_sensitivity = (s32) nla_get_u32(attr); + } + + attr = info->attrs[NL80211_ATTR_TRANSMIT_POWER]; + if (attr) { + config.valid |= CFG80211_CFG_VALID_TRANSMIT_POWER; + config.transmit_power = nla_get_u32(attr); + } + + attr = info->attrs[NL80211_ATTR_FRAG_THRESHOLD]; + if (attr) { + config.valid |= CFG80211_CFG_VALID_FRAG_THRESHOLD; + config.fragmentation_threshold = nla_get_u32(attr); + } + + attr = info->attrs[NL80211_ATTR_CHANNEL]; + if (attr) { + config.valid |= CFG80211_CFG_VALID_CHANNEL; + config.channel = nla_get_u32(attr); + } + + err = drv->ops->configure(drv->priv, dev, &config); + out: + cfg80211_put_drv(drv); + out_nodrv: + dev_put(dev); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_CMDLIST, @@ -337,6 +435,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_CONFIGURE, + .doit = nl80211_configure, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/wireless/wext-compat.c 2006-09-13 22:06:11.929647141 +0200 @@ -0,0 +1,25 @@ +/* NOT YET */ + +To implement compatibility, we add a new field to struct net_device +that contains the pending configuration structure. This is dynamically +allocated when needed and freed when committed. +In a way it replaces the wireless_handlers field in there which is now +done by dynamic lookup. No worries. No one is going to have thousands +of wireless devices, and if that changes we can still trivially change +this assumption :) + +Commit is done some time after the last parameter was changed +(with each parameter change simply (re-)schedule a timer) or +if explicitly asked for. This is probably not what most people +would expect, but perfectly fine in the WE API. + +compatibility mappings: + +SIOCSIWAP + -> if bssid is all-ones: set roaming to kernel, reassociate + -> if bssid is all-zeroes: set roaming to kernel + -> otherwise: set roaming to userspace, set bssid + +SIOCGIWAP + -> get association parameters and fill return bssid appropriately + - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html