aboutsummaryrefslogtreecommitdiffstats
path: root/net/core/wireless.c
diff options
context:
space:
mode:
authorJean Tourrilhes <jt@hpl.hp.com>2006-02-22 18:10:56 -0500
committerJohn W. Linville <linville@tuxdriver.com>2006-03-23 07:12:57 -0500
commit711e2c33ac9221a419a9e28d05dd78a6a9c5fd4d (patch)
tree2d04ff5de8a0d933d3d123a52a6e4285f84e9025 /net/core/wireless.c
parent9a107aa24a1dbc05d58a4fdd2c4d2861f8bd5ae6 (diff)
[PATCH] WE-20 for kernel 2.6.16
This is version 20 of the Wireless Extensions. This is the completion of the RtNetlink work I started early 2004, it enables the full Wireless Extension API over RtNetlink. Few comments on the patch : o totally driver transparent, no change in drivers needed. o iwevent were already RtNetlink based since they were created (around 2.5.7). This adds all the regular SET and GET requests over RtNetlink, using the exact same mechanism and data format as iwevents. o This is a Kconfig option, as currently most people have no need for it. Surprisingly, patch is actually small and well encapsulated. o Tested on SMP, attention as been paid to make it 64 bits clean. o Code do probably too many checks and could be further optimised, but better safe than sorry. o RtNetlink based version of the Wireless Tools available on my web page for people inclined to try out this stuff. I would also like to thank Alexey Kuznetsov for his helpful suggestions to make this patch better. Signed-off-by: Jean Tourrilhes <jt@hpl.hp.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/core/wireless.c')
-rw-r--r--net/core/wireless.c911
1 files changed, 823 insertions, 88 deletions
diff --git a/net/core/wireless.c b/net/core/wireless.c
index 2add7ed609e9..81d6995fcfdb 100644
--- a/net/core/wireless.c
+++ b/net/core/wireless.c
@@ -2,7 +2,7 @@
2 * This file implement the Wireless Extensions APIs. 2 * This file implement the Wireless Extensions APIs.
3 * 3 *
4 * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> 4 * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
5 * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. 5 * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
6 * 6 *
7 * (As all part of the Linux kernel, this file is GPL) 7 * (As all part of the Linux kernel, this file is GPL)
8 */ 8 */
@@ -65,6 +65,9 @@
65 * o Start deprecating dev->get_wireless_stats, output a warning 65 * o Start deprecating dev->get_wireless_stats, output a warning
66 * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless 66 * o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
67 * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats) 67 * o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
68 *
69 * v8 - 17.02.06 - Jean II
70 * o RtNetlink requests support (SET/GET)
68 */ 71 */
69 72
70/***************************** INCLUDES *****************************/ 73/***************************** INCLUDES *****************************/
@@ -89,11 +92,13 @@
89 92
90/* Debugging stuff */ 93/* Debugging stuff */
91#undef WE_IOCTL_DEBUG /* Debug IOCTL API */ 94#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
95#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */
92#undef WE_EVENT_DEBUG /* Debug Event dispatcher */ 96#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
93#undef WE_SPY_DEBUG /* Debug enhanced spy support */ 97#undef WE_SPY_DEBUG /* Debug enhanced spy support */
94 98
95/* Options */ 99/* Options */
96#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */ 100//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
101#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
97#define WE_SET_EVENT /* Generate an event on some set commands */ 102#define WE_SET_EVENT /* Generate an event on some set commands */
98 103
99/************************* GLOBAL VARIABLES *************************/ 104/************************* GLOBAL VARIABLES *************************/
@@ -156,13 +161,18 @@ static const struct iw_ioctl_description standard_ioctl[] = {
156 .header_type = IW_HEADER_TYPE_NULL, 161 .header_type = IW_HEADER_TYPE_NULL,
157 }, 162 },
158 [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ 163 [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
159 .header_type = IW_HEADER_TYPE_NULL, 164 .header_type = IW_HEADER_TYPE_POINT,
165 .token_size = sizeof(struct iw_priv_args),
166 .max_tokens = 16,
167 .flags = IW_DESCR_FLAG_NOMAX,
160 }, 168 },
161 [SIOCSIWSTATS - SIOCIWFIRST] = { 169 [SIOCSIWSTATS - SIOCIWFIRST] = {
162 .header_type = IW_HEADER_TYPE_NULL, 170 .header_type = IW_HEADER_TYPE_NULL,
163 }, 171 },
164 [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ 172 [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
165 .header_type = IW_HEADER_TYPE_NULL, 173 .header_type = IW_HEADER_TYPE_POINT,
174 .token_size = 1,
175 .max_tokens = sizeof(struct iw_statistics),
166 .flags = IW_DESCR_FLAG_DUMP, 176 .flags = IW_DESCR_FLAG_DUMP,
167 }, 177 },
168 [SIOCSIWSPY - SIOCIWFIRST] = { 178 [SIOCSIWSPY - SIOCIWFIRST] = {
@@ -529,6 +539,70 @@ static inline int adjust_priv_size(__u16 args,
529 return num * iw_priv_type_size[type]; 539 return num * iw_priv_type_size[type];
530} 540}
531 541
542/* ---------------------------------------------------------------- */
543/*
544 * Standard Wireless Handler : get wireless stats
545 * Allow programatic access to /proc/net/wireless even if /proc
546 * doesn't exist... Also more efficient...
547 */
548static int iw_handler_get_iwstats(struct net_device * dev,
549 struct iw_request_info * info,
550 union iwreq_data * wrqu,
551 char * extra)
552{
553 /* Get stats from the driver */
554 struct iw_statistics *stats;
555
556 stats = get_wireless_stats(dev);
557 if (stats != (struct iw_statistics *) NULL) {
558
559 /* Copy statistics to extra */
560 memcpy(extra, stats, sizeof(struct iw_statistics));
561 wrqu->data.length = sizeof(struct iw_statistics);
562
563 /* Check if we need to clear the updated flag */
564 if(wrqu->data.flags != 0)
565 stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
566 return 0;
567 } else
568 return -EOPNOTSUPP;
569}
570
571/* ---------------------------------------------------------------- */
572/*
573 * Standard Wireless Handler : get iwpriv definitions
574 * Export the driver private handler definition
575 * They will be picked up by tools like iwpriv...
576 */
577static int iw_handler_get_private(struct net_device * dev,
578 struct iw_request_info * info,
579 union iwreq_data * wrqu,
580 char * extra)
581{
582 /* Check if the driver has something to export */
583 if((dev->wireless_handlers->num_private_args == 0) ||
584 (dev->wireless_handlers->private_args == NULL))
585 return -EOPNOTSUPP;
586
587 /* Check if there is enough buffer up there */
588 if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
589 /* User space can't know in advance how large the buffer
590 * needs to be. Give it a hint, so that we can support
591 * any size buffer we want somewhat efficiently... */
592 wrqu->data.length = dev->wireless_handlers->num_private_args;
593 return -E2BIG;
594 }
595
596 /* Set the number of available ioctls. */
597 wrqu->data.length = dev->wireless_handlers->num_private_args;
598
599 /* Copy structure to the user buffer. */
600 memcpy(extra, dev->wireless_handlers->private_args,
601 sizeof(struct iw_priv_args) * wrqu->data.length);
602
603 return 0;
604}
605
532 606
533/******************** /proc/net/wireless SUPPORT ********************/ 607/******************** /proc/net/wireless SUPPORT ********************/
534/* 608/*
@@ -630,81 +704,14 @@ int __init wireless_proc_init(void)
630 704
631/* ---------------------------------------------------------------- */ 705/* ---------------------------------------------------------------- */
632/* 706/*
633 * Allow programatic access to /proc/net/wireless even if /proc
634 * doesn't exist... Also more efficient...
635 */
636static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
637{
638 /* Get stats from the driver */
639 struct iw_statistics *stats;
640
641 stats = get_wireless_stats(dev);
642 if (stats != (struct iw_statistics *) NULL) {
643 struct iwreq * wrq = (struct iwreq *)ifr;
644
645 /* Copy statistics to the user buffer */
646 if(copy_to_user(wrq->u.data.pointer, stats,
647 sizeof(struct iw_statistics)))
648 return -EFAULT;
649
650 /* Check if we need to clear the updated flag */
651 if(wrq->u.data.flags != 0)
652 stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
653 return 0;
654 } else
655 return -EOPNOTSUPP;
656}
657
658/* ---------------------------------------------------------------- */
659/*
660 * Export the driver private handler definition
661 * They will be picked up by tools like iwpriv...
662 */
663static inline int ioctl_export_private(struct net_device * dev,
664 struct ifreq * ifr)
665{
666 struct iwreq * iwr = (struct iwreq *) ifr;
667
668 /* Check if the driver has something to export */
669 if((dev->wireless_handlers->num_private_args == 0) ||
670 (dev->wireless_handlers->private_args == NULL))
671 return -EOPNOTSUPP;
672
673 /* Check NULL pointer */
674 if(iwr->u.data.pointer == NULL)
675 return -EFAULT;
676
677 /* Check if there is enough buffer up there */
678 if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
679 /* User space can't know in advance how large the buffer
680 * needs to be. Give it a hint, so that we can support
681 * any size buffer we want somewhat efficiently... */
682 iwr->u.data.length = dev->wireless_handlers->num_private_args;
683 return -E2BIG;
684 }
685
686 /* Set the number of available ioctls. */
687 iwr->u.data.length = dev->wireless_handlers->num_private_args;
688
689 /* Copy structure to the user buffer. */
690 if (copy_to_user(iwr->u.data.pointer,
691 dev->wireless_handlers->private_args,
692 sizeof(struct iw_priv_args) * iwr->u.data.length))
693 return -EFAULT;
694
695 return 0;
696}
697
698/* ---------------------------------------------------------------- */
699/*
700 * Wrapper to call a standard Wireless Extension handler. 707 * Wrapper to call a standard Wireless Extension handler.
701 * We do various checks and also take care of moving data between 708 * We do various checks and also take care of moving data between
702 * user space and kernel space. 709 * user space and kernel space.
703 */ 710 */
704static inline int ioctl_standard_call(struct net_device * dev, 711static int ioctl_standard_call(struct net_device * dev,
705 struct ifreq * ifr, 712 struct ifreq * ifr,
706 unsigned int cmd, 713 unsigned int cmd,
707 iw_handler handler) 714 iw_handler handler)
708{ 715{
709 struct iwreq * iwr = (struct iwreq *) ifr; 716 struct iwreq * iwr = (struct iwreq *) ifr;
710 const struct iw_ioctl_description * descr; 717 const struct iw_ioctl_description * descr;
@@ -1048,14 +1055,20 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
1048 { 1055 {
1049 case SIOCGIWSTATS: 1056 case SIOCGIWSTATS:
1050 /* Get Wireless Stats */ 1057 /* Get Wireless Stats */
1051 return dev_iwstats(dev, ifr); 1058 return ioctl_standard_call(dev,
1059 ifr,
1060 cmd,
1061 &iw_handler_get_iwstats);
1052 1062
1053 case SIOCGIWPRIV: 1063 case SIOCGIWPRIV:
1054 /* Check if we have some wireless handlers defined */ 1064 /* Check if we have some wireless handlers defined */
1055 if(dev->wireless_handlers != NULL) { 1065 if(dev->wireless_handlers != NULL) {
1056 /* We export to user space the definition of 1066 /* We export to user space the definition of
1057 * the private handler ourselves */ 1067 * the private handler ourselves */
1058 return ioctl_export_private(dev, ifr); 1068 return ioctl_standard_call(dev,
1069 ifr,
1070 cmd,
1071 &iw_handler_get_private);
1059 } 1072 }
1060 // ## Fall-through for old API ## 1073 // ## Fall-through for old API ##
1061 default: 1074 default:
@@ -1088,16 +1101,739 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
1088 return -EINVAL; 1101 return -EINVAL;
1089} 1102}
1090 1103
1104/********************** RTNETLINK REQUEST API **********************/
1105/*
1106 * The alternate user space API to configure all those Wireless Extensions
1107 * is through RtNetlink.
1108 * This API support only the new driver API (iw_handler).
1109 *
1110 * This RtNetlink API use the same query/reply model as the ioctl API.
1111 * Maximum effort has been done to fit in the RtNetlink model, and
1112 * we support both RtNetlink Set and RtNelink Get operations.
1113 * On the other hand, we don't offer Dump operations because of the
1114 * following reasons :
1115 * o Large number of parameters, most optional
1116 * o Large size of some parameters (> 100 bytes)
1117 * o Each parameters need to be extracted from hardware
1118 * o Scan requests can take seconds and disable network activity.
1119 * Because of this high cost/overhead, we want to return only the
1120 * parameters the user application is really interested in.
1121 * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
1122 *
1123 * The API uses the standard RtNetlink socket. When the RtNetlink code
1124 * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
1125 * it calls here.
1126 */
1127
1128#ifdef CONFIG_NET_WIRELESS_RTNETLINK
1129/* ---------------------------------------------------------------- */
1130/*
1131 * Wrapper to call a standard Wireless Extension GET handler.
1132 * We do various checks and call the handler with the proper args.
1133 */
1134static int rtnetlink_standard_get(struct net_device * dev,
1135 struct iw_event * request,
1136 int request_len,
1137 iw_handler handler,
1138 char ** p_buf,
1139 int * p_len)
1140{
1141 const struct iw_ioctl_description * descr = NULL;
1142 unsigned int cmd;
1143 union iwreq_data * wrqu;
1144 int hdr_len;
1145 struct iw_request_info info;
1146 char * buffer = NULL;
1147 int buffer_size = 0;
1148 int ret = -EINVAL;
1149
1150 /* Get the description of the Request */
1151 cmd = request->cmd;
1152 if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
1153 return -EOPNOTSUPP;
1154 descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
1155
1156#ifdef WE_RTNETLINK_DEBUG
1157 printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
1158 dev->name, cmd);
1159 printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
1160#endif /* WE_RTNETLINK_DEBUG */
1161
1162 /* Check if wrqu is complete */
1163 hdr_len = event_type_size[descr->header_type];
1164 if(request_len < hdr_len) {
1165#ifdef WE_RTNETLINK_DEBUG
1166 printk(KERN_DEBUG
1167 "%s (WE.r) : Wireless request too short (%d)\n",
1168 dev->name, request_len);
1169#endif /* WE_RTNETLINK_DEBUG */
1170 return -EINVAL;
1171 }
1172
1173 /* Prepare the call */
1174 info.cmd = cmd;
1175 info.flags = 0;
1176
1177 /* Check if we have extra data in the reply or not */
1178 if(descr->header_type != IW_HEADER_TYPE_POINT) {
1179
1180 /* Create the kernel buffer that we will return.
1181 * It's at an offset to match the TYPE_POINT case... */
1182 buffer_size = request_len + IW_EV_POINT_OFF;
1183 buffer = kmalloc(buffer_size, GFP_KERNEL);
1184 if (buffer == NULL) {
1185 return -ENOMEM;
1186 }
1187 /* Copy event data */
1188 memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
1189 /* Use our own copy of wrqu */
1190 wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
1191 + IW_EV_LCP_LEN);
1192
1193 /* No extra arguments. Trivial to handle */
1194 ret = handler(dev, &info, wrqu, NULL);
1195
1196 } else {
1197 union iwreq_data wrqu_point;
1198 char * extra = NULL;
1199 int extra_size = 0;
1200
1201 /* Get a temp copy of wrqu (skip pointer) */
1202 memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
1203 ((char *) request) + IW_EV_LCP_LEN,
1204 IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1205
1206 /* Calculate space needed by arguments. Always allocate
1207 * for max space. Easier, and won't last long... */
1208 extra_size = descr->max_tokens * descr->token_size;
1209 /* Support for very large requests */
1210 if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
1211 (wrqu_point.data.length > descr->max_tokens))
1212 extra_size = (wrqu_point.data.length
1213 * descr->token_size);
1214 buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
1215#ifdef WE_RTNETLINK_DEBUG
1216 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
1217 dev->name, extra_size, buffer_size);
1218#endif /* WE_RTNETLINK_DEBUG */
1219
1220 /* Create the kernel buffer that we will return */
1221 buffer = kmalloc(buffer_size, GFP_KERNEL);
1222 if (buffer == NULL) {
1223 return -ENOMEM;
1224 }
1225
1226 /* Put wrqu in the right place (just before extra).
1227 * Leave space for IWE header and dummy pointer...
1228 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
1229 */
1230 memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
1231 ((char *) &wrqu_point) + IW_EV_POINT_OFF,
1232 IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1233 wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
1234
1235 /* Extra comes logically after that. Offset +12 bytes. */
1236 extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
1237
1238 /* Call the handler */
1239 ret = handler(dev, &info, wrqu, extra);
1240
1241 /* Calculate real returned length */
1242 extra_size = (wrqu->data.length * descr->token_size);
1243 /* Re-adjust reply size */
1244 request->len = extra_size + IW_EV_POINT_LEN;
1245
1246 /* Put the iwe header where it should, i.e. scrap the
1247 * dummy pointer. */
1248 memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
1249
1250#ifdef WE_RTNETLINK_DEBUG
1251 printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
1252#endif /* WE_RTNETLINK_DEBUG */
1253
1254 /* Check if there is enough buffer up there */
1255 if(wrqu_point.data.length < wrqu->data.length)
1256 ret = -E2BIG;
1257 }
1258
1259 /* Return the buffer to the caller */
1260 if (!ret) {
1261 *p_buf = buffer;
1262 *p_len = request->len;
1263 } else {
1264 /* Cleanup */
1265 if(buffer)
1266 kfree(buffer);
1267 }
1268
1269 return ret;
1270}
1271
1272/* ---------------------------------------------------------------- */
1273/*
1274 * Wrapper to call a standard Wireless Extension SET handler.
1275 * We do various checks and call the handler with the proper args.
1276 */
1277static inline int rtnetlink_standard_set(struct net_device * dev,
1278 struct iw_event * request,
1279 int request_len,
1280 iw_handler handler)
1281{
1282 const struct iw_ioctl_description * descr = NULL;
1283 unsigned int cmd;
1284 union iwreq_data * wrqu;
1285 union iwreq_data wrqu_point;
1286 int hdr_len;
1287 char * extra = NULL;
1288 int extra_size = 0;
1289 struct iw_request_info info;
1290 int ret = -EINVAL;
1291
1292 /* Get the description of the Request */
1293 cmd = request->cmd;
1294 if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
1295 return -EOPNOTSUPP;
1296 descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
1297
1298#ifdef WE_RTNETLINK_DEBUG
1299 printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
1300 dev->name, cmd);
1301 printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
1302#endif /* WE_RTNETLINK_DEBUG */
1303
1304 /* Extract fixed header from request. This is properly aligned. */
1305 wrqu = &request->u;
1306
1307 /* Check if wrqu is complete */
1308 hdr_len = event_type_size[descr->header_type];
1309 if(request_len < hdr_len) {
1310#ifdef WE_RTNETLINK_DEBUG
1311 printk(KERN_DEBUG
1312 "%s (WE.r) : Wireless request too short (%d)\n",
1313 dev->name, request_len);
1314#endif /* WE_RTNETLINK_DEBUG */
1315 return -EINVAL;
1316 }
1317
1318 /* Prepare the call */
1319 info.cmd = cmd;
1320 info.flags = 0;
1321
1322 /* Check if we have extra data in the request or not */
1323 if(descr->header_type != IW_HEADER_TYPE_POINT) {
1324
1325 /* No extra arguments. Trivial to handle */
1326 ret = handler(dev, &info, wrqu, NULL);
1327
1328 } else {
1329 int extra_len;
1330
1331 /* Put wrqu in the right place (skip pointer) */
1332 memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
1333 wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1334 /* Don't forget about the event code... */
1335 wrqu = &wrqu_point;
1336
1337 /* Check if number of token fits within bounds */
1338 if(wrqu_point.data.length > descr->max_tokens)
1339 return -E2BIG;
1340 if(wrqu_point.data.length < descr->min_tokens)
1341 return -EINVAL;
1342
1343 /* Real length of payload */
1344 extra_len = wrqu_point.data.length * descr->token_size;
1345
1346 /* Check if request is self consistent */
1347 if((request_len - hdr_len) < extra_len) {
1348#ifdef WE_RTNETLINK_DEBUG
1349 printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
1350 dev->name, extra_size);
1351#endif /* WE_RTNETLINK_DEBUG */
1352 return -EINVAL;
1353 }
1354
1355#ifdef WE_RTNETLINK_DEBUG
1356 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
1357 dev->name, extra_size);
1358#endif /* WE_RTNETLINK_DEBUG */
1359
1360 /* Always allocate for max space. Easier, and won't last
1361 * long... */
1362 extra_size = descr->max_tokens * descr->token_size;
1363 extra = kmalloc(extra_size, GFP_KERNEL);
1364 if (extra == NULL)
1365 return -ENOMEM;
1366
1367 /* Copy extra in aligned buffer */
1368 memcpy(extra, ((char *) request) + hdr_len, extra_len);
1369
1370 /* Call the handler */
1371 ret = handler(dev, &info, &wrqu_point, extra);
1372 }
1373
1374#ifdef WE_SET_EVENT
1375 /* Generate an event to notify listeners of the change */
1376 if((descr->flags & IW_DESCR_FLAG_EVENT) &&
1377 ((ret == 0) || (ret == -EIWCOMMIT))) {
1378 if(descr->flags & IW_DESCR_FLAG_RESTRICT)
1379 /* If the event is restricted, don't
1380 * export the payload */
1381 wireless_send_event(dev, cmd, wrqu, NULL);
1382 else
1383 wireless_send_event(dev, cmd, wrqu, extra);
1384 }
1385#endif /* WE_SET_EVENT */
1386
1387 /* Cleanup - I told you it wasn't that long ;-) */
1388 if(extra)
1389 kfree(extra);
1390
1391 /* Call commit handler if needed and defined */
1392 if(ret == -EIWCOMMIT)
1393 ret = call_commit_handler(dev);
1394
1395 return ret;
1396}
1397
1398/* ---------------------------------------------------------------- */
1399/*
1400 * Wrapper to call a private Wireless Extension GET handler.
1401 * Same as above...
1402 * It's not as nice and slimline as the standard wrapper. The cause
1403 * is struct iw_priv_args, which was not really designed for the
1404 * job we are going here.
1405 *
1406 * IMPORTANT : This function prevent to set and get data on the same
1407 * IOCTL and enforce the SET/GET convention. Not doing it would be
1408 * far too hairy...
1409 * If you need to set and get data at the same time, please don't use
1410 * a iw_handler but process it in your ioctl handler (i.e. use the
1411 * old driver API).
1412 */
1413static inline int rtnetlink_private_get(struct net_device * dev,
1414 struct iw_event * request,
1415 int request_len,
1416 iw_handler handler,
1417 char ** p_buf,
1418 int * p_len)
1419{
1420 const struct iw_priv_args * descr = NULL;
1421 unsigned int cmd;
1422 union iwreq_data * wrqu;
1423 int hdr_len;
1424 struct iw_request_info info;
1425 int extra_size = 0;
1426 int i;
1427 char * buffer = NULL;
1428 int buffer_size = 0;
1429 int ret = -EINVAL;
1430
1431 /* Get the description of the Request */
1432 cmd = request->cmd;
1433 for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
1434 if(cmd == dev->wireless_handlers->private_args[i].cmd) {
1435 descr = &(dev->wireless_handlers->private_args[i]);
1436 break;
1437 }
1438 if(descr == NULL)
1439 return -EOPNOTSUPP;
1440
1441#ifdef WE_RTNETLINK_DEBUG
1442 printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
1443 dev->name, cmd);
1444 printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
1445 dev->name, descr->name, descr->set_args, descr->get_args);
1446#endif /* WE_RTNETLINK_DEBUG */
1447
1448 /* Compute the max size of the get arguments */
1449 extra_size = get_priv_size(descr->get_args);
1450
1451 /* Does it fits in wrqu ? */
1452 if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
1453 (extra_size <= IFNAMSIZ)) {
1454 hdr_len = extra_size;
1455 extra_size = 0;
1456 } else {
1457 hdr_len = IW_EV_POINT_LEN;
1458 }
1459
1460 /* Check if wrqu is complete */
1461 if(request_len < hdr_len) {
1462#ifdef WE_RTNETLINK_DEBUG
1463 printk(KERN_DEBUG
1464 "%s (WE.r) : Wireless request too short (%d)\n",
1465 dev->name, request_len);
1466#endif /* WE_RTNETLINK_DEBUG */
1467 return -EINVAL;
1468 }
1469
1470 /* Prepare the call */
1471 info.cmd = cmd;
1472 info.flags = 0;
1473
1474 /* Check if we have a pointer to user space data or not. */
1475 if(extra_size == 0) {
1476
1477 /* Create the kernel buffer that we will return.
1478 * It's at an offset to match the TYPE_POINT case... */
1479 buffer_size = request_len + IW_EV_POINT_OFF;
1480 buffer = kmalloc(buffer_size, GFP_KERNEL);
1481 if (buffer == NULL) {
1482 return -ENOMEM;
1483 }
1484 /* Copy event data */
1485 memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
1486 /* Use our own copy of wrqu */
1487 wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
1488 + IW_EV_LCP_LEN);
1489
1490 /* No extra arguments. Trivial to handle */
1491 ret = handler(dev, &info, wrqu, (char *) wrqu);
1492
1493 } else {
1494 char * extra;
1495
1496 /* Buffer for full reply */
1497 buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
1498
1499#ifdef WE_RTNETLINK_DEBUG
1500 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
1501 dev->name, extra_size, buffer_size);
1502#endif /* WE_RTNETLINK_DEBUG */
1503
1504 /* Create the kernel buffer that we will return */
1505 buffer = kmalloc(buffer_size, GFP_KERNEL);
1506 if (buffer == NULL) {
1507 return -ENOMEM;
1508 }
1509
1510 /* Put wrqu in the right place (just before extra).
1511 * Leave space for IWE header and dummy pointer...
1512 * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
1513 */
1514 memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
1515 ((char *) request) + IW_EV_LCP_LEN,
1516 IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1517 wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
1518
1519 /* Extra comes logically after that. Offset +12 bytes. */
1520 extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
1521
1522 /* Call the handler */
1523 ret = handler(dev, &info, wrqu, extra);
1524
1525 /* Adjust for the actual length if it's variable,
1526 * avoid leaking kernel bits outside. */
1527 if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
1528 extra_size = adjust_priv_size(descr->get_args, wrqu);
1529 /* Re-adjust reply size */
1530 request->len = extra_size + IW_EV_POINT_LEN;
1531
1532 /* Put the iwe header where it should, i.e. scrap the
1533 * dummy pointer. */
1534 memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
1535
1536#ifdef WE_RTNETLINK_DEBUG
1537 printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
1538#endif /* WE_RTNETLINK_DEBUG */
1539 }
1540
1541 /* Return the buffer to the caller */
1542 if (!ret) {
1543 *p_buf = buffer;
1544 *p_len = request->len;
1545 } else {
1546 /* Cleanup */
1547 if(buffer)
1548 kfree(buffer);
1549 }
1550
1551 return ret;
1552}
1553
1554/* ---------------------------------------------------------------- */
1555/*
1556 * Wrapper to call a private Wireless Extension SET handler.
1557 * Same as above...
1558 * It's not as nice and slimline as the standard wrapper. The cause
1559 * is struct iw_priv_args, which was not really designed for the
1560 * job we are going here.
1561 *
1562 * IMPORTANT : This function prevent to set and get data on the same
1563 * IOCTL and enforce the SET/GET convention. Not doing it would be
1564 * far too hairy...
1565 * If you need to set and get data at the same time, please don't use
1566 * a iw_handler but process it in your ioctl handler (i.e. use the
1567 * old driver API).
1568 */
1569static inline int rtnetlink_private_set(struct net_device * dev,
1570 struct iw_event * request,
1571 int request_len,
1572 iw_handler handler)
1573{
1574 const struct iw_priv_args * descr = NULL;
1575 unsigned int cmd;
1576 union iwreq_data * wrqu;
1577 union iwreq_data wrqu_point;
1578 int hdr_len;
1579 char * extra = NULL;
1580 int extra_size = 0;
1581 int offset = 0; /* For sub-ioctls */
1582 struct iw_request_info info;
1583 int i;
1584 int ret = -EINVAL;
1585
1586 /* Get the description of the Request */
1587 cmd = request->cmd;
1588 for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
1589 if(cmd == dev->wireless_handlers->private_args[i].cmd) {
1590 descr = &(dev->wireless_handlers->private_args[i]);
1591 break;
1592 }
1593 if(descr == NULL)
1594 return -EOPNOTSUPP;
1595
1596#ifdef WE_RTNETLINK_DEBUG
1597 printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
1598 ifr->ifr_name, cmd);
1599 printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
1600 dev->name, descr->name, descr->set_args, descr->get_args);
1601#endif /* WE_RTNETLINK_DEBUG */
1602
1603 /* Compute the size of the set arguments */
1604 /* Check for sub-ioctl handler */
1605 if(descr->name[0] == '\0')
1606 /* Reserve one int for sub-ioctl index */
1607 offset = sizeof(__u32);
1608
1609 /* Size of set arguments */
1610 extra_size = get_priv_size(descr->set_args);
1611
1612 /* Does it fits in wrqu ? */
1613 if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
1614 (extra_size <= IFNAMSIZ)) {
1615 hdr_len = IW_EV_LCP_LEN + extra_size;
1616 extra_size = 0;
1617 } else {
1618 hdr_len = IW_EV_POINT_LEN;
1619 }
1620
1621 /* Extract fixed header from request. This is properly aligned. */
1622 wrqu = &request->u;
1623
1624 /* Check if wrqu is complete */
1625 if(request_len < hdr_len) {
1626#ifdef WE_RTNETLINK_DEBUG
1627 printk(KERN_DEBUG
1628 "%s (WE.r) : Wireless request too short (%d)\n",
1629 dev->name, request_len);
1630#endif /* WE_RTNETLINK_DEBUG */
1631 return -EINVAL;
1632 }
1633
1634 /* Prepare the call */
1635 info.cmd = cmd;
1636 info.flags = 0;
1637
1638 /* Check if we have a pointer to user space data or not. */
1639 if(extra_size == 0) {
1640
1641 /* No extra arguments. Trivial to handle */
1642 ret = handler(dev, &info, wrqu, (char *) wrqu);
1643
1644 } else {
1645 int extra_len;
1646
1647 /* Put wrqu in the right place (skip pointer) */
1648 memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
1649 wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
1650
1651 /* Does it fits within bounds ? */
1652 if(wrqu_point.data.length > (descr->set_args &
1653 IW_PRIV_SIZE_MASK))
1654 return -E2BIG;
1655
1656 /* Real length of payload */
1657 extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
1658
1659 /* Check if request is self consistent */
1660 if((request_len - hdr_len) < extra_len) {
1661#ifdef WE_RTNETLINK_DEBUG
1662 printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
1663 dev->name, extra_size);
1664#endif /* WE_RTNETLINK_DEBUG */
1665 return -EINVAL;
1666 }
1667
1668#ifdef WE_RTNETLINK_DEBUG
1669 printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
1670 dev->name, extra_size);
1671#endif /* WE_RTNETLINK_DEBUG */
1672
1673 /* Always allocate for max space. Easier, and won't last
1674 * long... */
1675 extra = kmalloc(extra_size, GFP_KERNEL);
1676 if (extra == NULL)
1677 return -ENOMEM;
1678
1679 /* Copy extra in aligned buffer */
1680 memcpy(extra, ((char *) request) + hdr_len, extra_len);
1681
1682 /* Call the handler */
1683 ret = handler(dev, &info, &wrqu_point, extra);
1684
1685 /* Cleanup - I told you it wasn't that long ;-) */
1686 kfree(extra);
1687 }
1688
1689 /* Call commit handler if needed and defined */
1690 if(ret == -EIWCOMMIT)
1691 ret = call_commit_handler(dev);
1692
1693 return ret;
1694}
1695
1696/* ---------------------------------------------------------------- */
1697/*
1698 * Main RtNetlink dispatcher. Called from the main networking code
1699 * (do_getlink() in net/core/rtnetlink.c).
1700 * Check the type of Request and call the appropriate wrapper...
1701 */
1702int wireless_rtnetlink_get(struct net_device * dev,
1703 char * data,
1704 int len,
1705 char ** p_buf,
1706 int * p_len)
1707{
1708 struct iw_event * request = (struct iw_event *) data;
1709 iw_handler handler;
1710
1711 /* Check length */
1712 if(len < IW_EV_LCP_LEN) {
1713 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
1714 dev->name, len);
1715 return -EINVAL;
1716 }
1717
1718 /* ReCheck length (len may have padding) */
1719 if(request->len > len) {
1720 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
1721 dev->name, request->len, len);
1722 return -EINVAL;
1723 }
1724
1725 /* Only accept GET requests in here */
1726 if(!IW_IS_GET(request->cmd))
1727 return -EOPNOTSUPP;
1728
1729 /* Special cases */
1730 if(request->cmd == SIOCGIWSTATS)
1731 /* Get Wireless Stats */
1732 return rtnetlink_standard_get(dev,
1733 request,
1734 request->len,
1735 &iw_handler_get_iwstats,
1736 p_buf, p_len);
1737 if(request->cmd == SIOCGIWPRIV) {
1738 /* Check if we have some wireless handlers defined */
1739 if(dev->wireless_handlers == NULL)
1740 return -EOPNOTSUPP;
1741 /* Get Wireless Stats */
1742 return rtnetlink_standard_get(dev,
1743 request,
1744 request->len,
1745 &iw_handler_get_private,
1746 p_buf, p_len);
1747 }
1748
1749 /* Basic check */
1750 if (!netif_device_present(dev))
1751 return -ENODEV;
1752
1753 /* Try to find the handler */
1754 handler = get_handler(dev, request->cmd);
1755 if(handler != NULL) {
1756 /* Standard and private are not the same */
1757 if(request->cmd < SIOCIWFIRSTPRIV)
1758 return rtnetlink_standard_get(dev,
1759 request,
1760 request->len,
1761 handler,
1762 p_buf, p_len);
1763 else
1764 return rtnetlink_private_get(dev,
1765 request,
1766 request->len,
1767 handler,
1768 p_buf, p_len);
1769 }
1770
1771 return -EOPNOTSUPP;
1772}
1773
1774/* ---------------------------------------------------------------- */
1775/*
1776 * Main RtNetlink dispatcher. Called from the main networking code
1777 * (do_setlink() in net/core/rtnetlink.c).
1778 * Check the type of Request and call the appropriate wrapper...
1779 */
1780int wireless_rtnetlink_set(struct net_device * dev,
1781 char * data,
1782 int len)
1783{
1784 struct iw_event * request = (struct iw_event *) data;
1785 iw_handler handler;
1786
1787 /* Check length */
1788 if(len < IW_EV_LCP_LEN) {
1789 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
1790 dev->name, len);
1791 return -EINVAL;
1792 }
1793
1794 /* ReCheck length (len may have padding) */
1795 if(request->len > len) {
1796 printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
1797 dev->name, request->len, len);
1798 return -EINVAL;
1799 }
1800
1801 /* Only accept SET requests in here */
1802 if(!IW_IS_SET(request->cmd))
1803 return -EOPNOTSUPP;
1804
1805 /* Basic check */
1806 if (!netif_device_present(dev))
1807 return -ENODEV;
1808
1809 /* New driver API : try to find the handler */
1810 handler = get_handler(dev, request->cmd);
1811 if(handler != NULL) {
1812 /* Standard and private are not the same */
1813 if(request->cmd < SIOCIWFIRSTPRIV)
1814 return rtnetlink_standard_set(dev,
1815 request,
1816 request->len,
1817 handler);
1818 else
1819 return rtnetlink_private_set(dev,
1820 request,
1821 request->len,
1822 handler);
1823 }
1824
1825 return -EOPNOTSUPP;
1826}
1827#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
1828
1829
1091/************************* EVENT PROCESSING *************************/ 1830/************************* EVENT PROCESSING *************************/
1092/* 1831/*
1093 * Process events generated by the wireless layer or the driver. 1832 * Process events generated by the wireless layer or the driver.
1094 * Most often, the event will be propagated through rtnetlink 1833 * Most often, the event will be propagated through rtnetlink
1095 */ 1834 */
1096 1835
1097#ifdef WE_EVENT_NETLINK 1836#ifdef WE_EVENT_RTNETLINK
1098/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here.
1099 * It is declared in <linux/rtnetlink.h> */
1100
1101/* ---------------------------------------------------------------- */ 1837/* ---------------------------------------------------------------- */
1102/* 1838/*
1103 * Fill a rtnetlink message with our event data. 1839 * Fill a rtnetlink message with our event data.
@@ -1121,12 +1857,11 @@ static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb,
1121 r->__ifi_pad = 0; 1857 r->__ifi_pad = 0;
1122 r->ifi_type = dev->type; 1858 r->ifi_type = dev->type;
1123 r->ifi_index = dev->ifindex; 1859 r->ifi_index = dev->ifindex;
1124 r->ifi_flags = dev->flags; 1860 r->ifi_flags = dev_get_flags(dev);
1125 r->ifi_change = 0; /* Wireless changes don't affect those flags */ 1861 r->ifi_change = 0; /* Wireless changes don't affect those flags */
1126 1862
1127 /* Add the wireless events in the netlink packet */ 1863 /* Add the wireless events in the netlink packet */
1128 RTA_PUT(skb, IFLA_WIRELESS, 1864 RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
1129 event_len, event);
1130 1865
1131 nlh->nlmsg_len = skb->tail - b; 1866 nlh->nlmsg_len = skb->tail - b;
1132 return skb->len; 1867 return skb->len;
@@ -1163,7 +1898,7 @@ static inline void rtmsg_iwinfo(struct net_device * dev,
1163 NETLINK_CB(skb).dst_group = RTNLGRP_LINK; 1898 NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
1164 netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC); 1899 netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
1165} 1900}
1166#endif /* WE_EVENT_NETLINK */ 1901#endif /* WE_EVENT_RTNETLINK */
1167 1902
1168/* ---------------------------------------------------------------- */ 1903/* ---------------------------------------------------------------- */
1169/* 1904/*
@@ -1255,10 +1990,10 @@ void wireless_send_event(struct net_device * dev,
1255 if(extra != NULL) 1990 if(extra != NULL)
1256 memcpy(((char *) event) + hdr_len, extra, extra_len); 1991 memcpy(((char *) event) + hdr_len, extra, extra_len);
1257 1992
1258#ifdef WE_EVENT_NETLINK 1993#ifdef WE_EVENT_RTNETLINK
1259 /* rtnetlink event channel */ 1994 /* Send via the RtNetlink event channel */
1260 rtmsg_iwinfo(dev, (char *) event, event_len); 1995 rtmsg_iwinfo(dev, (char *) event, event_len);
1261#endif /* WE_EVENT_NETLINK */ 1996#endif /* WE_EVENT_RTNETLINK */
1262 1997
1263 /* Cleanup */ 1998 /* Cleanup */
1264 kfree(event); 1999 kfree(event);