0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/slab.h>
0011 #include <linux/wireless.h>
0012 #include <linux/netdevice.h>
0013 #include <net/iw_handler.h>
0014 #include <net/wext.h>
0015
0016 int iw_handler_get_private(struct net_device * dev,
0017 struct iw_request_info * info,
0018 union iwreq_data * wrqu,
0019 char * extra)
0020 {
0021
0022 if ((dev->wireless_handlers->num_private_args == 0) ||
0023 (dev->wireless_handlers->private_args == NULL))
0024 return -EOPNOTSUPP;
0025
0026
0027 if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
0028
0029
0030
0031 wrqu->data.length = dev->wireless_handlers->num_private_args;
0032 return -E2BIG;
0033 }
0034
0035
0036 wrqu->data.length = dev->wireless_handlers->num_private_args;
0037
0038
0039 memcpy(extra, dev->wireless_handlers->private_args,
0040 sizeof(struct iw_priv_args) * wrqu->data.length);
0041
0042 return 0;
0043 }
0044
0045
0046 static const char iw_priv_type_size[] = {
0047 0,
0048 1,
0049 1,
0050 0,
0051 sizeof(__u32),
0052 sizeof(struct iw_freq),
0053 sizeof(struct sockaddr),
0054 0,
0055 };
0056
0057 static int get_priv_size(__u16 args)
0058 {
0059 int num = args & IW_PRIV_SIZE_MASK;
0060 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
0061
0062 return num * iw_priv_type_size[type];
0063 }
0064
0065 static int adjust_priv_size(__u16 args, struct iw_point *iwp)
0066 {
0067 int num = iwp->length;
0068 int max = args & IW_PRIV_SIZE_MASK;
0069 int type = (args & IW_PRIV_TYPE_MASK) >> 12;
0070
0071
0072 if (max < num)
0073 num = max;
0074
0075 return num * iw_priv_type_size[type];
0076 }
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093 static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
0094 const struct iw_priv_args **descrp)
0095 {
0096 const struct iw_priv_args *descr;
0097 int i, extra_size;
0098
0099 descr = NULL;
0100 for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
0101 if (cmd == dev->wireless_handlers->private_args[i].cmd) {
0102 descr = &dev->wireless_handlers->private_args[i];
0103 break;
0104 }
0105 }
0106
0107 extra_size = 0;
0108 if (descr) {
0109 if (IW_IS_SET(cmd)) {
0110 int offset = 0;
0111
0112 if (descr->name[0] == '\0')
0113
0114 offset = sizeof(__u32);
0115
0116
0117 extra_size = get_priv_size(descr->set_args);
0118
0119
0120 if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
0121 ((extra_size + offset) <= IFNAMSIZ))
0122 extra_size = 0;
0123 } else {
0124
0125 extra_size = get_priv_size(descr->get_args);
0126
0127
0128 if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
0129 (extra_size <= IFNAMSIZ))
0130 extra_size = 0;
0131 }
0132 }
0133 *descrp = descr;
0134 return extra_size;
0135 }
0136
0137 static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
0138 const struct iw_priv_args *descr,
0139 iw_handler handler, struct net_device *dev,
0140 struct iw_request_info *info, int extra_size)
0141 {
0142 char *extra;
0143 int err;
0144
0145
0146 if (IW_IS_SET(cmd)) {
0147 if (!iwp->pointer && iwp->length != 0)
0148 return -EFAULT;
0149
0150 if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
0151 return -E2BIG;
0152 } else if (!iwp->pointer)
0153 return -EFAULT;
0154
0155 extra = kzalloc(extra_size, GFP_KERNEL);
0156 if (!extra)
0157 return -ENOMEM;
0158
0159
0160 if (IW_IS_SET(cmd) && (iwp->length != 0)) {
0161 if (copy_from_user(extra, iwp->pointer, extra_size)) {
0162 err = -EFAULT;
0163 goto out;
0164 }
0165 }
0166
0167
0168 err = handler(dev, info, (union iwreq_data *) iwp, extra);
0169
0170
0171 if (!err && IW_IS_GET(cmd)) {
0172
0173
0174
0175 if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
0176 extra_size = adjust_priv_size(descr->get_args, iwp);
0177
0178 if (copy_to_user(iwp->pointer, extra, extra_size))
0179 err = -EFAULT;
0180 }
0181
0182 out:
0183 kfree(extra);
0184 return err;
0185 }
0186
0187 int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
0188 unsigned int cmd, struct iw_request_info *info,
0189 iw_handler handler)
0190 {
0191 int extra_size = 0, ret = -EINVAL;
0192 const struct iw_priv_args *descr;
0193
0194 extra_size = get_priv_descr_and_size(dev, cmd, &descr);
0195
0196
0197 if (extra_size == 0) {
0198
0199 ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
0200 } else {
0201 ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
0202 handler, dev, info, extra_size);
0203 }
0204
0205
0206 if (ret == -EIWCOMMIT)
0207 ret = call_commit_handler(dev);
0208
0209 return ret;
0210 }
0211
0212 #ifdef CONFIG_COMPAT
0213 int compat_private_call(struct net_device *dev, struct iwreq *iwr,
0214 unsigned int cmd, struct iw_request_info *info,
0215 iw_handler handler)
0216 {
0217 const struct iw_priv_args *descr;
0218 int ret, extra_size;
0219
0220 extra_size = get_priv_descr_and_size(dev, cmd, &descr);
0221
0222
0223 if (extra_size == 0) {
0224
0225 ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
0226 } else {
0227 struct compat_iw_point *iwp_compat;
0228 struct iw_point iwp;
0229
0230 iwp_compat = (struct compat_iw_point *) &iwr->u.data;
0231 iwp.pointer = compat_ptr(iwp_compat->pointer);
0232 iwp.length = iwp_compat->length;
0233 iwp.flags = iwp_compat->flags;
0234
0235 ret = ioctl_private_iw_point(&iwp, cmd, descr,
0236 handler, dev, info, extra_size);
0237
0238 iwp_compat->pointer = ptr_to_compat(iwp.pointer);
0239 iwp_compat->length = iwp.length;
0240 iwp_compat->flags = iwp.flags;
0241 }
0242
0243
0244 if (ret == -EIWCOMMIT)
0245 ret = call_commit_handler(dev);
0246
0247 return ret;
0248 }
0249 #endif