From 25519a2a769d42fc2733a8f119682272d99b1304 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:22:38 -0800 Subject: wext: Remove inline from get_priv_size() and adjust_priv_size(). The compiler inlines when appropriate. Signed-off-by: David S. Miller --- net/wireless/wext.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 947188a5b93..e83d74affd7 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -500,7 +500,7 @@ static int call_commit_handler(struct net_device *dev) /* * Calculate size of private arguments */ -static inline int get_priv_size(__u16 args) +static int get_priv_size(__u16 args) { int num = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; @@ -512,8 +512,7 @@ static inline int get_priv_size(__u16 args) /* * Re-calculate the size of private arguments */ -static inline int adjust_priv_size(__u16 args, - union iwreq_data * wrqu) +static int adjust_priv_size(__u16 args, union iwreq_data *wrqu) { int num = wrqu->data.length; int max = args & IW_PRIV_SIZE_MASK; -- cgit v1.2.3 From 208887d4cc5a5c1eeb68bd170e21e32b1129cd94 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:24:24 -0800 Subject: wext: Make adjust_priv_size() take a "struct iw_point *". Signed-off-by: David S. Miller --- net/wireless/wext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index e83d74affd7..cd2cf9fec10 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -512,9 +512,9 @@ static int get_priv_size(__u16 args) /* * Re-calculate the size of private arguments */ -static int adjust_priv_size(__u16 args, union iwreq_data *wrqu) +static int adjust_priv_size(__u16 args, struct iw_point *iwp) { - int num = wrqu->data.length; + int num = iwp->length; int max = args & IW_PRIV_SIZE_MASK; int type = (args & IW_PRIV_TYPE_MASK) >> 12; @@ -976,7 +976,7 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, * avoid leaking kernel bits outside. */ if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { extra_size = adjust_priv_size(descr->get_args, - &(iwr->u)); + &(iwr->u.data)); } err = copy_to_user(iwr->u.data.pointer, extra, -- cgit v1.2.3 From 84149b0fca08f9ec554dfc28dabc39839fdf8a06 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:27:17 -0800 Subject: wext: Extract standard call iw_point handling into seperate function. Signed-off-by: David S. Miller --- net/wireless/wext.c | 258 +++++++++++++++++++++++++++------------------------- 1 file changed, 134 insertions(+), 124 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index cd2cf9fec10..d17c0f45acc 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -694,6 +694,138 @@ void wext_proc_exit(struct net *net) */ /* ---------------------------------------------------------------- */ +static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, + const struct iw_ioctl_description *descr, + iw_handler handler, struct net_device *dev, + struct iw_request_info *info) +{ + int err, extra_size, user_length = 0, essid_compat = 0; + char *extra; + + /* Calculate space needed by arguments. Always allocate + * for max space. + */ + extra_size = descr->max_tokens * descr->token_size; + + /* Check need for ESSID compatibility for WE < 21 */ + switch (cmd) { + case SIOCSIWESSID: + case SIOCGIWESSID: + case SIOCSIWNICKN: + case SIOCGIWNICKN: + if (iwp->length == descr->max_tokens + 1) + essid_compat = 1; + else if (IW_IS_SET(cmd) && (iwp->length != 0)) { + char essid[IW_ESSID_MAX_SIZE + 1]; + + err = copy_from_user(essid, iwp->pointer, + iwp->length * + descr->token_size); + if (err) + return -EFAULT; + + if (essid[iwp->length - 1] == '\0') + essid_compat = 1; + } + break; + default: + break; + } + + iwp->length -= essid_compat; + + /* Check what user space is giving us */ + if (IW_IS_SET(cmd)) { + /* Check NULL pointer */ + if (!iwp->pointer && iwp->length != 0) + return -EFAULT; + /* Check if number of token fits within bounds */ + if (iwp->length > descr->max_tokens) + return -E2BIG; + if (iwp->length < descr->min_tokens) + return -EINVAL; + } else { + /* Check NULL pointer */ + if (!iwp->pointer) + return -EFAULT; + /* Save user space buffer size for checking */ + user_length = iwp->length; + + /* Don't check if user_length > max to allow forward + * compatibility. The test user_length < min is + * implied by the test at the end. + */ + + /* Support for very large requests */ + if ((descr->flags & IW_DESCR_FLAG_NOMAX) && + (user_length > descr->max_tokens)) { + /* Allow userspace to GET more than max so + * we can support any size GET requests. + * There is still a limit : -ENOMEM. + */ + extra_size = user_length * descr->token_size; + + /* Note : user_length is originally a __u16, + * and token_size is controlled by us, + * so extra_size won't get negative and + * won't overflow... + */ + } + } + + /* kzalloc() ensures NULL-termination for essid_compat. */ + extra = kzalloc(extra_size, GFP_KERNEL); + if (!extra) + return -ENOMEM; + + /* If it is a SET, get all the extra data in here */ + if (IW_IS_SET(cmd) && (iwp->length != 0)) { + if (copy_from_user(extra, iwp->pointer, + iwp->length * + descr->token_size)) { + err = -EFAULT; + goto out; + } + } + + err = handler(dev, info, (union iwreq_data *) iwp, extra); + + iwp->length += essid_compat; + + /* If we have something to return to the user */ + if (!err && IW_IS_GET(cmd)) { + /* Check if there is enough buffer up there */ + if (user_length < iwp->length) { + err = -E2BIG; + goto out; + } + + if (copy_to_user(iwp->pointer, extra, + iwp->length * + descr->token_size)) { + err = -EFAULT; + goto out; + } + } + + /* Generate an event to notify listeners of the change */ + if ((descr->flags & IW_DESCR_FLAG_EVENT) && err == -EIWCOMMIT) { + union iwreq_data *data = (union iwreq_data *) iwp; + + if (descr->flags & IW_DESCR_FLAG_RESTRICT) + /* If the event is restricted, don't + * export the payload. + */ + wireless_send_event(dev, cmd, data, NULL); + else + wireless_send_event(dev, cmd, data, extra); + } + +out: + kfree(extra); + return err; +} + /* * Wrapper to call a standard Wireless Extension handler. * We do various checks and also take care of moving data between @@ -729,130 +861,8 @@ static int ioctl_standard_call(struct net_device * dev, ((ret == 0) || (ret == -EIWCOMMIT))) wireless_send_event(dev, cmd, &(iwr->u), NULL); } else { - char * extra; - int extra_size; - int user_length = 0; - int err; - int essid_compat = 0; - - /* Calculate space needed by arguments. Always allocate - * for max space. Easier, and won't last long... */ - extra_size = descr->max_tokens * descr->token_size; - - /* Check need for ESSID compatibility for WE < 21 */ - switch (cmd) { - case SIOCSIWESSID: - case SIOCGIWESSID: - case SIOCSIWNICKN: - case SIOCGIWNICKN: - if (iwr->u.data.length == descr->max_tokens + 1) - essid_compat = 1; - else if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { - char essid[IW_ESSID_MAX_SIZE + 1]; - - err = copy_from_user(essid, iwr->u.data.pointer, - iwr->u.data.length * - descr->token_size); - if (err) - return -EFAULT; - - if (essid[iwr->u.data.length - 1] == '\0') - essid_compat = 1; - } - break; - default: - break; - } - - iwr->u.data.length -= essid_compat; - - /* Check what user space is giving us */ - if (IW_IS_SET(cmd)) { - /* Check NULL pointer */ - if ((iwr->u.data.pointer == NULL) && - (iwr->u.data.length != 0)) - return -EFAULT; - /* Check if number of token fits within bounds */ - if (iwr->u.data.length > descr->max_tokens) - return -E2BIG; - if (iwr->u.data.length < descr->min_tokens) - return -EINVAL; - } else { - /* Check NULL pointer */ - if (iwr->u.data.pointer == NULL) - return -EFAULT; - /* Save user space buffer size for checking */ - user_length = iwr->u.data.length; - - /* Don't check if user_length > max to allow forward - * compatibility. The test user_length < min is - * implied by the test at the end. */ - - /* Support for very large requests */ - if ((descr->flags & IW_DESCR_FLAG_NOMAX) && - (user_length > descr->max_tokens)) { - /* Allow userspace to GET more than max so - * we can support any size GET requests. - * There is still a limit : -ENOMEM. */ - extra_size = user_length * descr->token_size; - /* Note : user_length is originally a __u16, - * and token_size is controlled by us, - * so extra_size won't get negative and - * won't overflow... */ - } - } - - /* Create the kernel buffer */ - /* kzalloc ensures NULL-termination for essid_compat */ - extra = kzalloc(extra_size, GFP_KERNEL); - if (extra == NULL) - return -ENOMEM; - - /* If it is a SET, get all the extra data in here */ - if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { - err = copy_from_user(extra, iwr->u.data.pointer, - iwr->u.data.length * - descr->token_size); - if (err) { - kfree(extra); - return -EFAULT; - } - } - - /* Call the handler */ - ret = handler(dev, &info, &(iwr->u), extra); - - iwr->u.data.length += essid_compat; - - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { - /* Check if there is enough buffer up there */ - if (user_length < iwr->u.data.length) { - kfree(extra); - return -E2BIG; - } - - err = copy_to_user(iwr->u.data.pointer, extra, - iwr->u.data.length * - descr->token_size); - if (err) - ret = -EFAULT; - } - - /* Generate an event to notify listeners of the change */ - if ((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) { - if (descr->flags & IW_DESCR_FLAG_RESTRICT) - /* If the event is restricted, don't - * export the payload */ - wireless_send_event(dev, cmd, &(iwr->u), NULL); - else - wireless_send_event(dev, cmd, &(iwr->u), - extra); - } - - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); + ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, + handler, dev, &info); } /* Call commit handler if needed and defined */ -- cgit v1.2.3 From d88174e4d295f0880e5f9cb6d42f26b0367c8fd9 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:33:46 -0800 Subject: wext: Extract private call iw_point handling into seperate functions. Signed-off-by: David S. Miller --- net/wireless/wext.c | 141 +++++++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 67 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index d17c0f45acc..a1cd19add6d 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -890,25 +890,22 @@ static int ioctl_standard_call(struct net_device * dev, * a iw_handler but process it in your ioctl handler (i.e. use the * old driver API). */ -static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, - unsigned int cmd, iw_handler handler) +static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, + const struct iw_priv_args **descrp) { - struct iwreq * iwr = (struct iwreq *) ifr; - const struct iw_priv_args * descr = NULL; - struct iw_request_info info; - int extra_size = 0; - int i; - int ret = -EINVAL; + const struct iw_priv_args *descr; + int i, extra_size; - /* Get the description of the IOCTL */ - for (i = 0; i < dev->wireless_handlers->num_private_args; i++) + descr = NULL; + for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { if (cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &(dev->wireless_handlers->private_args[i]); + descr = &dev->wireless_handlers->private_args[i]; break; } + } - /* Compute the size of the set/get arguments */ - if (descr != NULL) { + extra_size = 0; + if (descr) { if (IW_IS_SET(cmd)) { int offset = 0; /* For sub-ioctls */ /* Check for sub-ioctl handler */ @@ -933,72 +930,82 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, extra_size = 0; } } + *descrp = descr; + return extra_size; +} - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; +static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, + const struct iw_priv_args *descr, + iw_handler handler, struct net_device *dev, + struct iw_request_info *info, int extra_size) +{ + char *extra; + int err; - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); - } else { - char * extra; - int err; + /* Check what user space is giving us */ + if (IW_IS_SET(cmd)) { + if (!iwp->pointer && iwp->length != 0) + return -EFAULT; - /* Check what user space is giving us */ - if (IW_IS_SET(cmd)) { - /* Check NULL pointer */ - if ((iwr->u.data.pointer == NULL) && - (iwr->u.data.length != 0)) - return -EFAULT; + if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) + return -E2BIG; + } else if (!iwp->pointer) + return -EFAULT; - /* Does it fits within bounds ? */ - if (iwr->u.data.length > (descr->set_args & - IW_PRIV_SIZE_MASK)) - return -E2BIG; - } else if (iwr->u.data.pointer == NULL) - return -EFAULT; + extra = kmalloc(extra_size, GFP_KERNEL); + if (!extra) + return -ENOMEM; - /* Always allocate for max space. Easier, and won't last - * long... */ - extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) - return -ENOMEM; - - /* If it is a SET, get all the extra data in here */ - if (IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { - err = copy_from_user(extra, iwr->u.data.pointer, - extra_size); - if (err) { - kfree(extra); - return -EFAULT; - } + /* If it is a SET, get all the extra data in here */ + if (IW_IS_SET(cmd) && (iwp->length != 0)) { + if (copy_from_user(extra, iwp->pointer, extra_size)) { + err = -EFAULT; + goto out; } + } - /* Call the handler */ - ret = handler(dev, &info, &(iwr->u), extra); + /* Call the handler */ + err = handler(dev, info, (union iwreq_data *) iwp, extra); - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { + /* If we have something to return to the user */ + if (!err && IW_IS_GET(cmd)) { + /* Adjust for the actual length if it's variable, + * avoid leaking kernel bits outside. + */ + if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) + extra_size = adjust_priv_size(descr->get_args, iwp); - /* Adjust for the actual length if it's variable, - * avoid leaking kernel bits outside. */ - if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { - extra_size = adjust_priv_size(descr->get_args, - &(iwr->u.data)); - } + if (copy_to_user(iwp->pointer, extra, extra_size)) + err = -EFAULT; + } - err = copy_to_user(iwr->u.data.pointer, extra, - extra_size); - if (err) - ret = -EFAULT; - } +out: + kfree(extra); + return err; +} - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); - } +static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, + unsigned int cmd, iw_handler handler) +{ + struct iwreq *iwr = (struct iwreq *) ifr; + int extra_size = 0, ret = -EINVAL; + const struct iw_priv_args *descr; + struct iw_request_info info; + extra_size = get_priv_descr_and_size(dev, cmd, &descr); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, + handler, dev, &info, extra_size); + } /* Call commit handler if needed and defined */ if (ret == -EIWCOMMIT) -- cgit v1.2.3 From 67dd7608078b17f63f29ff2108fc5bf2407ddcec Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:36:31 -0800 Subject: wext: Pull ioctl permission checking out into helper function. Signed-off-by: David S. Miller --- net/wireless/wext.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index a1cd19add6d..e96559ea0fb 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1061,18 +1061,26 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned i return -EOPNOTSUPP; } +/* If command is `set a parameter', or `get the encoding parameters', + * check if the user has the right to do it. + */ +static int wext_permission_check(unsigned int cmd) +{ + if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) + && !capable(CAP_NET_ADMIN)) + return -EPERM; + + return 0; +} + /* entry point from dev ioctl */ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg) { - int ret; + int ret = wext_permission_check(cmd); - /* If command is `set a parameter', or - * `get the encoding parameters', check if - * the user has the right to do it */ - if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || cmd == SIOCGIWENCODEEXT) - && !capable(CAP_NET_ADMIN)) - return -EPERM; + if (ret) + return ret; dev_load(net, ifr->ifr_name); rtnl_lock(); -- cgit v1.2.3 From ca1e8bb8e4e89e2769e2b39eb29fdcfc5c19cf89 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:41:45 -0800 Subject: wext: Parameterize the standard/private handlers. The WEXT standard and private handlers to use are now arguments to wireless_process_ioctl(). Signed-off-by: David S. Miller --- net/wireless/wext.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index e96559ea0fb..929d24120f3 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1015,11 +1015,17 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, } /* ---------------------------------------------------------------- */ +typedef int (*wext_ioctl_func)(struct net_device *, struct ifreq *, + unsigned int, iw_handler); + /* * Main IOCTl dispatcher. * Check the type of IOCTL and call the appropriate wrapper... */ -static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd) +static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, + unsigned int cmd, + wext_ioctl_func standard, + wext_ioctl_func private) { struct net_device *dev; iw_handler handler; @@ -1035,12 +1041,12 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned i * Note that 'cmd' is already filtered in dev_ioctl() with * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ if (cmd == SIOCGIWSTATS) - return ioctl_standard_call(dev, ifr, cmd, - &iw_handler_get_iwstats); + return standard(dev, ifr, cmd, + &iw_handler_get_iwstats); if (cmd == SIOCGIWPRIV && dev->wireless_handlers) - return ioctl_standard_call(dev, ifr, cmd, - &iw_handler_get_private); + return standard(dev, ifr, cmd, + &iw_handler_get_private); /* Basic check */ if (!netif_device_present(dev)) @@ -1051,9 +1057,9 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned i if (handler) { /* Standard and private are not the same */ if (cmd < SIOCIWFIRSTPRIV) - return ioctl_standard_call(dev, ifr, cmd, handler); + return standard(dev, ifr, cmd, handler); else - return ioctl_private_call(dev, ifr, cmd, handler); + return private(dev, ifr, cmd, handler); } /* Old driver API : call driver ioctl handler */ if (dev->do_ioctl) @@ -1084,7 +1090,9 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, dev_load(net, ifr->ifr_name); rtnl_lock(); - ret = wireless_process_ioctl(net, ifr, cmd); + ret = wireless_process_ioctl(net, ifr, cmd, + ioctl_standard_call, + ioctl_private_call); rtnl_unlock(); if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq))) return -EFAULT; -- cgit v1.2.3 From d2911255590d9ca561a481b9dbebcfcbbf38fa4e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 21 Dec 2007 03:46:01 -0800 Subject: wext: Pass iwreq pointer down into standard/private handlers. They have no need to see the object as an ifreq. Signed-off-by: David S. Miller --- net/wireless/wext.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 929d24120f3..e9c88172ec5 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -832,11 +832,10 @@ out: * user space and kernel space. */ static int ioctl_standard_call(struct net_device * dev, - struct ifreq * ifr, + struct iwreq *iwr, unsigned int cmd, iw_handler handler) { - struct iwreq * iwr = (struct iwreq *) ifr; const struct iw_ioctl_description * descr; struct iw_request_info info; int ret = -EINVAL; @@ -984,10 +983,9 @@ out: return err; } -static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, +static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, unsigned int cmd, iw_handler handler) { - struct iwreq *iwr = (struct iwreq *) ifr; int extra_size = 0, ret = -EINVAL; const struct iw_priv_args *descr; struct iw_request_info info; @@ -1015,7 +1013,7 @@ static int ioctl_private_call(struct net_device *dev, struct ifreq *ifr, } /* ---------------------------------------------------------------- */ -typedef int (*wext_ioctl_func)(struct net_device *, struct ifreq *, +typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, unsigned int, iw_handler); /* @@ -1027,6 +1025,7 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, wext_ioctl_func standard, wext_ioctl_func private) { + struct iwreq *iwr = (struct iwreq *) ifr; struct net_device *dev; iw_handler handler; @@ -1041,11 +1040,11 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, * Note that 'cmd' is already filtered in dev_ioctl() with * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ if (cmd == SIOCGIWSTATS) - return standard(dev, ifr, cmd, + return standard(dev, iwr, cmd, &iw_handler_get_iwstats); if (cmd == SIOCGIWPRIV && dev->wireless_handlers) - return standard(dev, ifr, cmd, + return standard(dev, iwr, cmd, &iw_handler_get_private); /* Basic check */ @@ -1057,9 +1056,9 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, if (handler) { /* Standard and private are not the same */ if (cmd < SIOCIWFIRSTPRIV) - return standard(dev, ifr, cmd, handler); + return standard(dev, iwr, cmd, handler); else - return private(dev, ifr, cmd, handler); + return private(dev, iwr, cmd, handler); } /* Old driver API : call driver ioctl handler */ if (dev->do_ioctl) -- cgit v1.2.3 From a67fa76d8be4e24e2d61cd76438a893d4c2886f7 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jun 2008 07:36:30 -0700 Subject: wext: Pull top-level ioctl dispatch logic into helper function. Signed-off-by: David S. Miller --- net/wireless/wext.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index e9c88172ec5..09022cbb58b 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1079,8 +1079,10 @@ static int wext_permission_check(unsigned int cmd) } /* entry point from dev ioctl */ -int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, - void __user *arg) +static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, + unsigned int cmd, + wext_ioctl_func standard, + wext_ioctl_func private) { int ret = wext_permission_check(cmd); @@ -1089,12 +1091,24 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, dev_load(net, ifr->ifr_name); rtnl_lock(); - ret = wireless_process_ioctl(net, ifr, cmd, - ioctl_standard_call, - ioctl_private_call); + ret = wireless_process_ioctl(net, ifr, cmd, standard, private); rtnl_unlock(); - if (IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq))) + + return ret; +} + +int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, + void __user *arg) +{ + int ret = wext_ioctl_dispatch(net, ifr, cmd, + ioctl_standard_call, + ioctl_private_call); + + if (ret >= 0 && + IW_IS_GET(cmd) && + copy_to_user(arg, ifr, sizeof(struct iwreq))) return -EFAULT; + return ret; } -- cgit v1.2.3 From 87de87d5e47f94b4ea647a5bd1bc8dc1f7930db4 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jun 2008 09:14:03 -0700 Subject: wext: Dispatch and handle compat ioctls entirely in net/wireless/wext.c Next we can kill the hacks in fs/compat_ioctl.c and also dispatch compat ioctls down into the driver and 80211 protocol helper layers in order to handle iw_point objects embedded in stream replies which need to be translated. Signed-off-by: David S. Miller --- net/socket.c | 10 +++++ net/wireless/wext.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) (limited to 'net') diff --git a/net/socket.c b/net/socket.c index 66c4a8cf6db..81fe8251304 100644 --- a/net/socket.c +++ b/net/socket.c @@ -90,6 +90,7 @@ #include #include +#include #include #include @@ -2210,10 +2211,19 @@ static long compat_sock_ioctl(struct file *file, unsigned cmd, { struct socket *sock = file->private_data; int ret = -ENOIOCTLCMD; + struct sock *sk; + struct net *net; + + sk = sock->sk; + net = sock_net(sk); if (sock->ops->compat_ioctl) ret = sock->ops->compat_ioctl(sock, cmd, arg); + if (ret == -ENOIOCTLCMD && + (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)) + ret = compat_wext_handle_ioctl(net, cmd, arg); + return ret; } #endif diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 09022cbb58b..1a4636a9fcd 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -1112,6 +1112,110 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, return ret; } +#ifdef CONFIG_COMPAT +static int compat_standard_call(struct net_device *dev, + struct iwreq *iwr, + unsigned int cmd, + iw_handler handler) +{ + const struct iw_ioctl_description *descr; + struct compat_iw_point *iwp_compat; + struct iw_request_info info; + struct iw_point iwp; + int err; + + descr = standard_ioctl + (cmd - SIOCIWFIRST); + + if (descr->header_type != IW_HEADER_TYPE_POINT) + return ioctl_standard_call(dev, iwr, cmd, handler); + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + info.cmd = cmd; + info.flags = 0; + + err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, &info); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + + return err; +} + +static int compat_private_call(struct net_device *dev, struct iwreq *iwr, + unsigned int cmd, iw_handler handler) +{ + const struct iw_priv_args *descr; + struct iw_request_info info; + int ret, extra_size; + + extra_size = get_priv_descr_and_size(dev, cmd, &descr); + + /* Prepare the call */ + info.cmd = cmd; + info.flags = 0; + + /* Check if we have a pointer to user space data or not. */ + if (extra_size == 0) { + /* No extra arguments. Trivial to handle */ + ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + } else { + struct compat_iw_point *iwp_compat; + struct iw_point iwp; + + iwp_compat = (struct compat_iw_point *) &iwr->u.data; + iwp.pointer = compat_ptr(iwp_compat->pointer); + iwp.length = iwp_compat->length; + iwp.flags = iwp_compat->flags; + + ret = ioctl_private_iw_point(&iwp, cmd, descr, + handler, dev, &info, extra_size); + + iwp_compat->pointer = ptr_to_compat(iwp.pointer); + iwp_compat->length = iwp.length; + iwp_compat->flags = iwp.flags; + } + + /* Call commit handler if needed and defined */ + if (ret == -EIWCOMMIT) + ret = call_commit_handler(dev); + + return ret; +} + +int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct iwreq iwr; + char *colon; + int ret; + + if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) + return -EFAULT; + + iwr.ifr_name[IFNAMSIZ-1] = 0; + colon = strchr(iwr.ifr_name, ':'); + if (colon) + *colon = 0; + + ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, + compat_standard_call, + compat_private_call); + + if (ret >= 0 && + IW_IS_GET(cmd) && + copy_to_user(argp, &iwr, sizeof(struct iwreq))) + return -EFAULT; + + return ret; +} +#endif + /************************* EVENT PROCESSING *************************/ /* * Process events generated by the wireless layer or the driver. -- cgit v1.2.3 From 0f5cabba49021d36e9f76bd97d7fa0f4a408063f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 3 Jun 2008 07:39:16 -0700 Subject: wext: Create IW_REQUEST_FLAG_COMPAT and set it as needed. Now low-level WEXT ioctl handlers can do compat handling when necessary. Signed-off-by: David S. Miller --- net/wireless/wext.c | 73 ++++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 40 deletions(-) (limited to 'net') diff --git a/net/wireless/wext.c b/net/wireless/wext.c index 1a4636a9fcd..273a8435999 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c @@ -834,10 +834,10 @@ out: static int ioctl_standard_call(struct net_device * dev, struct iwreq *iwr, unsigned int cmd, + struct iw_request_info *info, iw_handler handler) { const struct iw_ioctl_description * descr; - struct iw_request_info info; int ret = -EINVAL; /* Get the description of the IOCTL */ @@ -845,15 +845,11 @@ static int ioctl_standard_call(struct net_device * dev, return -EOPNOTSUPP; descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - /* Check if we have a pointer to user space data or not */ if (descr->header_type != IW_HEADER_TYPE_POINT) { /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), NULL); + ret = handler(dev, info, &(iwr->u), NULL); /* Generate an event to notify listeners of the change */ if ((descr->flags & IW_DESCR_FLAG_EVENT) && @@ -861,7 +857,7 @@ static int ioctl_standard_call(struct net_device * dev, wireless_send_event(dev, cmd, &(iwr->u), NULL); } else { ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, - handler, dev, &info); + handler, dev, info); } /* Call commit handler if needed and defined */ @@ -984,25 +980,21 @@ out: } static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, - unsigned int cmd, iw_handler handler) + unsigned int cmd, struct iw_request_info *info, + iw_handler handler) { int extra_size = 0, ret = -EINVAL; const struct iw_priv_args *descr; - struct iw_request_info info; extra_size = get_priv_descr_and_size(dev, cmd, &descr); - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - /* Check if we have a pointer to user space data or not. */ if (extra_size == 0) { /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); } else { ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, - handler, dev, &info, extra_size); + handler, dev, info, extra_size); } /* Call commit handler if needed and defined */ @@ -1014,7 +1006,8 @@ static int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, /* ---------------------------------------------------------------- */ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, - unsigned int, iw_handler); + unsigned int, struct iw_request_info *, + iw_handler); /* * Main IOCTl dispatcher. @@ -1022,6 +1015,7 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, */ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, + struct iw_request_info *info, wext_ioctl_func standard, wext_ioctl_func private) { @@ -1040,11 +1034,11 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, * Note that 'cmd' is already filtered in dev_ioctl() with * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ if (cmd == SIOCGIWSTATS) - return standard(dev, iwr, cmd, + return standard(dev, iwr, cmd, info, &iw_handler_get_iwstats); if (cmd == SIOCGIWPRIV && dev->wireless_handlers) - return standard(dev, iwr, cmd, + return standard(dev, iwr, cmd, info, &iw_handler_get_private); /* Basic check */ @@ -1056,9 +1050,9 @@ static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, if (handler) { /* Standard and private are not the same */ if (cmd < SIOCIWFIRSTPRIV) - return standard(dev, iwr, cmd, handler); + return standard(dev, iwr, cmd, info, handler); else - return private(dev, iwr, cmd, handler); + return private(dev, iwr, cmd, info, handler); } /* Old driver API : call driver ioctl handler */ if (dev->do_ioctl) @@ -1080,7 +1074,7 @@ static int wext_permission_check(unsigned int cmd) /* entry point from dev ioctl */ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, - unsigned int cmd, + unsigned int cmd, struct iw_request_info *info, wext_ioctl_func standard, wext_ioctl_func private) { @@ -1091,7 +1085,7 @@ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, dev_load(net, ifr->ifr_name); rtnl_lock(); - ret = wireless_process_ioctl(net, ifr, cmd, standard, private); + ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); rtnl_unlock(); return ret; @@ -1100,10 +1094,12 @@ static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg) { - int ret = wext_ioctl_dispatch(net, ifr, cmd, - ioctl_standard_call, - ioctl_private_call); + struct iw_request_info info = { .cmd = cmd, .flags = 0 }; + int ret; + ret = wext_ioctl_dispatch(net, ifr, cmd, &info, + ioctl_standard_call, + ioctl_private_call); if (ret >= 0 && IW_IS_GET(cmd) && copy_to_user(arg, ifr, sizeof(struct iwreq))) @@ -1116,28 +1112,25 @@ int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, static int compat_standard_call(struct net_device *dev, struct iwreq *iwr, unsigned int cmd, + struct iw_request_info *info, iw_handler handler) { const struct iw_ioctl_description *descr; struct compat_iw_point *iwp_compat; - struct iw_request_info info; struct iw_point iwp; int err; descr = standard_ioctl + (cmd - SIOCIWFIRST); if (descr->header_type != IW_HEADER_TYPE_POINT) - return ioctl_standard_call(dev, iwr, cmd, handler); + return ioctl_standard_call(dev, iwr, cmd, info, handler); iwp_compat = (struct compat_iw_point *) &iwr->u.data; iwp.pointer = compat_ptr(iwp_compat->pointer); iwp.length = iwp_compat->length; iwp.flags = iwp_compat->flags; - info.cmd = cmd; - info.flags = 0; - - err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, &info); + err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); iwp_compat->pointer = ptr_to_compat(iwp.pointer); iwp_compat->length = iwp.length; @@ -1147,22 +1140,18 @@ static int compat_standard_call(struct net_device *dev, } static int compat_private_call(struct net_device *dev, struct iwreq *iwr, - unsigned int cmd, iw_handler handler) + unsigned int cmd, struct iw_request_info *info, + iw_handler handler) { const struct iw_priv_args *descr; - struct iw_request_info info; int ret, extra_size; extra_size = get_priv_descr_and_size(dev, cmd, &descr); - /* Prepare the call */ - info.cmd = cmd; - info.flags = 0; - /* Check if we have a pointer to user space data or not. */ if (extra_size == 0) { /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); + ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); } else { struct compat_iw_point *iwp_compat; struct iw_point iwp; @@ -1173,7 +1162,7 @@ static int compat_private_call(struct net_device *dev, struct iwreq *iwr, iwp.flags = iwp_compat->flags; ret = ioctl_private_iw_point(&iwp, cmd, descr, - handler, dev, &info, extra_size); + handler, dev, info, extra_size); iwp_compat->pointer = ptr_to_compat(iwp.pointer); iwp_compat->length = iwp.length; @@ -1191,6 +1180,7 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; + struct iw_request_info info; struct iwreq iwr; char *colon; int ret; @@ -1203,7 +1193,10 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, if (colon) *colon = 0; - ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, + info.cmd = cmd; + info.flags = IW_REQUEST_FLAG_COMPAT; + + ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info, compat_standard_call, compat_private_call); -- cgit v1.2.3 From ccc580571cf0799d0460a085a7632b77753f083e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 16 Jun 2008 18:50:49 -0700 Subject: wext: Emit event stream entries correctly when compat. Three major portions to this change: 1) Add IW_EV_COMPAT_LCP_LEN, IW_EV_COMPAT_POINT_OFF, and IW_EV_COMPAT_POINT_LEN helper defines. 2) Delete iw_stream_check_add_*(), they are unused. 3) Add iw_request_info argument to iwe_stream_add_*(), and use it to size the event and pointer lengths correctly depending upon whether IW_REQUEST_FLAG_COMPAT is set or not. 4) The mechanical transformations to the drivers and wireless stack bits to get the iw_request_info passed down into the routines modified in #3. Also, explicit references to IW_EV_LCP_LEN are replaced with iwe_stream_lcp_len(info). With a lot of help and bug fixes from Masakazu Mokuno. Signed-off-by: David S. Miller --- net/ieee80211/ieee80211_wx.c | 48 ++++++++++++++++++-------------- net/mac80211/ieee80211_i.h | 5 +++- net/mac80211/mlme.c | 66 +++++++++++++++++++++++++------------------- net/mac80211/wext.c | 2 +- 4 files changed, 71 insertions(+), 50 deletions(-) (limited to 'net') diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index 822606b615c..973832dd7fa 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c @@ -43,8 +43,9 @@ static const char *ieee80211_modes[] = { #define MAX_CUSTOM_LEN 64 static char *ieee80211_translate_scan(struct ieee80211_device *ieee, - char *start, char *stop, - struct ieee80211_network *network) + char *start, char *stop, + struct ieee80211_network *network, + struct iw_request_info *info) { char custom[MAX_CUSTOM_LEN]; char *p; @@ -57,7 +58,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); /* Remaining entries will be displayed in the order we provide them */ @@ -66,17 +67,19 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.data.flags = 1; if (network->flags & NETWORK_EMPTY_ESSID) { iwe.u.data.length = sizeof(""); - start = iwe_stream_add_point(start, stop, &iwe, ""); + start = iwe_stream_add_point(info, start, stop, + &iwe, ""); } else { iwe.u.data.length = min(network->ssid_len, (u8) 32); - start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); } /* Add the protocol name */ iwe.cmd = SIOCGIWNAME; snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]); - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); /* Add mode */ iwe.cmd = SIOCGIWMODE; @@ -86,7 +89,8 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, else iwe.u.mode = IW_MODE_ADHOC; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN); + start = iwe_stream_add_event(info, start, stop, + &iwe, IW_EV_UINT_LEN); } /* Add channel and frequency */ @@ -95,7 +99,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel); iwe.u.freq.e = 6; iwe.u.freq.i = 0; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); /* Add encryption capability */ iwe.cmd = SIOCGIWENCODE; @@ -104,12 +108,13 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + start = iwe_stream_add_point(info, start, stop, + &iwe, network->ssid); /* Add basic and extended rates */ /* Rate : stuffing multiple values in a single event require a bit * more of magic - Jean II */ - current_val = start + IW_EV_LCP_LEN; + current_val = start + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; /* Those two flags are ignored... */ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; @@ -124,17 +129,19 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } for (; j < network->rates_ex_len; j++) { rate = network->rates_ex[j] & 0x7F; /* Bit rate given in 500 kb/s units (+ 0x80) */ iwe.u.bitrate.value = ((rate & 0x7f) * 500000); /* Add new value to event */ - current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); + current_val = iwe_stream_add_value(info, start, current_val, + stop, &iwe, IW_EV_PARAM_LEN); } /* Check if we added any rate */ - if((current_val - start) > IW_EV_LCP_LEN) + if ((current_val - start) > iwe_stream_lcp_len(info)) start = current_val; /* Add quality statistics */ @@ -181,14 +188,14 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.qual.level = network->stats.signal; } - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); iwe.cmd = IWEVCUSTOM; p = custom; iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); memset(&iwe, 0, sizeof(iwe)); if (network->wpa_ie_len) { @@ -196,7 +203,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, memcpy(buf, network->wpa_ie, network->wpa_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->wpa_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } memset(&iwe, 0, sizeof(iwe)); @@ -205,7 +212,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, memcpy(buf, network->rsn_ie, network->rsn_ie_len); iwe.cmd = IWEVGENIE; iwe.u.data.length = network->rsn_ie_len; - start = iwe_stream_add_point(start, stop, &iwe, buf); + start = iwe_stream_add_point(info, start, stop, &iwe, buf); } /* Add EXTRA: Age to display seconds since last beacon/probe response @@ -217,7 +224,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, jiffies_to_msecs(jiffies - network->last_scanned)); iwe.u.data.length = p - custom; if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); /* Add spectrum management information */ iwe.cmd = -1; @@ -238,7 +245,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, if (iwe.cmd == IWEVCUSTOM) { iwe.u.data.length = p - custom; - start = iwe_stream_add_point(start, stop, &iwe, custom); + start = iwe_stream_add_point(info, start, stop, &iwe, custom); } return start; @@ -272,7 +279,8 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, if (ieee->scan_age == 0 || time_after(network->last_scanned + ieee->scan_age, jiffies)) - ev = ieee80211_translate_scan(ieee, ev, stop, network); + ev = ieee80211_translate_scan(ieee, ev, stop, network, + info); else IEEE80211_DEBUG_SCAN("Not showing network '%s (" "%s)' due to age (%dms).\n", diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 14fccf16b80..80a9e7c07b4 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "key.h" #include "sta_info.h" @@ -867,7 +868,9 @@ int ieee80211_sta_set_bssid(struct net_device *dev, u8 *bssid); int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len); void ieee80211_sta_req_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta); -int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len); +int ieee80211_sta_scan_results(struct net_device *dev, + struct iw_request_info *info, + char *buf, size_t len); ieee80211_rx_result ieee80211_sta_rx_scan( struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 55659a730dc..e06d6450f21 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4087,6 +4087,7 @@ int ieee80211_sta_req_scan(struct net_device *dev, u8 *ssid, size_t ssid_len) static char * ieee80211_sta_scan_result(struct net_device *dev, + struct iw_request_info *info, struct ieee80211_sta_bss *bss, char *current_ev, char *end_buf) { @@ -4101,7 +4102,7 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_ADDR_LEN); memset(&iwe, 0, sizeof(iwe)); @@ -4109,13 +4110,13 @@ ieee80211_sta_scan_result(struct net_device *dev, if (bss_mesh_cfg(bss)) { iwe.u.data.length = bss_mesh_id_len(bss); iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss_mesh_id(bss)); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss_mesh_id(bss)); } else { iwe.u.data.length = bss->ssid_len; iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->ssid); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ssid); } if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) @@ -4128,22 +4129,22 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.u.mode = IW_MODE_MASTER; else iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, - IW_EV_UINT_LEN); + current_ev = iwe_stream_add_event(info, current_ev, end_buf, + &iwe, IW_EV_UINT_LEN); } memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = bss->freq; iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVQUAL; @@ -4151,7 +4152,7 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.u.qual.level = bss->signal; iwe.u.qual.noise = bss->noise; iwe.u.qual.updated = local->wstats_flags; - current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_QUAL_LEN); memset(&iwe, 0, sizeof(iwe)); @@ -4161,35 +4162,36 @@ ieee80211_sta_scan_result(struct net_device *dev, else iwe.u.data.flags = IW_ENCODE_DISABLED; iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, ""); if (bss && bss->wpa_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->wpa_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->wpa_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->wpa_ie); } if (bss && bss->rsn_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->rsn_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->rsn_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->rsn_ie); } if (bss && bss->ht_ie) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = bss->ht_ie_len; - current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, - bss->ht_ie); + current_ev = iwe_stream_add_point(info, current_ev, end_buf, + &iwe, bss->ht_ie); } if (bss && bss->supp_rates_len > 0) { /* display all supported rates in readable format */ - char *p = current_ev + IW_EV_LCP_LEN; + char *p = current_ev + iwe_stream_lcp_len(info); int i; memset(&iwe, 0, sizeof(iwe)); @@ -4200,7 +4202,7 @@ ieee80211_sta_scan_result(struct net_device *dev, for (i = 0; i < bss->supp_rates_len; i++) { iwe.u.bitrate.value = ((bss->supp_rates[i] & 0x7f) * 500000); - p = iwe_stream_add_value(current_ev, p, + p = iwe_stream_add_value(info, current_ev, p, end_buf, &iwe, IW_EV_PARAM_LEN); } current_ev = p; @@ -4214,7 +4216,8 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); kfree(buf); } @@ -4229,31 +4232,36 @@ ieee80211_sta_scan_result(struct net_device *dev, iwe.cmd = IWEVCUSTOM; sprintf(buf, "Mesh network (version %d)", cfg[0]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Path Selection Protocol ID: " "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], cfg[4]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Path Selection Metric ID: " "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], cfg[8]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Congestion Control Mode ID: " "0x%02X%02X%02X%02X", cfg[9], cfg[10], cfg[11], cfg[12]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); sprintf(buf, "Channel Precedence: " "0x%02X%02X%02X%02X", cfg[13], cfg[14], cfg[15], cfg[16]); iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(current_ev, end_buf, + current_ev = iwe_stream_add_point(info, current_ev, + end_buf, &iwe, buf); kfree(buf); } @@ -4263,7 +4271,9 @@ ieee80211_sta_scan_result(struct net_device *dev, } -int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) +int ieee80211_sta_scan_results(struct net_device *dev, + struct iw_request_info *info, + char *buf, size_t len) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); char *current_ev = buf; @@ -4276,8 +4286,8 @@ int ieee80211_sta_scan_results(struct net_device *dev, char *buf, size_t len) spin_unlock_bh(&local->sta_bss_lock); return -E2BIG; } - current_ev = ieee80211_sta_scan_result(dev, bss, current_ev, - end_buf); + current_ev = ieee80211_sta_scan_result(dev, info, bss, + current_ev, end_buf); } spin_unlock_bh(&local->sta_bss_lock); return current_ev - buf; diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5af3862e719..f47d13bdf7f 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -567,7 +567,7 @@ static int ieee80211_ioctl_giwscan(struct net_device *dev, if (local->sta_sw_scanning || local->sta_hw_scanning) return -EAGAIN; - res = ieee80211_sta_scan_results(dev, extra, data->length); + res = ieee80211_sta_scan_results(dev, info, extra, data->length); if (res >= 0) { data->length = res; return 0; -- cgit v1.2.3 From c8fcd905a59a535bff93a120ac44b09ce24e13e6 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:57 -0300 Subject: rfkill: fix minor typo in kernel doc Fix a minor typo in an exported function documentation Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 4e10a95de83..f95081a4a02 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -412,7 +412,7 @@ int rfkill_register(struct rfkill *rfkill) EXPORT_SYMBOL(rfkill_register); /** - * rfkill_unregister - Uegister a rfkill structure. + * rfkill_unregister - Unregister a rfkill structure. * @rfkill: rfkill structure to be unregistered * * This function should be called by the network driver during device -- cgit v1.2.3 From 28f089c18464810ec9e91ee10a89adbb02ad7765 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:58 -0300 Subject: rfkill: handle SW_RFKILL_ALL events Teach rfkill-input how to handle SW_RFKILL_ALL events (new name for the SW_RADIO event). SW_RFKILL_ALL is an absolute enable-or-disable command that is tied to all radios in a system. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index e4b051dbed6..9d6c9255bf2 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -55,6 +55,22 @@ static void rfkill_task_handler(struct work_struct *work) mutex_unlock(&task->mutex); } +static void rfkill_schedule_set(struct rfkill_task *task, + enum rfkill_state desired_state) +{ + unsigned long flags; + + spin_lock_irqsave(&task->lock, flags); + + if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { + task->desired_state = desired_state; + task->last = jiffies; + schedule_work(&task->work); + } + + spin_unlock_irqrestore(&task->lock, flags); +} + static void rfkill_schedule_toggle(struct rfkill_task *task) { unsigned long flags; @@ -87,9 +103,9 @@ static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); static void rfkill_event(struct input_handle *handle, unsigned int type, - unsigned int code, int down) + unsigned int code, int data) { - if (type == EV_KEY && down == 1) { + if (type == EV_KEY && data == 1) { switch (code) { case KEY_WLAN: rfkill_schedule_toggle(&rfkill_wlan); @@ -106,6 +122,26 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, default: break; } + } else if (type == EV_SW) { + switch (code) { + case SW_RFKILL_ALL: + /* EVERY radio type. data != 0 means radios ON */ + rfkill_schedule_set(&rfkill_wimax, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + rfkill_schedule_set(&rfkill_uwb, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + rfkill_schedule_set(&rfkill_bt, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + rfkill_schedule_set(&rfkill_wlan, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); + break; + default: + break; + } } } @@ -168,6 +204,11 @@ static const struct input_device_id rfkill_ids[] = { .evbit = { BIT_MASK(EV_KEY) }, .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) }, }, + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT, + .evbit = { BIT(EV_SW) }, + .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) }, + }, { } }; -- cgit v1.2.3 From e954b0b85b9e737564b8ad9738de5816747b5901 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:22:59 -0300 Subject: rfkill: add parameter to disable radios by default Currently, radios are always enabled when their rfkill interface is registered. This is not optimal, the safest state for a radio is to be offline unless the user turns it on. Add a module parameter that causes all radios to be disabled when their rfkill interface is registered. The module default is not changed so unless the parameter is used, radios will still be forced to their enabled state when they are registered. The new rfkill module parameter is called "default_state". Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index f95081a4a02..3edc585dcfa 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,6 +39,11 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_mutex); +static unsigned int rfkill_default_state = RFKILL_STATE_ON; +module_param_named(default_state, rfkill_default_state, uint, 0444); +MODULE_PARM_DESC(default_state, + "Default initial state for all radio types, 0 = radio off"); + static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; @@ -436,8 +441,12 @@ static int __init rfkill_init(void) int error; int i; + if (rfkill_default_state != RFKILL_STATE_OFF && + rfkill_default_state != RFKILL_STATE_ON) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) - rfkill_states[i] = RFKILL_STATE_ON; + rfkill_states[i] = rfkill_default_state; error = class_register(&rfkill_class); if (error) { -- cgit v1.2.3 From 801e49af4c1a9b988ba0d25de2b368c99c3bf2b3 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:00 -0300 Subject: rfkill: add read-write rfkill switch support Currently, rfkill support for read/write rfkill switches is hacked through a round-trip over the input layer and rfkill-input to let a driver sync rfkill->state to hardware changes. This is buggy and sub-optimal. It causes real problems. It is best to think of the rfkill class as supporting only write-only switches at the moment. In order to implement the read/write functionality properly: Add a get_state() hook that is called by the class every time it needs to fetch the current state of the switch. Add a call to this hook every time the *current* state of the radio plays a role in a decision. Also add a force_state() method that can be used to forcefully syncronize the class' idea of the current state of the switch. This allows for a faster implementation of the read/write functionality, as a driver which get events on switch changes can avoid the need for a get_state() hook. If the get_state() hook is left as NULL, current behaviour is maintained, so this change is fully backwards compatible with the current rfkill drivers. For hardware that issues events when the rfkill state changes, leave get_state() NULL in the rfkill struct, set the initial state properly before registering with the rfkill class, and use the force_state() method in the driver to keep the rfkill interface up-to-date. get_state() can be called by the class from atomic context. It must not sleep. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3edc585dcfa..4ae4486c77e 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -62,19 +62,39 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void update_rfkill_state(struct rfkill *rfkill) +{ + enum rfkill_state newstate; + + if (rfkill->get_state) { + mutex_lock(&rfkill->mutex); + if (!rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; + mutex_unlock(&rfkill->mutex); + } +} + static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state) { int retval = 0; + enum rfkill_state oldstate, newstate; + + oldstate = rfkill->state; + + if (rfkill->get_state && + !rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; if (state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) { + if (!retval) rfkill->state = state; - rfkill_led_trigger(rfkill, state); - } } + if (rfkill->state != oldstate) + rfkill_led_trigger(rfkill, rfkill->state); + return retval; } @@ -105,6 +125,32 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_force_state - Force the internal rfkill radio state + * @rfkill: pointer to the rfkill class to modify. + * @state: the current radio state the class should be forced to. + * + * This function updates the internal state of the radio cached + * by the rfkill class. It should be used when the driver gets + * a notification by the firmware/hardware of the current *real* + * state of the radio rfkill switch. + * + * It may not be called from an atomic context. + */ +int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) +{ + if (state != RFKILL_STATE_OFF && + state != RFKILL_STATE_ON) + return -EINVAL; + + mutex_lock(&rfkill->mutex); + rfkill->state = state; + mutex_unlock(&rfkill->mutex); + + return 0; +} +EXPORT_SYMBOL(rfkill_force_state); + static ssize_t rfkill_name_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -147,6 +193,7 @@ static ssize_t rfkill_state_show(struct device *dev, { struct rfkill *rfkill = to_rfkill(dev); + update_rfkill_state(rfkill); return sprintf(buf, "%d\n", rfkill->state); } -- cgit v1.2.3 From 477576a073699783abb53ae14993d5d41c66301d Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:01 -0300 Subject: rfkill: add the WWAN radio type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately, instead of adding a generic Wireless WAN type, a technology- specific type (WiMAX) was added. That's useless for other WWAN devices, such as EDGE, UMTS, X-RTT and other such radios. Add a WWAN rfkill type for generic wireless WAN devices. No keys are added as most devices really want to use KEY_WLAN for WWAN control (in a cycle of none, WLAN, WWAN, WLAN+WWAN) and need no specific keycode added. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Iñaky Pérez-González Cc: David S. Miller Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 4 ++++ net/rfkill/rfkill.c | 3 +++ 2 files changed, 7 insertions(+) (limited to 'net') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 9d6c9255bf2..29c13d308b3 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -101,6 +101,7 @@ static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); static DEFINE_RFKILL_TASK(rfkill_bt, RFKILL_TYPE_BLUETOOTH); static DEFINE_RFKILL_TASK(rfkill_uwb, RFKILL_TYPE_UWB); static DEFINE_RFKILL_TASK(rfkill_wimax, RFKILL_TYPE_WIMAX); +static DEFINE_RFKILL_TASK(rfkill_wwan, RFKILL_TYPE_WWAN); static void rfkill_event(struct input_handle *handle, unsigned int type, unsigned int code, int data) @@ -126,6 +127,9 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, switch (code) { case SW_RFKILL_ALL: /* EVERY radio type. data != 0 means radios ON */ + rfkill_schedule_set(&rfkill_wwan, + (data)? RFKILL_STATE_ON: + RFKILL_STATE_OFF); rfkill_schedule_set(&rfkill_wimax, (data)? RFKILL_STATE_ON: RFKILL_STATE_OFF); diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 4ae4486c77e..79f3bbb027f 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -180,6 +180,9 @@ static ssize_t rfkill_type_show(struct device *dev, case RFKILL_TYPE_WIMAX: type = "wimax"; break; + case RFKILL_TYPE_WWAN: + type = "wwan"; + break; default: BUG(); } -- cgit v1.2.3 From 526324b61a9667ed9a71f0a8a8899cf675346c76 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:02 -0300 Subject: rfkill: rework suspend and resume handlers The resume handler should reset the wireless transmitter rfkill state to exactly what it was when the system was suspended. Do it, and do it using the normal routines for state change while at it. The suspend handler should force-switch the transmitter to blocked state, ignoring caches. Do it. Also take an opportunity shot to rfkill_remove_switch() and also force the transmitter to blocked state there, bypassing caches. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 79f3bbb027f..fb566902030 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -75,24 +75,25 @@ static void update_rfkill_state(struct rfkill *rfkill) } static int rfkill_toggle_radio(struct rfkill *rfkill, - enum rfkill_state state) + enum rfkill_state state, + int force) { int retval = 0; enum rfkill_state oldstate, newstate; oldstate = rfkill->state; - if (rfkill->get_state && + if (rfkill->get_state && !force && !rfkill->get_state(rfkill->data, &newstate)) rfkill->state = newstate; - if (state != rfkill->state) { + if (force || state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); if (!retval) rfkill->state = state; } - if (rfkill->state != oldstate) + if (force || rfkill->state != oldstate) rfkill_led_trigger(rfkill, rfkill->state); return retval; @@ -107,7 +108,6 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, * a specific switch is claimed by userspace in which case it is * left alone. */ - void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) { struct rfkill *rfkill; @@ -118,7 +118,7 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) list_for_each_entry(rfkill, &rfkill_list, node) { if ((!rfkill->user_claim) && (rfkill->type == type)) - rfkill_toggle_radio(rfkill, state); + rfkill_toggle_radio(rfkill, state, 0); } mutex_unlock(&rfkill_mutex); @@ -214,7 +214,8 @@ static ssize_t rfkill_state_store(struct device *dev, if (mutex_lock_interruptible(&rfkill->mutex)) return -ERESTARTSYS; error = rfkill_toggle_radio(rfkill, - state ? RFKILL_STATE_ON : RFKILL_STATE_OFF); + state ? RFKILL_STATE_ON : RFKILL_STATE_OFF, + 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -255,7 +256,8 @@ static ssize_t rfkill_claim_store(struct device *dev, if (rfkill->user_claim != claim) { if (!claim) rfkill_toggle_radio(rfkill, - rfkill_states[rfkill->type]); + rfkill_states[rfkill->type], + 0); rfkill->user_claim = claim; } @@ -288,12 +290,11 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) if (dev->power.power_state.event != state.event) { if (state.event & PM_EVENT_SLEEP) { - mutex_lock(&rfkill->mutex); - - if (rfkill->state == RFKILL_STATE_ON) - rfkill->toggle_radio(rfkill->data, - RFKILL_STATE_OFF); + /* Stop transmitter, keep state, no notifies */ + update_rfkill_state(rfkill); + mutex_lock(&rfkill->mutex); + rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); mutex_unlock(&rfkill->mutex); } @@ -310,8 +311,8 @@ static int rfkill_resume(struct device *dev) if (dev->power.power_state.event != PM_EVENT_ON) { mutex_lock(&rfkill->mutex); - if (rfkill->state == RFKILL_STATE_ON) - rfkill->toggle_radio(rfkill->data, RFKILL_STATE_ON); + /* restore radio state AND notify everybody */ + rfkill_toggle_radio(rfkill, rfkill->state, 1); mutex_unlock(&rfkill->mutex); } @@ -338,7 +339,7 @@ static int rfkill_add_switch(struct rfkill *rfkill) mutex_lock(&rfkill_mutex); - error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type]); + error = rfkill_toggle_radio(rfkill, rfkill_states[rfkill->type], 0); if (!error) list_add_tail(&rfkill->node, &rfkill_list); @@ -351,7 +352,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF); + rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); mutex_unlock(&rfkill_mutex); } -- cgit v1.2.3 From 79399a8d1908f6a406e82d23c5a9937e1722ed3a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:03 -0300 Subject: rfkill: add notifier chains support Add a notifier chain for use by the rfkill class. This notifier chain signals the following events (more to be added when needed): 1. rfkill: rfkill device state has changed A pointer to the rfkill struct will be passed as a parameter. The notifier message types have been added to include/linux/rfkill.h instead of to include/linux/notifier.h in order to avoid the madness of modifying a header used globally (and that triggers an almost full tree rebuild every time it is touched) with information that is of interest only to code that includes the rfkill.h header. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index fb566902030..a561e350a70 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -46,6 +46,49 @@ MODULE_PARM_DESC(default_state, static enum rfkill_state rfkill_states[RFKILL_TYPE_MAX]; +static BLOCKING_NOTIFIER_HEAD(rfkill_notifier_list); + + +/** + * register_rfkill_notifier - Add notifier to rfkill notifier chain + * @nb: pointer to the new entry to add to the chain + * + * See blocking_notifier_chain_register() for return value and further + * observations. + * + * Adds a notifier to the rfkill notifier chain. The chain will be + * called with a pointer to the relevant rfkill structure as a parameter, + * refer to include/linux/rfkill.h for the possible events. + * + * Notifiers added to this chain are to always return NOTIFY_DONE. This + * chain is a blocking notifier chain: notifiers can sleep. + * + * Calls to this chain may have been done through a workqueue. One must + * assume unordered asynchronous behaviour, there is no way to know if + * actions related to the event that generated the notification have been + * carried out already. + */ +int register_rfkill_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&rfkill_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(register_rfkill_notifier); + +/** + * unregister_rfkill_notifier - remove notifier from rfkill notifier chain + * @nb: pointer to the entry to remove from the chain + * + * See blocking_notifier_chain_unregister() for return value and further + * observations. + * + * Removes a notifier from the rfkill notifier chain. + */ +int unregister_rfkill_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&rfkill_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(unregister_rfkill_notifier); + static void rfkill_led_trigger(struct rfkill *rfkill, enum rfkill_state state) @@ -62,14 +105,25 @@ static void rfkill_led_trigger(struct rfkill *rfkill, #endif /* CONFIG_RFKILL_LEDS */ } +static void notify_rfkill_state_change(struct rfkill *rfkill) +{ + blocking_notifier_call_chain(&rfkill_notifier_list, + RFKILL_STATE_CHANGED, + rfkill); +} + static void update_rfkill_state(struct rfkill *rfkill) { - enum rfkill_state newstate; + enum rfkill_state newstate, oldstate; if (rfkill->get_state) { mutex_lock(&rfkill->mutex); - if (!rfkill->get_state(rfkill->data, &newstate)) + if (!rfkill->get_state(rfkill->data, &newstate)) { + oldstate = rfkill->state; rfkill->state = newstate; + if (oldstate != newstate) + notify_rfkill_state_change(rfkill); + } mutex_unlock(&rfkill->mutex); } } @@ -93,8 +147,10 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, rfkill->state = state; } - if (force || rfkill->state != oldstate) + if (force || rfkill->state != oldstate) { rfkill_led_trigger(rfkill, rfkill->state); + notify_rfkill_state_change(rfkill); + } return retval; } @@ -139,12 +195,20 @@ EXPORT_SYMBOL(rfkill_switch_all); */ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { + enum rfkill_state oldstate; + if (state != RFKILL_STATE_OFF && state != RFKILL_STATE_ON) return -EINVAL; mutex_lock(&rfkill->mutex); + + oldstate = rfkill->state; rfkill->state = state; + + if (state != oldstate) + notify_rfkill_state_change(rfkill); + mutex_unlock(&rfkill->mutex); return 0; -- cgit v1.2.3 From 99c632e5a304e1f76350eb9e8b2493514de8b60c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:04 -0300 Subject: rfkill: add type string helper We will need access to the rfkill switch type in string format for more than just sysfs. Therefore, move it to a generic helper. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index a561e350a70..3c7773475ea 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -224,34 +224,31 @@ static ssize_t rfkill_name_show(struct device *dev, return sprintf(buf, "%s\n", rfkill->name); } -static ssize_t rfkill_type_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static const char *rfkill_get_type_str(enum rfkill_type type) { - struct rfkill *rfkill = to_rfkill(dev); - const char *type; - - switch (rfkill->type) { + switch (type) { case RFKILL_TYPE_WLAN: - type = "wlan"; - break; + return "wlan"; case RFKILL_TYPE_BLUETOOTH: - type = "bluetooth"; - break; + return "bluetooth"; case RFKILL_TYPE_UWB: - type = "ultrawideband"; - break; + return "ultrawideband"; case RFKILL_TYPE_WIMAX: - type = "wimax"; - break; + return "wimax"; case RFKILL_TYPE_WWAN: - type = "wwan"; - break; + return "wwan"; default: BUG(); } +} + +static ssize_t rfkill_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rfkill *rfkill = to_rfkill(dev); - return sprintf(buf, "%s\n", type); + return sprintf(buf, "%s\n", rfkill_get_type_str(rfkill->type)); } static ssize_t rfkill_state_show(struct device *dev, -- cgit v1.2.3 From ffb67c34e436fb163c4067936ccec797354fa6c6 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:05 -0300 Subject: rfkill: add uevent notifications Use the notification chains to also send uevents, so that userspace can be notified of state changes of every rfkill switch. Userspace should use these events for OSD/status report applications and rfkill GUI frontends. HAL might want to broadcast them over DBUS, for example. It might be also useful for userspace implementations of rfkill-input, or to use HAL as the platform driver which promotes rfkill switch change events into input events (to synchronize all other switches) when necessary for platforms that lack a convenient platform-specific kernel module to do it. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'net') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 3c7773475ea..dd1c3f18f31 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -386,12 +386,51 @@ static int rfkill_resume(struct device *dev) #define rfkill_resume NULL #endif +static int rfkill_blocking_uevent_notifier(struct notifier_block *nb, + unsigned long eventid, + void *data) +{ + struct rfkill *rfkill = (struct rfkill *)data; + + switch (eventid) { + case RFKILL_STATE_CHANGED: + kobject_uevent(&rfkill->dev.kobj, KOBJ_CHANGE); + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block rfkill_blocking_uevent_nb = { + .notifier_call = rfkill_blocking_uevent_notifier, + .priority = 0, +}; + +static int rfkill_dev_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct rfkill *rfkill = to_rfkill(dev); + int error; + + error = add_uevent_var(env, "RFKILL_NAME=%s", rfkill->name); + if (error) + return error; + error = add_uevent_var(env, "RFKILL_TYPE=%s", + rfkill_get_type_str(rfkill->type)); + if (error) + return error; + error = add_uevent_var(env, "RFKILL_STATE=%d", rfkill->state); + return error; +} + static struct class rfkill_class = { .name = "rfkill", .dev_release = rfkill_release, .dev_attrs = rfkill_dev_attrs, .suspend = rfkill_suspend, .resume = rfkill_resume, + .dev_uevent = rfkill_dev_uevent, }; static int rfkill_add_switch(struct rfkill *rfkill) @@ -566,11 +605,14 @@ static int __init rfkill_init(void) return error; } + register_rfkill_notifier(&rfkill_blocking_uevent_nb); + return 0; } static void __exit rfkill_exit(void) { + unregister_rfkill_notifier(&rfkill_blocking_uevent_nb); class_unregister(&rfkill_class); } -- cgit v1.2.3 From fbc6af2f3c46df4722f5161d0ad20dd87cd7dfa9 Mon Sep 17 00:00:00 2001 From: Fabien Crespel Date: Mon, 23 Jun 2008 17:23:06 -0300 Subject: rfkill: drop current_state from tasks in rfkill-input The whole current_state thing seems completely useless and a source of problems in rfkill-input, since state comparison is already done in rfkill, and rfkill-input is more than likely to become out of sync with the real state. Signed-off-by: Fabien Crespel Acked-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 29c13d308b3..d285f9a9d82 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -30,27 +30,15 @@ struct rfkill_task { spinlock_t lock; /* for accessing last and desired state */ unsigned long last; /* last schedule */ enum rfkill_state desired_state; /* on/off */ - enum rfkill_state current_state; /* on/off */ }; static void rfkill_task_handler(struct work_struct *work) { struct rfkill_task *task = container_of(work, struct rfkill_task, work); - enum rfkill_state state; mutex_lock(&task->mutex); - /* - * Use temp variable to fetch desired state to keep it - * consistent even if rfkill_schedule_toggle() runs in - * another thread or interrupts us. - */ - state = task->desired_state; - - if (state != task->current_state) { - rfkill_switch_all(task->type, state); - task->current_state = state; - } + rfkill_switch_all(task->type, task->desired_state); mutex_unlock(&task->mutex); } @@ -94,7 +82,6 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) .mutex = __MUTEX_INITIALIZER(n.mutex), \ .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ .desired_state = RFKILL_STATE_ON, \ - .current_state = RFKILL_STATE_ON, \ } static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); -- cgit v1.2.3 From 4081f00dc45abce6bdac352a6354c07ce15db45b Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:23:07 -0300 Subject: rfkill: do not allow userspace to override ALL RADIOS OFF SW_RFKILL_ALL is the "emergency power-off all radios" input event. It must be handled, and must always do the same thing as far as the rfkill system is concerned: all transmitters are to go *immediately* offline. For safety, do NOT allow userspace to override EV_SW SW_RFKILL_ALL OFF. As long as rfkill-input is loaded, that event will *always* be processed, and it will *always* force all rfkill switches to disable all wireless transmitters, regardless of user_claim attribute or anything else. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Dmitry Torokhov Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 47 ++++++++++++++++++++++++++++++++--------------- net/rfkill/rfkill-input.h | 1 + net/rfkill/rfkill.c | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index d285f9a9d82..5d4c8b2446f 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -43,11 +43,26 @@ static void rfkill_task_handler(struct work_struct *work) mutex_unlock(&task->mutex); } +static void rfkill_task_epo_handler(struct work_struct *work) +{ + rfkill_epo(); +} + +static DECLARE_WORK(epo_work, rfkill_task_epo_handler); + +static void rfkill_schedule_epo(void) +{ + schedule_work(&epo_work); +} + static void rfkill_schedule_set(struct rfkill_task *task, enum rfkill_state desired_state) { unsigned long flags; + if (unlikely(work_pending(&epo_work))) + return; + spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { @@ -63,6 +78,9 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) { unsigned long flags; + if (unlikely(work_pending(&epo_work))) + return; + spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { @@ -114,21 +132,20 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, switch (code) { case SW_RFKILL_ALL: /* EVERY radio type. data != 0 means radios ON */ - rfkill_schedule_set(&rfkill_wwan, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_wimax, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_uwb, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_bt, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); - rfkill_schedule_set(&rfkill_wlan, - (data)? RFKILL_STATE_ON: - RFKILL_STATE_OFF); + /* handle EPO (emergency power off) through shortcut */ + if (data) { + rfkill_schedule_set(&rfkill_wwan, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_wimax, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_uwb, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_bt, + RFKILL_STATE_ON); + rfkill_schedule_set(&rfkill_wlan, + RFKILL_STATE_ON); + } else + rfkill_schedule_epo(); break; default: break; diff --git a/net/rfkill/rfkill-input.h b/net/rfkill/rfkill-input.h index 4dae5006fc7..f63d0504568 100644 --- a/net/rfkill/rfkill-input.h +++ b/net/rfkill/rfkill-input.h @@ -12,5 +12,6 @@ #define __RFKILL_INPUT_H void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); +void rfkill_epo(void); #endif /* __RFKILL_INPUT_H */ diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index dd1c3f18f31..7d07175c407 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -181,6 +181,24 @@ void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state) } EXPORT_SYMBOL(rfkill_switch_all); +/** + * rfkill_epo - emergency power off all transmitters + * + * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring + * everything in its path but rfkill_mutex. + */ +void rfkill_epo(void) +{ + struct rfkill *rfkill; + + mutex_lock(&rfkill_mutex); + list_for_each_entry(rfkill, &rfkill_list, node) { + rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + } + mutex_unlock(&rfkill_mutex); +} +EXPORT_SYMBOL_GPL(rfkill_epo); + /** * rfkill_force_state - Force the internal rfkill radio state * @rfkill: pointer to the rfkill class to modify. -- cgit v1.2.3 From 5005657cbd0fd6f277f807c0612a6b6d4396a02c Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Mon, 23 Jun 2008 17:46:42 -0300 Subject: rfkill: rename the rfkill_state states and add block-locked state The current naming of rfkill_state causes a lot of confusion: not only the "kill" in rfkill suggests negative logic, but also the fact that rfkill cannot turn anything on (it can just force something off or stop forcing something off) is often forgotten. Rename RFKILL_STATE_OFF to RFKILL_STATE_SOFT_BLOCKED (transmitter is blocked and will not operate; state can be changed by a toggle_radio request), and RFKILL_STATE_ON to RFKILL_STATE_UNBLOCKED (transmitter is not blocked, and may operate). Also, add a new third state, RFKILL_STATE_HARD_BLOCKED (transmitter is blocked and will not operate; state cannot be changed through a toggle_radio request), which is used by drivers to indicate a wireless transmiter was blocked by a hardware rfkill line that accepts no overrides. Keep the old names as #defines, but document them as deprecated. This way, drivers can be converted to the new names *and* verified to actually use rfkill correctly one by one. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- net/rfkill/rfkill-input.c | 29 +++++++++--------- net/rfkill/rfkill.c | 75 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/rfkill/rfkill-input.c b/net/rfkill/rfkill-input.c index 5d4c8b2446f..8aa82273014 100644 --- a/net/rfkill/rfkill-input.c +++ b/net/rfkill/rfkill-input.c @@ -84,7 +84,8 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_lock_irqsave(&task->lock, flags); if (time_after(jiffies, task->last + msecs_to_jiffies(200))) { - task->desired_state = !task->desired_state; + task->desired_state = + rfkill_state_complement(task->desired_state); task->last = jiffies; schedule_work(&task->work); } @@ -92,14 +93,14 @@ static void rfkill_schedule_toggle(struct rfkill_task *task) spin_unlock_irqrestore(&task->lock, flags); } -#define DEFINE_RFKILL_TASK(n, t) \ - struct rfkill_task n = { \ - .work = __WORK_INITIALIZER(n.work, \ - rfkill_task_handler), \ - .type = t, \ - .mutex = __MUTEX_INITIALIZER(n.mutex), \ - .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ - .desired_state = RFKILL_STATE_ON, \ +#define DEFINE_RFKILL_TASK(n, t) \ + struct rfkill_task n = { \ + .work = __WORK_INITIALIZER(n.work, \ + rfkill_task_handler), \ + .type = t, \ + .mutex = __MUTEX_INITIALIZER(n.mutex), \ + .lock = __SPIN_LOCK_UNLOCKED(n.lock), \ + .desired_state = RFKILL_STATE_UNBLOCKED, \ } static DEFINE_RFKILL_TASK(rfkill_wlan, RFKILL_TYPE_WLAN); @@ -135,15 +136,15 @@ static void rfkill_event(struct input_handle *handle, unsigned int type, /* handle EPO (emergency power off) through shortcut */ if (data) { rfkill_schedule_set(&rfkill_wwan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wimax, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_uwb, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_bt, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); rfkill_schedule_set(&rfkill_wlan, - RFKILL_STATE_ON); + RFKILL_STATE_UNBLOCKED); } else rfkill_schedule_epo(); break; diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 7d07175c407..ce0e23148cd 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -39,7 +39,7 @@ MODULE_LICENSE("GPL"); static LIST_HEAD(rfkill_list); /* list of registered rf switches */ static DEFINE_MUTEX(rfkill_mutex); -static unsigned int rfkill_default_state = RFKILL_STATE_ON; +static unsigned int rfkill_default_state = RFKILL_STATE_UNBLOCKED; module_param_named(default_state, rfkill_default_state, uint, 0444); MODULE_PARM_DESC(default_state, "Default initial state for all radio types, 0 = radio off"); @@ -98,7 +98,7 @@ static void rfkill_led_trigger(struct rfkill *rfkill, if (!led->name) return; - if (state == RFKILL_STATE_OFF) + if (state != RFKILL_STATE_UNBLOCKED) led_trigger_event(led, LED_OFF); else led_trigger_event(led, LED_FULL); @@ -128,6 +128,28 @@ static void update_rfkill_state(struct rfkill *rfkill) } } +/** + * rfkill_toggle_radio - wrapper for toggle_radio hook + * calls toggle_radio taking into account a lot of "small" + * details. + * @rfkill: the rfkill struct to use + * @force: calls toggle_radio even if cache says it is not needed, + * and also makes sure notifications of the state will be + * sent even if it didn't change + * @state: the new state to call toggle_radio() with + * + * This wrappen protects and enforces the API for toggle_radio + * calls. Note that @force cannot override a (possibly cached) + * state of RFKILL_STATE_HARD_BLOCKED. Any device making use of + * RFKILL_STATE_HARD_BLOCKED implements either get_state() or + * rfkill_force_state(), so the cache either is bypassed or valid. + * + * Note that we do call toggle_radio for RFKILL_STATE_SOFT_BLOCKED + * even if the radio is in RFKILL_STATE_HARD_BLOCKED state, so as to + * give the driver a hint that it should double-BLOCK the transmitter. + * + * Caller must have aquired rfkill_mutex. + */ static int rfkill_toggle_radio(struct rfkill *rfkill, enum rfkill_state state, int force) @@ -141,9 +163,28 @@ static int rfkill_toggle_radio(struct rfkill *rfkill, !rfkill->get_state(rfkill->data, &newstate)) rfkill->state = newstate; + switch (state) { + case RFKILL_STATE_HARD_BLOCKED: + /* typically happens when refreshing hardware state, + * such as on resume */ + state = RFKILL_STATE_SOFT_BLOCKED; + break; + case RFKILL_STATE_UNBLOCKED: + /* force can't override this, only rfkill_force_state() can */ + if (rfkill->state == RFKILL_STATE_HARD_BLOCKED) + return -EPERM; + break; + case RFKILL_STATE_SOFT_BLOCKED: + /* nothing to do, we want to give drivers the hint to double + * BLOCK even a transmitter that is already in state + * RFKILL_STATE_HARD_BLOCKED */ + break; + } + if (force || state != rfkill->state) { retval = rfkill->toggle_radio(rfkill->data, state); - if (!retval) + /* never allow a HARD->SOFT downgrade! */ + if (!retval && rfkill->state != RFKILL_STATE_HARD_BLOCKED) rfkill->state = state; } @@ -184,7 +225,7 @@ EXPORT_SYMBOL(rfkill_switch_all); /** * rfkill_epo - emergency power off all transmitters * - * This kicks all rfkill devices to RFKILL_STATE_OFF, ignoring + * This kicks all rfkill devices to RFKILL_STATE_SOFT_BLOCKED, ignoring * everything in its path but rfkill_mutex. */ void rfkill_epo(void) @@ -193,7 +234,7 @@ void rfkill_epo(void) mutex_lock(&rfkill_mutex); list_for_each_entry(rfkill, &rfkill_list, node) { - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); } mutex_unlock(&rfkill_mutex); } @@ -215,8 +256,9 @@ int rfkill_force_state(struct rfkill *rfkill, enum rfkill_state state) { enum rfkill_state oldstate; - if (state != RFKILL_STATE_OFF && - state != RFKILL_STATE_ON) + if (state != RFKILL_STATE_SOFT_BLOCKED && + state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_HARD_BLOCKED) return -EINVAL; mutex_lock(&rfkill->mutex); @@ -290,11 +332,14 @@ static ssize_t rfkill_state_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (state != RFKILL_STATE_UNBLOCKED && + state != RFKILL_STATE_SOFT_BLOCKED) + return -EINVAL; + if (mutex_lock_interruptible(&rfkill->mutex)) return -ERESTARTSYS; - error = rfkill_toggle_radio(rfkill, - state ? RFKILL_STATE_ON : RFKILL_STATE_OFF, - 0); + error = rfkill_toggle_radio(rfkill, state, 0); mutex_unlock(&rfkill->mutex); return error ? error : count; @@ -373,7 +418,8 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) update_rfkill_state(rfkill); mutex_lock(&rfkill->mutex); - rfkill->toggle_radio(rfkill->data, RFKILL_STATE_OFF); + rfkill->toggle_radio(rfkill->data, + RFKILL_STATE_SOFT_BLOCKED); mutex_unlock(&rfkill->mutex); } @@ -470,7 +516,7 @@ static void rfkill_remove_switch(struct rfkill *rfkill) { mutex_lock(&rfkill_mutex); list_del_init(&rfkill->node); - rfkill_toggle_radio(rfkill, RFKILL_STATE_OFF, 1); + rfkill_toggle_radio(rfkill, RFKILL_STATE_SOFT_BLOCKED, 1); mutex_unlock(&rfkill_mutex); } @@ -610,8 +656,9 @@ static int __init rfkill_init(void) int error; int i; - if (rfkill_default_state != RFKILL_STATE_OFF && - rfkill_default_state != RFKILL_STATE_ON) + /* RFKILL_STATE_HARD_BLOCKED is illegal here... */ + if (rfkill_default_state != RFKILL_STATE_SOFT_BLOCKED && + rfkill_default_state != RFKILL_STATE_UNBLOCKED) return -EINVAL; for (i = 0; i < ARRAY_SIZE(rfkill_states); i++) -- cgit v1.2.3 From f2df38596a81b6c24f4586b0b4befeaebf3e02db Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Sun, 15 Jun 2008 18:23:29 +0300 Subject: mac80211: 11h Infrastructure - Parsing This patch introduces parsing of 11h and 11d related elements from incoming management frames. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 9 +++++++++ net/mac80211/mlme.c | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'net') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 80a9e7c07b4..af352c05c98 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -791,6 +791,10 @@ struct ieee802_11_elems { u8 *preq; u8 *prep; u8 *perr; + u8 *ch_switch_elem; + u8 *country_elem; + u8 *pwr_constr_elem; + u8 *quiet_elem; /* first quite element */ /* length of them, respectively */ u8 ssid_len; @@ -815,6 +819,11 @@ struct ieee802_11_elems { u8 preq_len; u8 prep_len; u8 perr_len; + u8 ch_switch_elem_len; + u8 country_elem_len; + u8 pwr_constr_elem_len; + u8 quiet_elem_len; + u8 num_of_quiet_elem; /* can be more the one */ }; static inline struct ieee80211_local *hw_to_local( diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index e06d6450f21..32453561fe3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -204,6 +204,25 @@ void ieee802_11_parse_elems(u8 *start, size_t len, elems->perr = pos; elems->perr_len = elen; break; + case WLAN_EID_CHANNEL_SWITCH: + elems->ch_switch_elem = pos; + elems->ch_switch_elem_len = elen; + break; + case WLAN_EID_QUIET: + if (!elems->quiet_elem) { + elems->quiet_elem = pos; + elems->quiet_elem_len = elen; + } + elems->num_of_quiet_elem++; + break; + case WLAN_EID_COUNTRY: + elems->country_elem = pos; + elems->country_elem_len = elen; + break; + case WLAN_EID_PWR_CONSTRAINT: + elems->pwr_constr_elem = pos; + elems->pwr_constr_elem_len = elen; + break; default: break; } -- cgit v1.2.3 From b662348662f9661f9259c7186c1bdb65620045f1 Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Mon, 16 Jun 2008 16:09:49 +0300 Subject: mac80211: 11h - Handling measurement request This patch handles the 11h measurement request information element. This is minimal requested implementation - refuse measurement. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 32453561fe3..6d9a4facf9d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1720,6 +1720,71 @@ void ieee80211_sta_tear_down_BA_sessions(struct net_device *dev, u8 *addr) } } +static void ieee80211_send_refuse_measurement_request(struct net_device *dev, + struct ieee80211_msrment_ie *request_ie, + const u8 *da, const u8 *bssid, + u8 dialog_token) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sk_buff *skb; + struct ieee80211_mgmt *msr_report; + + skb = dev_alloc_skb(sizeof(*msr_report) + local->hw.extra_tx_headroom + + sizeof(struct ieee80211_msrment_ie)); + + if (!skb) { + printk(KERN_ERR "%s: failed to allocate buffer for " + "measurement report frame\n", dev->name); + return; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + msr_report = (struct ieee80211_mgmt *)skb_put(skb, 24); + memset(msr_report, 0, 24); + memcpy(msr_report->da, da, ETH_ALEN); + memcpy(msr_report->sa, dev->dev_addr, ETH_ALEN); + memcpy(msr_report->bssid, bssid, ETH_ALEN); + msr_report->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, + IEEE80211_STYPE_ACTION); + + skb_put(skb, 1 + sizeof(msr_report->u.action.u.measurement)); + msr_report->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; + msr_report->u.action.u.measurement.action_code = + WLAN_ACTION_SPCT_MSR_RPRT; + msr_report->u.action.u.measurement.dialog_token = dialog_token; + + msr_report->u.action.u.measurement.element_id = WLAN_EID_MEASURE_REPORT; + msr_report->u.action.u.measurement.length = + sizeof(struct ieee80211_msrment_ie); + + memset(&msr_report->u.action.u.measurement.msr_elem, 0, + sizeof(struct ieee80211_msrment_ie)); + msr_report->u.action.u.measurement.msr_elem.token = request_ie->token; + msr_report->u.action.u.measurement.msr_elem.mode |= + IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED; + msr_report->u.action.u.measurement.msr_elem.type = request_ie->type; + + ieee80211_sta_tx(dev, skb, 0); +} + +static void ieee80211_sta_process_measurement_req(struct net_device *dev, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + /* + * Ignoring measurement request is spec violation. + * Mandatory measurements must be reported optional + * measurements might be refused or reported incapable + * For now just refuse + * TODO: Answer basic measurement as unmeasured + */ + ieee80211_send_refuse_measurement_request(dev, + &mgmt->u.action.u.measurement.msr_elem, + mgmt->sa, mgmt->bssid, + mgmt->u.action.u.measurement.dialog_token); +} + + static void ieee80211_rx_mgmt_auth(struct net_device *dev, struct ieee80211_if_sta *ifsta, struct ieee80211_mgmt *mgmt, @@ -3044,11 +3109,24 @@ static void ieee80211_rx_mgmt_action(struct net_device *dev, struct ieee80211_rx_status *rx_status) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); if (len < IEEE80211_MIN_ACTION_SIZE) return; switch (mgmt->u.action.category) { + case WLAN_CATEGORY_SPECTRUM_MGMT: + if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ) + break; + switch (mgmt->u.action.u.chan_switch.action_code) { + case WLAN_ACTION_SPCT_MSR_REQ: + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.measurement))) + break; + ieee80211_sta_process_measurement_req(dev, mgmt, len); + break; + } + break; case WLAN_CATEGORY_BACK: switch (mgmt->u.action.u.addba_req.action_code) { case WLAN_ACTION_ADDBA_REQ: -- cgit v1.2.3 From 135a2110c55c71d7ccaf5ac66968b993347fe8e2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 16 Jun 2008 20:55:29 +0200 Subject: mac80211: remove shared key todo Adding shared key authentication is not going to happen anyway. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6d9a4facf9d..75c4e9743fb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1837,11 +1837,12 @@ static void ieee80211_rx_mgmt_auth(struct net_device *dev, auth_transaction, status_code); if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) { - /* IEEE 802.11 standard does not require authentication in IBSS + /* + * IEEE 802.11 standard does not require authentication in IBSS * networks and most implementations do not seem to use it. * However, try to reply to authentication attempts if someone * has actually implemented this. - * TODO: Could implement shared key authentication. */ + */ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) { printk(KERN_DEBUG "%s: unexpected IBSS authentication " "frame (alg=%d transaction=%d)\n", -- cgit v1.2.3 From 5a9f7b047e81a73a1ce3e42ef87c28a61fd4df24 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jun 2008 14:58:09 +0200 Subject: mac80211: use separate spinlock for sta flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit David Ellingsworth posted a bug that was only noticable on UP/NO-PREEMPT and Michael correctly analysed it to be a spin_lock_bh() section within a spin_lock_irqsave() section. This adds a separate spinlock for the sta_info flags to fix that issue and avoid having to take much care about where the sta flag manipulation functions are called. Signed-off-by: Johannes Berg Reported-By: David Ellingsworth Signed-off-by: John W. Linville --- net/mac80211/sta_info.c | 1 + net/mac80211/sta_info.h | 40 +++++++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index c24770cb02c..b3c733162fc 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -235,6 +235,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return NULL; spin_lock_init(&sta->lock); + spin_lock_init(&sta->flaglock); memcpy(sta->addr, addr, ETH_ALEN); sta->local = local; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 95753f860ac..fd228c198e3 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -164,6 +164,7 @@ struct sta_ampdu_mlme { * @aid: STA's unique AID (1..2007, 0 = not assigned yet), * only used in AP (and IBSS?) mode * @flags: STA flags, see &enum ieee80211_sta_info_flags + * @flaglock: spinlock for flags accesses * @ps_tx_buf: buffer of frames to transmit to this station * when it leaves power saving state * @tx_filtered: buffer of frames we already tried to transmit @@ -186,6 +187,7 @@ struct sta_info { struct rate_control_ref *rate_ctrl; void *rate_ctrl_priv; spinlock_t lock; + spinlock_t flaglock; struct ieee80211_ht_info ht_info; u64 supp_rates[IEEE80211_NUM_BANDS]; u8 addr[ETH_ALEN]; @@ -198,7 +200,10 @@ struct sta_info { */ u8 pin_status; - /* frequently updated information, locked with lock spinlock */ + /* + * frequently updated, locked with own spinlock (flaglock), + * use the accessors defined below + */ u32 flags; /* @@ -293,34 +298,41 @@ static inline enum plink_state sta_plink_state(struct sta_info *sta) static inline void set_sta_flags(struct sta_info *sta, const u32 flags) { - spin_lock_bh(&sta->lock); + unsigned long irqfl; + + spin_lock_irqsave(&sta->flaglock, irqfl); sta->flags |= flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); } static inline void clear_sta_flags(struct sta_info *sta, const u32 flags) { - spin_lock_bh(&sta->lock); + unsigned long irqfl; + + spin_lock_irqsave(&sta->flaglock, irqfl); sta->flags &= ~flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); } static inline void set_and_clear_sta_flags(struct sta_info *sta, const u32 set, const u32 clear) { - spin_lock_bh(&sta->lock); + unsigned long irqfl; + + spin_lock_irqsave(&sta->flaglock, irqfl); sta->flags |= set; sta->flags &= ~clear; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); } static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags) { u32 ret; + unsigned long irqfl; - spin_lock_bh(&sta->lock); + spin_lock_irqsave(&sta->flaglock, irqfl); ret = sta->flags & flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); return ret; } @@ -329,11 +341,12 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta, const u32 flags) { u32 ret; + unsigned long irqfl; - spin_lock_bh(&sta->lock); + spin_lock_irqsave(&sta->flaglock, irqfl); ret = sta->flags & flags; sta->flags &= ~flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); return ret; } @@ -341,10 +354,11 @@ static inline u32 test_and_clear_sta_flags(struct sta_info *sta, static inline u32 get_sta_flags(struct sta_info *sta) { u32 ret; + unsigned long irqfl; - spin_lock_bh(&sta->lock); + spin_lock_irqsave(&sta->flaglock, irqfl); ret = sta->flags; - spin_unlock_bh(&sta->lock); + spin_unlock_irqrestore(&sta->flaglock, irqfl); return ret; } -- cgit v1.2.3 From 97b045d62bffae5a91a286b56ac51db0c4385687 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Jun 2008 01:22:30 +0200 Subject: mac80211: add single function calling tx handlers This modifies mac80211 to only have a single function calling the TX handlers rather than them being invoked in multiple places. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index ce06e791bf4..7a14a39ebd7 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1083,13 +1083,46 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb, return IEEE80211_TX_OK; } +/* + * Invoke TX handlers, return 0 on success and non-zero if the + * frame was dropped or queued. + */ +static int invoke_tx_handlers(struct ieee80211_tx_data *tx) +{ + struct ieee80211_local *local = tx->local; + struct sk_buff *skb = tx->skb; + ieee80211_tx_handler *handler; + ieee80211_tx_result res = TX_DROP; + int i; + + for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) { + res = (*handler)(tx); + if (res != TX_CONTINUE) + break; + } + + if (unlikely(res == TX_DROP)) { + I802_DEBUG_INC(local->tx_handlers_drop); + dev_kfree_skb(skb); + for (i = 0; i < tx->num_extra_frag; i++) + if (tx->extra_frag[i]) + dev_kfree_skb(tx->extra_frag[i]); + kfree(tx->extra_frag); + return -1; + } else if (unlikely(res == TX_QUEUED)) { + I802_DEBUG_INC(local->tx_handlers_queued); + return -1; + } + + return 0; +} + static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct sta_info *sta; - ieee80211_tx_handler *handler; struct ieee80211_tx_data tx; - ieee80211_tx_result res = TX_DROP, res_prepare; + ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); int ret, i; u16 queue; @@ -1118,26 +1151,8 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) tx.channel = local->hw.conf.channel; info->band = tx.channel->band; - for (handler = ieee80211_tx_handlers; *handler != NULL; - handler++) { - res = (*handler)(&tx); - if (res != TX_CONTINUE) - break; - } - - if (WARN_ON(tx.skb != skb)) - goto drop; - - if (unlikely(res == TX_DROP)) { - I802_DEBUG_INC(local->tx_handlers_drop); - goto drop; - } - - if (unlikely(res == TX_QUEUED)) { - I802_DEBUG_INC(local->tx_handlers_queued); - rcu_read_unlock(); - return 0; - } + if (invoke_tx_handlers(&tx)) + goto out; if (tx.extra_frag) { for (i = 0; i < tx.num_extra_frag; i++) { @@ -1198,6 +1213,7 @@ retry: store->last_frag_rate_ctrl_probe = !!(tx.flags & IEEE80211_TX_PROBE_LAST_FRAG); } + out: rcu_read_unlock(); return 0; @@ -1948,9 +1964,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, struct ieee80211_local *local = hw_to_local(hw); struct sk_buff *skb = NULL; struct sta_info *sta; - ieee80211_tx_handler *handler; struct ieee80211_tx_data tx; - ieee80211_tx_result res = TX_DROP; struct net_device *bdev; struct ieee80211_sub_if_data *sdata; struct ieee80211_if_ap *bss = NULL; @@ -2001,25 +2015,9 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, tx.channel = local->hw.conf.channel; info->band = tx.channel->band; - for (handler = ieee80211_tx_handlers; *handler != NULL; handler++) { - res = (*handler)(&tx); - if (res == TX_DROP || res == TX_QUEUED) - break; - } - - if (WARN_ON(tx.skb != skb)) - res = TX_DROP; - - if (res == TX_DROP) { - I802_DEBUG_INC(local->tx_handlers_drop); - dev_kfree_skb(skb); - skb = NULL; - } else if (res == TX_QUEUED) { - I802_DEBUG_INC(local->tx_handlers_queued); + if (invoke_tx_handlers(&tx)) skb = NULL; - } - -out: + out: rcu_read_unlock(); return skb; -- cgit v1.2.3 From 9ae705cfd390f9077eec856ea4dff374d166de33 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Jun 2008 11:54:24 +0200 Subject: mac80211: rename TKIP debugging Kconfig symbol ... to MAC80211_TKIP_DEBUG rather than TKIP_DEBUG. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/Kconfig | 2 +- net/mac80211/tkip.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 590e00b2766..0d3661d9b6a 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -150,7 +150,7 @@ config MAC80211_LOWTX_FRAME_DUMP If unsure, say N and insert the debugging code you require into the driver you are debugging. -config TKIP_DEBUG +config MAC80211_TKIP_DEBUG bool "TKIP debugging" depends on MAC80211_DEBUG diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index e710243d82e..69980788998 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -167,7 +167,7 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; ctx = &key->u.tkip.tx; -#ifdef CONFIG_TKIP_DEBUG +#ifdef CONFIG_MAC80211_TKIP_DEBUG printk(KERN_DEBUG "TKIP encrypt: iv16 = 0x%04x, iv32 = 0x%08x\n", iv16, iv32); @@ -177,7 +177,7 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, printk(KERN_DEBUG "Wrap around of iv16 in the middle of a " "fragmented packet\n"); } -#endif /* CONFIG_TKIP_DEBUG */ +#endif /* Update the p1k only when the iv16 in the packet wraps around, this * might occur after the wrap around of iv16 in the key in case of @@ -240,7 +240,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, keyid = pos[3]; iv32 = get_unaligned_le32(pos + 4); pos += 8; -#ifdef CONFIG_TKIP_DEBUG +#ifdef CONFIG_MAC80211_TKIP_DEBUG { int i; printk(KERN_DEBUG "TKIP decrypt: data(len=%zd)", payload_len); @@ -250,7 +250,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, printk(KERN_DEBUG "TKIP decrypt: iv16=%04x iv32=%08x\n", iv16, iv32); } -#endif /* CONFIG_TKIP_DEBUG */ +#endif if (!(keyid & (1 << 5))) return TKIP_DECRYPT_NO_EXT_IV; @@ -262,14 +262,14 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, (iv32 < key->u.tkip.rx[queue].iv32 || (iv32 == key->u.tkip.rx[queue].iv32 && iv16 <= key->u.tkip.rx[queue].iv16))) { -#ifdef CONFIG_TKIP_DEBUG +#ifdef CONFIG_MAC80211_TKIP_DEBUG DECLARE_MAC_BUF(mac); printk(KERN_DEBUG "TKIP replay detected for RX frame from " "%s (RX IV (%04x,%02x) <= prev. IV (%04x,%02x)\n", print_mac(mac, ta), iv32, iv16, key->u.tkip.rx[queue].iv32, key->u.tkip.rx[queue].iv16); -#endif /* CONFIG_TKIP_DEBUG */ +#endif return TKIP_DECRYPT_REPLAY; } @@ -283,7 +283,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, key->u.tkip.rx[queue].iv32 != iv32) { /* IV16 wrapped around - perform TKIP phase 1 */ tkip_mixing_phase1(tk, &key->u.tkip.rx[queue], ta, iv32); -#ifdef CONFIG_TKIP_DEBUG +#ifdef CONFIG_MAC80211_TKIP_DEBUG { int i; DECLARE_MAC_BUF(mac); @@ -299,7 +299,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, printk("%04x ", key->u.tkip.rx[queue].p1k[i]); printk("\n"); } -#endif /* CONFIG_TKIP_DEBUG */ +#endif if (key->local->ops->update_tkip_key && key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) { u8 bcast[ETH_ALEN] = @@ -316,7 +316,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, } tkip_mixing_phase2(tk, &key->u.tkip.rx[queue], iv16, rc4key); -#ifdef CONFIG_TKIP_DEBUG +#ifdef CONFIG_MAC80211_TKIP_DEBUG { int i; printk(KERN_DEBUG "TKIP decrypt: Phase2 rc4key="); @@ -324,7 +324,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, printk("%02x ", rc4key[i]); printk("\n"); } -#endif /* CONFIG_TKIP_DEBUG */ +#endif res = ieee80211_wep_decrypt_data(tfm, rc4key, 16, pos, payload_len - 12); done: -- cgit v1.2.3 From ffd7891dc909b3648e87f7cf8f84a6dc12fc1cc6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 21 Jun 2008 10:02:46 -0400 Subject: mac80211: Let drivers have access to TKIP key offets for TX and RX MIC Some drivers may want to to use the TKIP key offsets for TX and RX MIC so lets move this out. Lets also clear up a bit how this is used internally in mac80211. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/key.h | 37 ++++++++++++------------------------- net/mac80211/tkip.c | 10 +++++----- net/mac80211/wpa.c | 20 ++++++++++++++------ 3 files changed, 31 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/net/mac80211/key.h b/net/mac80211/key.h index a0f774aafa4..425816e0996 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -16,31 +16,18 @@ #include #include -/* ALG_TKIP - * struct ieee80211_key::key is encoded as a 256-bit (32 byte) data block: - * Temporal Encryption Key (128 bits) - * Temporal Authenticator Tx MIC Key (64 bits) - * Temporal Authenticator Rx MIC Key (64 bits) - */ - -#define WEP_IV_LEN 4 -#define WEP_ICV_LEN 4 - -#define ALG_TKIP_KEY_LEN 32 -/* Starting offsets for each key */ -#define ALG_TKIP_TEMP_ENCR_KEY 0 -#define ALG_TKIP_TEMP_AUTH_TX_MIC_KEY 16 -#define ALG_TKIP_TEMP_AUTH_RX_MIC_KEY 24 -#define TKIP_IV_LEN 8 -#define TKIP_ICV_LEN 4 - -#define ALG_CCMP_KEY_LEN 16 -#define CCMP_HDR_LEN 8 -#define CCMP_MIC_LEN 8 -#define CCMP_TK_LEN 16 -#define CCMP_PN_LEN 6 - -#define NUM_RX_DATA_QUEUES 17 +#define WEP_IV_LEN 4 +#define WEP_ICV_LEN 4 +#define ALG_TKIP_KEY_LEN 32 +#define ALG_CCMP_KEY_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 +#define TKIP_IV_LEN 8 +#define TKIP_ICV_LEN 4 + +#define NUM_RX_DATA_QUEUES 17 struct ieee80211_local; struct ieee80211_sub_if_data; diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c index 69980788998..995f7af3d25 100644 --- a/net/mac80211/tkip.c +++ b/net/mac80211/tkip.c @@ -164,7 +164,7 @@ void ieee80211_get_tkip_key(struct ieee80211_key_conf *keyconf, iv16 = data[2] | (data[0] << 8); iv32 = get_unaligned_le32(&data[4]); - tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; ctx = &key->u.tkip.tx; #ifdef CONFIG_MAC80211_TKIP_DEBUG @@ -205,7 +205,7 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, { u8 rc4key[16]; struct tkip_ctx *ctx = &key->u.tkip.tx; - const u8 *tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; /* Calculate per-packet key */ if (ctx->iv16 == 0 || !ctx->initialized) @@ -231,7 +231,7 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, u32 iv16; u8 rc4key[16], keyid, *pos = payload; int res; - const u8 *tk = &key->conf.key[ALG_TKIP_TEMP_ENCR_KEY]; + const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY]; if (payload_len < 12) return -1; @@ -286,13 +286,13 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm, #ifdef CONFIG_MAC80211_TKIP_DEBUG { int i; + u8 key_offset = NL80211_TKIP_DATA_OFFSET_ENCR_KEY; DECLARE_MAC_BUF(mac); printk(KERN_DEBUG "TKIP decrypt: Phase1 TA=%s" " TK=", print_mac(mac, ta)); for (i = 0; i < 16; i++) printk("%02x ", - key->conf.key[ - ALG_TKIP_TEMP_ENCR_KEY + i]); + key->conf.key[key_offset + i]); printk("\n"); printk(KERN_DEBUG "TKIP decrypt: P1K="); for (i = 0; i < 5; i++) diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 345e10e9b31..f809761fbfb 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -49,7 +49,7 @@ static int ieee80211_get_hdr_info(const struct sk_buff *skb, u8 **sa, u8 **da, ieee80211_tx_result ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) { - u8 *data, *sa, *da, *key, *mic, qos_tid; + u8 *data, *sa, *da, *key, *mic, qos_tid, key_offset; size_t data_len; u16 fc; struct sk_buff *skb = tx->skb; @@ -88,8 +88,12 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) #else authenticator = 1; #endif - key = &tx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_TX_MIC_KEY : - ALG_TKIP_TEMP_AUTH_RX_MIC_KEY]; + /* At this point we know we're using ALG_TKIP. To get the MIC key + * we now will rely on the offset from the ieee80211_key_conf::key */ + key_offset = authenticator ? + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY : + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + key = &tx->key->conf.key[key_offset]; mic = skb_put(skb, MICHAEL_MIC_LEN); michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); @@ -100,7 +104,7 @@ ieee80211_tx_h_michael_mic_add(struct ieee80211_tx_data *tx) ieee80211_rx_result ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) { - u8 *data, *sa, *da, *key = NULL, qos_tid; + u8 *data, *sa, *da, *key = NULL, qos_tid, key_offset; size_t data_len; u16 fc; u8 mic[MICHAEL_MIC_LEN]; @@ -131,8 +135,12 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx) #else authenticator = 1; #endif - key = &rx->key->conf.key[authenticator ? ALG_TKIP_TEMP_AUTH_RX_MIC_KEY : - ALG_TKIP_TEMP_AUTH_TX_MIC_KEY]; + /* At this point we know we're using ALG_TKIP. To get the MIC key + * we now will rely on the offset from the ieee80211_key_conf::key */ + key_offset = authenticator ? + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY : + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key = &rx->key->conf.key[key_offset]; michael_mic(key, da, sa, qos_tid & 0x0f, data, data_len, mic); if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0 || wpa_test) { if (!(rx->flags & IEEE80211_RX_RA_MATCH)) -- cgit v1.2.3 From 70217d7f83a12e0a16c726951ddf8f7f0ba7d1a4 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 22 Jun 2008 16:45:23 -0700 Subject: mac80211: wep.c use new frame control helpers Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- net/mac80211/wep.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index e7b6344c900..35b664d00e2 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -84,20 +84,17 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_key *key) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 fc; - int hdrlen; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; u8 *newhdr; - fc = le16_to_cpu(hdr->frame_control); - fc |= IEEE80211_FCTL_PROTECTED; - hdr->frame_control = cpu_to_le16(fc); + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); if (WARN_ON(skb_tailroom(skb) < WEP_ICV_LEN || skb_headroom(skb) < WEP_IV_LEN)) return NULL; - hdrlen = ieee80211_get_hdrlen(fc); + hdrlen = ieee80211_hdrlen(hdr->frame_control); newhdr = skb_push(skb, WEP_IV_LEN); memmove(newhdr, newhdr + WEP_IV_LEN, hdrlen); ieee80211_wep_get_iv(local, key, newhdr + hdrlen); @@ -109,12 +106,10 @@ static void ieee80211_wep_remove_iv(struct ieee80211_local *local, struct sk_buff *skb, struct ieee80211_key *key) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 fc; - int hdrlen; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; - fc = le16_to_cpu(hdr->frame_control); - hdrlen = ieee80211_get_hdrlen(fc); + hdrlen = ieee80211_hdrlen(hdr->frame_control); memmove(skb->data + WEP_IV_LEN, skb->data, hdrlen); skb_pull(skb, WEP_IV_LEN); } @@ -224,17 +219,15 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, u32 klen; u8 *rc4key; u8 keyidx; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 fc; - int hdrlen; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; size_t len; int ret = 0; - fc = le16_to_cpu(hdr->frame_control); - if (!(fc & IEEE80211_FCTL_PROTECTED)) + if (!ieee80211_has_protected(hdr->frame_control)) return -1; - hdrlen = ieee80211_get_hdrlen(fc); + hdrlen = ieee80211_hdrlen(hdr->frame_control); if (skb->len < 8 + hdrlen) return -1; @@ -281,17 +274,15 @@ int ieee80211_wep_decrypt(struct ieee80211_local *local, struct sk_buff *skb, u8 * ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 fc; - int hdrlen; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; u8 *ivpos; u32 iv; - fc = le16_to_cpu(hdr->frame_control); - if (!(fc & IEEE80211_FCTL_PROTECTED)) + if (!ieee80211_has_protected(hdr->frame_control)) return NULL; - hdrlen = ieee80211_get_hdrlen(fc); + hdrlen = ieee80211_hdrlen(hdr->frame_control); ivpos = skb->data + hdrlen; iv = (ivpos[0] << 16) | (ivpos[1] << 8) | ivpos[2]; -- cgit v1.2.3 From 065e9605f941b8bc4dbfa1f14ba98eb0da7e3fbe Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 22 Jun 2008 16:45:27 -0700 Subject: mac80211: tx.c use new frame control helpers Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- net/mac80211/tx.c | 54 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7a14a39ebd7..bf3600a0477 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -52,9 +52,8 @@ static inline void ieee80211_include_sequence(struct ieee80211_sub_if_data *sdat static void ieee80211_dump_frame(const char *ifname, const char *title, const struct sk_buff *skb) { - const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 fc; - int hdrlen; + const struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + unsigned int hdrlen; DECLARE_MAC_BUF(mac); printk(KERN_DEBUG "%s: %s (len=%d)", ifname, title, skb->len); @@ -63,13 +62,12 @@ static void ieee80211_dump_frame(const char *ifname, const char *title, return; } - fc = le16_to_cpu(hdr->frame_control); - hdrlen = ieee80211_get_hdrlen(fc); + hdrlen = ieee80211_hdrlen(hdr->frame_control); if (hdrlen > skb->len) hdrlen = skb->len; if (hdrlen >= 4) printk(" FC=0x%04x DUR=0x%04x", - fc, le16_to_cpu(hdr->duration_id)); + le16_to_cpu(hdr->frame_control), le16_to_cpu(hdr->duration_id)); if (hdrlen >= 10) printk(" A1=%s", print_mac(mac, hdr->addr1)); if (hdrlen >= 16) @@ -281,7 +279,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - if (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)) >= 24) + if (ieee80211_hdrlen(hdr->frame_control) >= 24) ieee80211_include_sequence(tx->sdata, hdr); return TX_CONTINUE; @@ -542,8 +540,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) static ieee80211_tx_result ieee80211_tx_h_misc(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; - u16 fc = le16_to_cpu(hdr->frame_control); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; u16 dur; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_supported_band *sband; @@ -595,7 +592,7 @@ ieee80211_tx_h_misc(struct ieee80211_tx_data *tx) /* Transmit data frames using short preambles if the driver supports * short preambles at the selected rate and short preambles are * available on the network at the current point in time. */ - if (((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) && + if (ieee80211_is_data(hdr->frame_control) && (sband->bitrates[tx->rate_idx].flags & IEEE80211_RATE_SHORT_PREAMBLE) && tx->sdata->bss_conf.use_short_preamble && (!tx->sta || test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))) { @@ -647,7 +644,7 @@ ieee80211_tx_h_misc(struct ieee80211_tx_data *tx) static ieee80211_tx_result ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; size_t hdrlen, per_fragm, num_fragm, payload_len, left; struct sk_buff **frags, *first, *frag; int i; @@ -670,7 +667,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) first = tx->skb; - hdrlen = ieee80211_get_hdrlen(tx->fc); + hdrlen = ieee80211_hdrlen(hdr->frame_control); payload_len = first->len - hdrlen; per_fragm = frag_threshold - hdrlen - FCS_LEN; num_fragm = DIV_ROUND_UP(payload_len, per_fragm); @@ -1395,7 +1392,8 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata; int ret = 1, head_need; - u16 ethertype, hdrlen, meshhdrlen = 0, fc; + u16 ethertype, hdrlen, meshhdrlen = 0; + __le16 fc; struct ieee80211_hdr hdr; struct ieee80211s_hdr mesh_hdr; const u8 *encaps_data; @@ -1418,12 +1416,12 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ ethertype = (skb->data[12] << 8) | skb->data[13]; - fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); switch (sdata->vif.type) { case IEEE80211_IF_TYPE_AP: case IEEE80211_IF_TYPE_VLAN: - fc |= IEEE80211_FCTL_FROMDS; + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); /* DA BSSID SA */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); @@ -1431,7 +1429,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, hdrlen = 24; break; case IEEE80211_IF_TYPE_WDS: - fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr.addr1, sdata->u.wds.remote_addr, ETH_ALEN); memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN); @@ -1441,7 +1439,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, break; #ifdef CONFIG_MAC80211_MESH case IEEE80211_IF_TYPE_MESH_POINT: - fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; + fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ if (is_multicast_ether_addr(skb->data)) memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -1471,7 +1469,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, break; #endif case IEEE80211_IF_TYPE_STA: - fc |= IEEE80211_FCTL_TODS; + fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); @@ -1506,7 +1504,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, /* receiver and we are QoS enabled, use a QoS type frame */ if (sta_flags & WLAN_STA_WME && ieee80211_num_regular_queues(&local->hw) >= 4) { - fc |= IEEE80211_STYPE_QOS_DATA; + fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } @@ -1534,7 +1532,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } - hdr.frame_control = cpu_to_le16(fc); + hdr.frame_control = fc; hdr.duration_id = 0; hdr.seq_ctrl = 0; @@ -1603,7 +1601,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, h_pos += meshhdrlen; } - if (fc & IEEE80211_STYPE_QOS_DATA) { + if (ieee80211_is_data_qos(fc)) { __le16 *qos_control; qos_control = (__le16*) skb_push(skb, 2); @@ -1861,8 +1859,8 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + sizeof(mgmt->u.beacon)); memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); - mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT, - IEEE80211_STYPE_BEACON); + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); memset(mgmt->da, 0xff, ETH_ALEN); memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN); /* BSSID is left zeroed, wildcard value */ @@ -1930,10 +1928,9 @@ void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_rts *rts) { const struct ieee80211_hdr *hdr = frame; - u16 fctl; - fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS; - rts->frame_control = cpu_to_le16(fctl); + rts->frame_control = + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_RTS); rts->duration = ieee80211_rts_duration(hw, vif, frame_len, frame_txctl); memcpy(rts->ra, hdr->addr1, sizeof(rts->ra)); @@ -1947,10 +1944,9 @@ void ieee80211_ctstoself_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_cts *cts) { const struct ieee80211_hdr *hdr = frame; - u16 fctl; - fctl = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS; - cts->frame_control = cpu_to_le16(fctl); + cts->frame_control = + cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS); cts->duration = ieee80211_ctstoself_duration(hw, vif, frame_len, frame_txctl); memcpy(cts->ra, hdr->addr1, sizeof(cts->ra)); -- cgit v1.2.3 From 182503abf450d39417c2cc6a2c49b4731117d21b Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 22 Jun 2008 16:45:29 -0700 Subject: mac80211: rx.c use new frame control helpers Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- net/mac80211/rx.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index c32a0bcd53b..8962d1355f0 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -61,7 +61,7 @@ static inline int should_drop_frame(struct ieee80211_rx_status *status, int present_fcs_len, int radiotap_len) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC)) return 1; @@ -2123,7 +2123,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, struct tid_ampdu_rx *tid_agg_rx; u16 sc; u16 mpdu_seq_num; - u8 ret = 0, *qc; + u8 ret = 0; int tid; sta = sta_info_get(local, hdr->addr2); @@ -2135,8 +2135,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local, if (!ieee80211_is_data_qos(hdr->frame_control)) goto end_reorder; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & QOS_CONTROL_TID_MASK; + tid = *ieee80211_get_qos_ctl(hdr) & QOS_CONTROL_TID_MASK; if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL) goto end_reorder; -- cgit v1.2.3 From 4e3996fe899651e00d3085110cc6e92f6a78ee3e Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Sun, 22 Jun 2008 16:45:32 -0700 Subject: mac80211: mlme.c use new frame control helpers Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 75c4e9743fb..7b4d4d46843 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3271,33 +3271,32 @@ ieee80211_sta_rx_scan(struct net_device *dev, struct sk_buff *skb, struct ieee80211_rx_status *rx_status) { struct ieee80211_mgmt *mgmt; - u16 fc; + __le16 fc; if (skb->len < 2) return RX_DROP_UNUSABLE; mgmt = (struct ieee80211_mgmt *) skb->data; - fc = le16_to_cpu(mgmt->frame_control); + fc = mgmt->frame_control; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) + if (ieee80211_is_ctl(fc)) return RX_CONTINUE; if (skb->len < 24) return RX_DROP_MONITOR; - if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { - if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) { - ieee80211_rx_mgmt_probe_resp(dev, mgmt, - skb->len, rx_status); - dev_kfree_skb(skb); - return RX_QUEUED; - } else if ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) { - ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, - rx_status); - dev_kfree_skb(skb); - return RX_QUEUED; - } + if (ieee80211_is_probe_resp(fc)) { + ieee80211_rx_mgmt_probe_resp(dev, mgmt, skb->len, rx_status); + dev_kfree_skb(skb); + return RX_QUEUED; } + + if (ieee80211_is_beacon(fc)) { + ieee80211_rx_mgmt_beacon(dev, mgmt, skb->len, rx_status); + dev_kfree_skb(skb); + return RX_QUEUED; + } + return RX_CONTINUE; } @@ -3875,7 +3874,7 @@ static void ieee80211_send_nullfunc(struct ieee80211_local *local, { struct sk_buff *skb; struct ieee80211_hdr *nullfunc; - u16 fc; + __le16 fc; skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24); if (!skb) { @@ -3887,11 +3886,11 @@ static void ieee80211_send_nullfunc(struct ieee80211_local *local, nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24); memset(nullfunc, 0, 24); - fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); if (powersave) - fc |= IEEE80211_FCTL_PM; - nullfunc->frame_control = cpu_to_le16(fc); + fc |= cpu_to_le16(IEEE80211_FCTL_PM); + nullfunc->frame_control = fc; memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN); memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN); memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN); -- cgit v1.2.3 From fa6adfe9e625a6a843a1abed5f4e7a000c11952c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Jun 2008 13:37:58 +0300 Subject: mac80211: don't return -EINVAL upon iwconfig wlan0 rts auto This patch avoids returning -EINVAL upon iwconfig wlan0 rts auto. If rts->fixed is 0, then we should choose a default value instead of failing. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Acked-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/wext.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index f47d13bdf7f..3cbaf5301d0 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -721,6 +721,9 @@ static int ieee80211_ioctl_siwrts(struct net_device *dev, if (rts->disabled) local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + else if (!rts->fixed) + /* if the rts value is not fixed, then take default */ + local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD) return -EINVAL; else -- cgit v1.2.3 From b9fcc4f2987a757acb3af43aa31dc860bb957970 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 24 Jun 2008 13:37:59 +0300 Subject: mac80211: update the authentication method This patch updates the authentication method upon giwencode ioctl. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Acked-by: Johannes Berg Acked-by: Dan Williams Signed-off-by: John W. Linville --- net/mac80211/wext.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 3cbaf5301d0..5e76ab120cd 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -952,6 +952,19 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev, erq->length = sdata->keys[idx]->conf.keylen; erq->flags |= IW_ENCODE_ENABLED; + if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + switch (ifsta->auth_alg) { + case WLAN_AUTH_OPEN: + case WLAN_AUTH_LEAP: + erq->flags |= IW_ENCODE_OPEN; + break; + case WLAN_AUTH_SHARED_KEY: + erq->flags |= IW_ENCODE_RESTRICTED; + break; + } + } + return 0; } -- cgit v1.2.3 From f37d08bddc5cb8de18e55f2b0a401b3eb6269af4 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Tue, 24 Jun 2008 15:50:17 +0300 Subject: mac80211: add phy information to giwname This patch add phy information to giwname. Quoting: It's not useless, it's supposed to tell you about the protocol capability of the device, like "IEEE 802.11b" or "IEEE 802.11abg" Jean Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- net/mac80211/wext.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'net') diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c index 5e76ab120cd..df0531c2814 100644 --- a/net/mac80211/wext.c +++ b/net/mac80211/wext.c @@ -135,7 +135,39 @@ static int ieee80211_ioctl_giwname(struct net_device *dev, struct iw_request_info *info, char *name, char *extra) { + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_supported_band *sband; + u8 is_ht = 0, is_a = 0, is_b = 0, is_g = 0; + + + sband = local->hw.wiphy->bands[IEEE80211_BAND_5GHZ]; + if (sband) { + is_a = 1; + is_ht |= sband->ht_info.ht_supported; + } + + sband = local->hw.wiphy->bands[IEEE80211_BAND_2GHZ]; + if (sband) { + int i; + /* Check for mandatory rates */ + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == 10) + is_b = 1; + if (sband->bitrates[i].bitrate == 60) + is_g = 1; + } + is_ht |= sband->ht_info.ht_supported; + } + strcpy(name, "IEEE 802.11"); + if (is_a) + strcat(name, "a"); + if (is_b) + strcat(name, "b"); + if (is_g) + strcat(name, "g"); + if (is_ht) + strcat(name, "n"); return 0; } -- cgit v1.2.3 From 59959a6150c8af737898e83f727e824dbed7b0fa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 26 Jun 2008 19:59:56 +0200 Subject: mac80211: make workqueue freezable This patch makes the mac80211 workqueue freezable making it interact a bit better with system suspend and not try to ping the AP while the hardware is down. This doesn't really help with implementing proper suspend in any way but makes some bad things trigger less. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5c5396edad3..b661ee5bb82 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1691,7 +1691,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) list_add_tail(&sdata->list, &local->interfaces); name = wiphy_dev(local->hw.wiphy)->driver->name; - local->hw.workqueue = create_singlethread_workqueue(name); + local->hw.workqueue = create_freezeable_workqueue(name); if (!local->hw.workqueue) { result = -ENOMEM; goto fail_workqueue; -- cgit v1.2.3 From 03f93c3d4c8aa9ed2e2b0a949ece658053527d71 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 25 Jun 2008 14:36:27 +0300 Subject: mac80211: fix tx fragmentation This patch fixes TX fragmentation caused by tx handlers reordering and 'tx info to cb' patches Signed-off-by: Johannes Berg Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- net/mac80211/tx.c | 68 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 37 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index bf3600a0477..52ab85c4341 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -85,8 +85,8 @@ static inline void ieee80211_dump_frame(const char *ifname, const char *title, } #endif /* CONFIG_MAC80211_LOWTX_FRAME_DUMP */ -static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, - int next_frag_len) +static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, + int next_frag_len) { int rate, mrate, erp, dur, i; struct ieee80211_rate *txrate; @@ -138,7 +138,7 @@ static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, /* data/mgmt */ if (0 /* FIX: data/mgmt during CFP */) - return 32768; + return cpu_to_le16(32768); if (group_addr) /* Group address as the destination - no ACK */ return 0; @@ -208,7 +208,7 @@ static u16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, tx->sdata->bss_conf.use_short_preamble); } - return dur; + return cpu_to_le16(dur); } static int inline is_ieee80211_device(struct net_device *dev, @@ -541,7 +541,6 @@ static ieee80211_tx_result ieee80211_tx_h_misc(struct ieee80211_tx_data *tx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - u16 dur; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_supported_band *sband; @@ -599,14 +598,6 @@ ieee80211_tx_h_misc(struct ieee80211_tx_data *tx) info->flags |= IEEE80211_TX_CTL_SHORT_PREAMBLE; } - /* Setup duration field for the first fragment of the frame. Duration - * for remaining fragments will be updated when they are being sent - * to low-level driver in ieee80211_tx(). */ - dur = ieee80211_duration(tx, is_multicast_ether_addr(hdr->addr1), - (tx->flags & IEEE80211_TX_FRAGMENTED) ? - tx->extra_frag[0]->len : 0); - hdr->duration_id = cpu_to_le16(dur); - if ((info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) || (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT)) { struct ieee80211_rate *rate; @@ -708,6 +699,8 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG)); copylen = left > per_fragm ? per_fragm : left; memcpy(skb_put(frag, copylen), pos, copylen); + memcpy(frag->cb, first->cb, sizeof(frag->cb)); + skb_copy_queue_mapping(frag, first); pos += copylen; left -= copylen; @@ -751,6 +744,36 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx) return TX_DROP; } +static ieee80211_tx_result +ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; + int next_len, i; + int group_addr = is_multicast_ether_addr(hdr->addr1); + + if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) { + hdr->duration_id = ieee80211_duration(tx, group_addr, 0); + return TX_CONTINUE; + } + + hdr->duration_id = ieee80211_duration(tx, group_addr, + tx->extra_frag[0]->len); + + for (i = 0; i < tx->num_extra_frag; i++) { + if (i + 1 < tx->num_extra_frag) { + next_len = tx->extra_frag[i + 1]->len; + } else { + next_len = 0; + tx->rate_idx = tx->last_frag_rate_idx; + } + + hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data; + hdr->duration_id = ieee80211_duration(tx, 0, next_len); + } + + return TX_CONTINUE; +} + static ieee80211_tx_result ieee80211_tx_h_stats(struct ieee80211_tx_data *tx) { @@ -785,6 +808,7 @@ static ieee80211_tx_handler ieee80211_tx_handlers[] = ieee80211_tx_h_fragment, /* handlers after fragment must be aware of tx info fragmentation! */ ieee80211_tx_h_encrypt, + ieee80211_tx_h_calculate_duration, ieee80211_tx_h_stats, NULL }; @@ -1151,24 +1175,6 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb) if (invoke_tx_handlers(&tx)) goto out; - if (tx.extra_frag) { - for (i = 0; i < tx.num_extra_frag; i++) { - int next_len, dur; - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *) - tx.extra_frag[i]->data; - - if (i + 1 < tx.num_extra_frag) { - next_len = tx.extra_frag[i + 1]->len; - } else { - next_len = 0; - tx.rate_idx = tx.last_frag_rate_idx; - } - dur = ieee80211_duration(&tx, 0, next_len); - hdr->duration_id = cpu_to_le16(dur); - } - } - retry: ret = __ieee80211_tx(local, skb, &tx); if (ret) { -- cgit v1.2.3