13d23e349SJohannes Berg /*
23d23e349SJohannes Berg * This file implement the Wireless Extensions priv API.
33d23e349SJohannes Berg *
43d23e349SJohannes Berg * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
53d23e349SJohannes Berg * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
63d23e349SJohannes Berg * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
73d23e349SJohannes Berg *
83d23e349SJohannes Berg * (As all part of the Linux kernel, this file is GPL)
93d23e349SJohannes Berg */
105a0e3ad6STejun Heo #include <linux/slab.h>
113d23e349SJohannes Berg #include <linux/wireless.h>
123d23e349SJohannes Berg #include <linux/netdevice.h>
133d23e349SJohannes Berg #include <net/iw_handler.h>
143d23e349SJohannes Berg #include <net/wext.h>
153d23e349SJohannes Berg
iw_handler_get_private(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)163d23e349SJohannes Berg int iw_handler_get_private(struct net_device * dev,
173d23e349SJohannes Berg struct iw_request_info * info,
183d23e349SJohannes Berg union iwreq_data * wrqu,
193d23e349SJohannes Berg char * extra)
203d23e349SJohannes Berg {
213d23e349SJohannes Berg /* Check if the driver has something to export */
223d23e349SJohannes Berg if ((dev->wireless_handlers->num_private_args == 0) ||
233d23e349SJohannes Berg (dev->wireless_handlers->private_args == NULL))
243d23e349SJohannes Berg return -EOPNOTSUPP;
253d23e349SJohannes Berg
263d23e349SJohannes Berg /* Check if there is enough buffer up there */
273d23e349SJohannes Berg if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
283d23e349SJohannes Berg /* User space can't know in advance how large the buffer
293d23e349SJohannes Berg * needs to be. Give it a hint, so that we can support
303d23e349SJohannes Berg * any size buffer we want somewhat efficiently... */
313d23e349SJohannes Berg wrqu->data.length = dev->wireless_handlers->num_private_args;
323d23e349SJohannes Berg return -E2BIG;
333d23e349SJohannes Berg }
343d23e349SJohannes Berg
353d23e349SJohannes Berg /* Set the number of available ioctls. */
363d23e349SJohannes Berg wrqu->data.length = dev->wireless_handlers->num_private_args;
373d23e349SJohannes Berg
383d23e349SJohannes Berg /* Copy structure to the user buffer. */
393d23e349SJohannes Berg memcpy(extra, dev->wireless_handlers->private_args,
403d23e349SJohannes Berg sizeof(struct iw_priv_args) * wrqu->data.length);
413d23e349SJohannes Berg
423d23e349SJohannes Berg return 0;
433d23e349SJohannes Berg }
443d23e349SJohannes Berg
453d23e349SJohannes Berg /* Size (in bytes) of the various private data types */
463d23e349SJohannes Berg static const char iw_priv_type_size[] = {
473d23e349SJohannes Berg 0, /* IW_PRIV_TYPE_NONE */
483d23e349SJohannes Berg 1, /* IW_PRIV_TYPE_BYTE */
493d23e349SJohannes Berg 1, /* IW_PRIV_TYPE_CHAR */
503d23e349SJohannes Berg 0, /* Not defined */
513d23e349SJohannes Berg sizeof(__u32), /* IW_PRIV_TYPE_INT */
523d23e349SJohannes Berg sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
533d23e349SJohannes Berg sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
543d23e349SJohannes Berg 0, /* Not defined */
553d23e349SJohannes Berg };
563d23e349SJohannes Berg
get_priv_size(__u16 args)573d23e349SJohannes Berg static int get_priv_size(__u16 args)
583d23e349SJohannes Berg {
593d23e349SJohannes Berg int num = args & IW_PRIV_SIZE_MASK;
603d23e349SJohannes Berg int type = (args & IW_PRIV_TYPE_MASK) >> 12;
613d23e349SJohannes Berg
623d23e349SJohannes Berg return num * iw_priv_type_size[type];
633d23e349SJohannes Berg }
643d23e349SJohannes Berg
adjust_priv_size(__u16 args,struct iw_point * iwp)653d23e349SJohannes Berg static int adjust_priv_size(__u16 args, struct iw_point *iwp)
663d23e349SJohannes Berg {
673d23e349SJohannes Berg int num = iwp->length;
683d23e349SJohannes Berg int max = args & IW_PRIV_SIZE_MASK;
693d23e349SJohannes Berg int type = (args & IW_PRIV_TYPE_MASK) >> 12;
703d23e349SJohannes Berg
713d23e349SJohannes Berg /* Make sure the driver doesn't goof up */
723d23e349SJohannes Berg if (max < num)
733d23e349SJohannes Berg num = max;
743d23e349SJohannes Berg
753d23e349SJohannes Berg return num * iw_priv_type_size[type];
763d23e349SJohannes Berg }
773d23e349SJohannes Berg
783d23e349SJohannes Berg /*
793d23e349SJohannes Berg * Wrapper to call a private Wireless Extension handler.
803d23e349SJohannes Berg * We do various checks and also take care of moving data between
813d23e349SJohannes Berg * user space and kernel space.
823d23e349SJohannes Berg * It's not as nice and slimline as the standard wrapper. The cause
833d23e349SJohannes Berg * is struct iw_priv_args, which was not really designed for the
843d23e349SJohannes Berg * job we are going here.
853d23e349SJohannes Berg *
863d23e349SJohannes Berg * IMPORTANT : This function prevent to set and get data on the same
873d23e349SJohannes Berg * IOCTL and enforce the SET/GET convention. Not doing it would be
883d23e349SJohannes Berg * far too hairy...
893d23e349SJohannes Berg * If you need to set and get data at the same time, please don't use
903d23e349SJohannes Berg * a iw_handler but process it in your ioctl handler (i.e. use the
913d23e349SJohannes Berg * old driver API).
923d23e349SJohannes Berg */
get_priv_descr_and_size(struct net_device * dev,unsigned int cmd,const struct iw_priv_args ** descrp)933d23e349SJohannes Berg static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
943d23e349SJohannes Berg const struct iw_priv_args **descrp)
953d23e349SJohannes Berg {
963d23e349SJohannes Berg const struct iw_priv_args *descr;
973d23e349SJohannes Berg int i, extra_size;
983d23e349SJohannes Berg
993d23e349SJohannes Berg descr = NULL;
1003d23e349SJohannes Berg for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
1013d23e349SJohannes Berg if (cmd == dev->wireless_handlers->private_args[i].cmd) {
1023d23e349SJohannes Berg descr = &dev->wireless_handlers->private_args[i];
1033d23e349SJohannes Berg break;
1043d23e349SJohannes Berg }
1053d23e349SJohannes Berg }
1063d23e349SJohannes Berg
1073d23e349SJohannes Berg extra_size = 0;
1083d23e349SJohannes Berg if (descr) {
1093d23e349SJohannes Berg if (IW_IS_SET(cmd)) {
1103d23e349SJohannes Berg int offset = 0; /* For sub-ioctls */
1113d23e349SJohannes Berg /* Check for sub-ioctl handler */
1123d23e349SJohannes Berg if (descr->name[0] == '\0')
1133d23e349SJohannes Berg /* Reserve one int for sub-ioctl index */
1143d23e349SJohannes Berg offset = sizeof(__u32);
1153d23e349SJohannes Berg
1163d23e349SJohannes Berg /* Size of set arguments */
1173d23e349SJohannes Berg extra_size = get_priv_size(descr->set_args);
1183d23e349SJohannes Berg
1193d23e349SJohannes Berg /* Does it fits in iwr ? */
1203d23e349SJohannes Berg if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
1213d23e349SJohannes Berg ((extra_size + offset) <= IFNAMSIZ))
1223d23e349SJohannes Berg extra_size = 0;
1233d23e349SJohannes Berg } else {
1243d23e349SJohannes Berg /* Size of get arguments */
1253d23e349SJohannes Berg extra_size = get_priv_size(descr->get_args);
1263d23e349SJohannes Berg
1273d23e349SJohannes Berg /* Does it fits in iwr ? */
1283d23e349SJohannes Berg if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
1293d23e349SJohannes Berg (extra_size <= IFNAMSIZ))
1303d23e349SJohannes Berg extra_size = 0;
1313d23e349SJohannes Berg }
1323d23e349SJohannes Berg }
1333d23e349SJohannes Berg *descrp = descr;
1343d23e349SJohannes Berg return extra_size;
1353d23e349SJohannes Berg }
1363d23e349SJohannes Berg
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)1373d23e349SJohannes Berg static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
1383d23e349SJohannes Berg const struct iw_priv_args *descr,
1393d23e349SJohannes Berg iw_handler handler, struct net_device *dev,
1403d23e349SJohannes Berg struct iw_request_info *info, int extra_size)
1413d23e349SJohannes Berg {
1423d23e349SJohannes Berg char *extra;
1433d23e349SJohannes Berg int err;
1443d23e349SJohannes Berg
1453d23e349SJohannes Berg /* Check what user space is giving us */
1463d23e349SJohannes Berg if (IW_IS_SET(cmd)) {
1473d23e349SJohannes Berg if (!iwp->pointer && iwp->length != 0)
1483d23e349SJohannes Berg return -EFAULT;
1493d23e349SJohannes Berg
1503d23e349SJohannes Berg if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
1513d23e349SJohannes Berg return -E2BIG;
1523d23e349SJohannes Berg } else if (!iwp->pointer)
1533d23e349SJohannes Berg return -EFAULT;
1543d23e349SJohannes Berg
155*df6d0230SJohannes Berg extra = kzalloc(extra_size, GFP_KERNEL);
1563d23e349SJohannes Berg if (!extra)
1573d23e349SJohannes Berg return -ENOMEM;
1583d23e349SJohannes Berg
1593d23e349SJohannes Berg /* If it is a SET, get all the extra data in here */
1603d23e349SJohannes Berg if (IW_IS_SET(cmd) && (iwp->length != 0)) {
1613d23e349SJohannes Berg if (copy_from_user(extra, iwp->pointer, extra_size)) {
1623d23e349SJohannes Berg err = -EFAULT;
1633d23e349SJohannes Berg goto out;
1643d23e349SJohannes Berg }
1653d23e349SJohannes Berg }
1663d23e349SJohannes Berg
1673d23e349SJohannes Berg /* Call the handler */
1683d23e349SJohannes Berg err = handler(dev, info, (union iwreq_data *) iwp, extra);
1693d23e349SJohannes Berg
1703d23e349SJohannes Berg /* If we have something to return to the user */
1713d23e349SJohannes Berg if (!err && IW_IS_GET(cmd)) {
1723d23e349SJohannes Berg /* Adjust for the actual length if it's variable,
1733d23e349SJohannes Berg * avoid leaking kernel bits outside.
1743d23e349SJohannes Berg */
1753d23e349SJohannes Berg if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
1763d23e349SJohannes Berg extra_size = adjust_priv_size(descr->get_args, iwp);
1773d23e349SJohannes Berg
1783d23e349SJohannes Berg if (copy_to_user(iwp->pointer, extra, extra_size))
1793d23e349SJohannes Berg err = -EFAULT;
1803d23e349SJohannes Berg }
1813d23e349SJohannes Berg
1823d23e349SJohannes Berg out:
1833d23e349SJohannes Berg kfree(extra);
1843d23e349SJohannes Berg return err;
1853d23e349SJohannes Berg }
1863d23e349SJohannes Berg
ioctl_private_call(struct net_device * dev,struct iwreq * iwr,unsigned int cmd,struct iw_request_info * info,iw_handler handler)1873d23e349SJohannes Berg int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
1883d23e349SJohannes Berg unsigned int cmd, struct iw_request_info *info,
1893d23e349SJohannes Berg iw_handler handler)
1903d23e349SJohannes Berg {
1913d23e349SJohannes Berg int extra_size = 0, ret = -EINVAL;
1923d23e349SJohannes Berg const struct iw_priv_args *descr;
1933d23e349SJohannes Berg
1943d23e349SJohannes Berg extra_size = get_priv_descr_and_size(dev, cmd, &descr);
1953d23e349SJohannes Berg
1963d23e349SJohannes Berg /* Check if we have a pointer to user space data or not. */
1973d23e349SJohannes Berg if (extra_size == 0) {
1983d23e349SJohannes Berg /* No extra arguments. Trivial to handle */
1993d23e349SJohannes Berg ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
2003d23e349SJohannes Berg } else {
2013d23e349SJohannes Berg ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
2023d23e349SJohannes Berg handler, dev, info, extra_size);
2033d23e349SJohannes Berg }
2043d23e349SJohannes Berg
2053d23e349SJohannes Berg /* Call commit handler if needed and defined */
2063d23e349SJohannes Berg if (ret == -EIWCOMMIT)
2073d23e349SJohannes Berg ret = call_commit_handler(dev);
2083d23e349SJohannes Berg
2093d23e349SJohannes Berg return ret;
2103d23e349SJohannes Berg }
2113d23e349SJohannes Berg
2123d23e349SJohannes Berg #ifdef CONFIG_COMPAT
compat_private_call(struct net_device * dev,struct iwreq * iwr,unsigned int cmd,struct iw_request_info * info,iw_handler handler)2133d23e349SJohannes Berg int compat_private_call(struct net_device *dev, struct iwreq *iwr,
2143d23e349SJohannes Berg unsigned int cmd, struct iw_request_info *info,
2153d23e349SJohannes Berg iw_handler handler)
2163d23e349SJohannes Berg {
2173d23e349SJohannes Berg const struct iw_priv_args *descr;
2183d23e349SJohannes Berg int ret, extra_size;
2193d23e349SJohannes Berg
2203d23e349SJohannes Berg extra_size = get_priv_descr_and_size(dev, cmd, &descr);
2213d23e349SJohannes Berg
2223d23e349SJohannes Berg /* Check if we have a pointer to user space data or not. */
2233d23e349SJohannes Berg if (extra_size == 0) {
2243d23e349SJohannes Berg /* No extra arguments. Trivial to handle */
2253d23e349SJohannes Berg ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
2263d23e349SJohannes Berg } else {
2273d23e349SJohannes Berg struct compat_iw_point *iwp_compat;
2283d23e349SJohannes Berg struct iw_point iwp;
2293d23e349SJohannes Berg
2303d23e349SJohannes Berg iwp_compat = (struct compat_iw_point *) &iwr->u.data;
2313d23e349SJohannes Berg iwp.pointer = compat_ptr(iwp_compat->pointer);
2323d23e349SJohannes Berg iwp.length = iwp_compat->length;
2333d23e349SJohannes Berg iwp.flags = iwp_compat->flags;
2343d23e349SJohannes Berg
2353d23e349SJohannes Berg ret = ioctl_private_iw_point(&iwp, cmd, descr,
2363d23e349SJohannes Berg handler, dev, info, extra_size);
2373d23e349SJohannes Berg
2383d23e349SJohannes Berg iwp_compat->pointer = ptr_to_compat(iwp.pointer);
2393d23e349SJohannes Berg iwp_compat->length = iwp.length;
2403d23e349SJohannes Berg iwp_compat->flags = iwp.flags;
2413d23e349SJohannes Berg }
2423d23e349SJohannes Berg
2433d23e349SJohannes Berg /* Call commit handler if needed and defined */
2443d23e349SJohannes Berg if (ret == -EIWCOMMIT)
2453d23e349SJohannes Berg ret = call_commit_handler(dev);
2463d23e349SJohannes Berg
2473d23e349SJohannes Berg return ret;
2483d23e349SJohannes Berg }
2493d23e349SJohannes Berg #endif
250