diff options
Diffstat (limited to 'net/wireless/wext.c')
-rw-r--r-- | net/wireless/wext.c | 312 |
1 files changed, 217 insertions, 95 deletions
diff --git a/net/wireless/wext.c b/net/wireless/wext.c index cb6a5bb85d80..5b4a0cee4418 100644 --- a/net/wireless/wext.c +++ b/net/wireless/wext.c | |||
@@ -417,6 +417,21 @@ static const int event_type_size[] = { | |||
417 | IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ | 417 | IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ |
418 | }; | 418 | }; |
419 | 419 | ||
420 | #ifdef CONFIG_COMPAT | ||
421 | static const int compat_event_type_size[] = { | ||
422 | IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ | ||
423 | 0, | ||
424 | IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ | ||
425 | 0, | ||
426 | IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ | ||
427 | IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ | ||
428 | IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ | ||
429 | 0, | ||
430 | IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ | ||
431 | IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ | ||
432 | IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ | ||
433 | }; | ||
434 | #endif | ||
420 | 435 | ||
421 | /************************ COMMON SUBROUTINES ************************/ | 436 | /************************ COMMON SUBROUTINES ************************/ |
422 | /* | 437 | /* |
@@ -610,6 +625,11 @@ static void wireless_seq_printf_stats(struct seq_file *seq, | |||
610 | { | 625 | { |
611 | /* Get stats from the driver */ | 626 | /* Get stats from the driver */ |
612 | struct iw_statistics *stats = get_wireless_stats(dev); | 627 | struct iw_statistics *stats = get_wireless_stats(dev); |
628 | static struct iw_statistics nullstats = {}; | ||
629 | |||
630 | /* show device if it's wireless regardless of current stats */ | ||
631 | if (!stats && dev->wireless_handlers) | ||
632 | stats = &nullstats; | ||
613 | 633 | ||
614 | if (stats) { | 634 | if (stats) { |
615 | seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " | 635 | seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " |
@@ -628,7 +648,9 @@ static void wireless_seq_printf_stats(struct seq_file *seq, | |||
628 | stats->discard.nwid, stats->discard.code, | 648 | stats->discard.nwid, stats->discard.code, |
629 | stats->discard.fragment, stats->discard.retries, | 649 | stats->discard.fragment, stats->discard.retries, |
630 | stats->discard.misc, stats->miss.beacon); | 650 | stats->discard.misc, stats->miss.beacon); |
631 | stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; | 651 | |
652 | if (stats != &nullstats) | ||
653 | stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; | ||
632 | } | 654 | } |
633 | } | 655 | } |
634 | 656 | ||
@@ -636,8 +658,10 @@ static void wireless_seq_printf_stats(struct seq_file *seq, | |||
636 | /* | 658 | /* |
637 | * Print info for /proc/net/wireless (print all entries) | 659 | * Print info for /proc/net/wireless (print all entries) |
638 | */ | 660 | */ |
639 | static int wireless_seq_show(struct seq_file *seq, void *v) | 661 | static int wireless_dev_seq_show(struct seq_file *seq, void *v) |
640 | { | 662 | { |
663 | might_sleep(); | ||
664 | |||
641 | if (v == SEQ_START_TOKEN) | 665 | if (v == SEQ_START_TOKEN) |
642 | seq_printf(seq, "Inter-| sta-| Quality | Discarded " | 666 | seq_printf(seq, "Inter-| sta-| Quality | Discarded " |
643 | "packets | Missed | WE\n" | 667 | "packets | Missed | WE\n" |
@@ -649,14 +673,46 @@ static int wireless_seq_show(struct seq_file *seq, void *v) | |||
649 | return 0; | 673 | return 0; |
650 | } | 674 | } |
651 | 675 | ||
676 | static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) | ||
677 | { | ||
678 | struct net *net = seq_file_net(seq); | ||
679 | loff_t off; | ||
680 | struct net_device *dev; | ||
681 | |||
682 | rtnl_lock(); | ||
683 | if (!*pos) | ||
684 | return SEQ_START_TOKEN; | ||
685 | |||
686 | off = 1; | ||
687 | for_each_netdev(net, dev) | ||
688 | if (off++ == *pos) | ||
689 | return dev; | ||
690 | return NULL; | ||
691 | } | ||
692 | |||
693 | static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
694 | { | ||
695 | struct net *net = seq_file_net(seq); | ||
696 | |||
697 | ++*pos; | ||
698 | |||
699 | return v == SEQ_START_TOKEN ? | ||
700 | first_net_device(net) : next_net_device(v); | ||
701 | } | ||
702 | |||
703 | static void wireless_dev_seq_stop(struct seq_file *seq, void *v) | ||
704 | { | ||
705 | rtnl_unlock(); | ||
706 | } | ||
707 | |||
652 | static const struct seq_operations wireless_seq_ops = { | 708 | static const struct seq_operations wireless_seq_ops = { |
653 | .start = dev_seq_start, | 709 | .start = wireless_dev_seq_start, |
654 | .next = dev_seq_next, | 710 | .next = wireless_dev_seq_next, |
655 | .stop = dev_seq_stop, | 711 | .stop = wireless_dev_seq_stop, |
656 | .show = wireless_seq_show, | 712 | .show = wireless_dev_seq_show, |
657 | }; | 713 | }; |
658 | 714 | ||
659 | static int wireless_seq_open(struct inode *inode, struct file *file) | 715 | static int seq_open_wireless(struct inode *inode, struct file *file) |
660 | { | 716 | { |
661 | return seq_open_net(inode, file, &wireless_seq_ops, | 717 | return seq_open_net(inode, file, &wireless_seq_ops, |
662 | sizeof(struct seq_net_private)); | 718 | sizeof(struct seq_net_private)); |
@@ -664,7 +720,7 @@ static int wireless_seq_open(struct inode *inode, struct file *file) | |||
664 | 720 | ||
665 | static const struct file_operations wireless_seq_fops = { | 721 | static const struct file_operations wireless_seq_fops = { |
666 | .owner = THIS_MODULE, | 722 | .owner = THIS_MODULE, |
667 | .open = wireless_seq_open, | 723 | .open = seq_open_wireless, |
668 | .read = seq_read, | 724 | .read = seq_read, |
669 | .llseek = seq_lseek, | 725 | .llseek = seq_lseek, |
670 | .release = seq_release_net, | 726 | .release = seq_release_net, |
@@ -786,6 +842,13 @@ static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, | |||
786 | err = -EFAULT; | 842 | err = -EFAULT; |
787 | goto out; | 843 | goto out; |
788 | } | 844 | } |
845 | |||
846 | if (cmd == SIOCSIWENCODEEXT) { | ||
847 | struct iw_encode_ext *ee = (void *) extra; | ||
848 | |||
849 | if (iwp->length < sizeof(*ee) + ee->key_len) | ||
850 | return -EFAULT; | ||
851 | } | ||
789 | } | 852 | } |
790 | 853 | ||
791 | err = handler(dev, info, (union iwreq_data *) iwp, extra); | 854 | err = handler(dev, info, (union iwreq_data *) iwp, extra); |
@@ -1209,65 +1272,57 @@ int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, | |||
1209 | } | 1272 | } |
1210 | #endif | 1273 | #endif |
1211 | 1274 | ||
1212 | /************************* EVENT PROCESSING *************************/ | 1275 | static int __net_init wext_pernet_init(struct net *net) |
1213 | /* | 1276 | { |
1214 | * Process events generated by the wireless layer or the driver. | 1277 | skb_queue_head_init(&net->wext_nlevents); |
1215 | * Most often, the event will be propagated through rtnetlink | 1278 | return 0; |
1216 | */ | 1279 | } |
1217 | 1280 | ||
1218 | /* ---------------------------------------------------------------- */ | 1281 | static void __net_exit wext_pernet_exit(struct net *net) |
1219 | /* | 1282 | { |
1220 | * Locking... | 1283 | skb_queue_purge(&net->wext_nlevents); |
1221 | * ---------- | 1284 | } |
1222 | * | ||
1223 | * Thanks to Herbert Xu <herbert@gondor.apana.org.au> for fixing | ||
1224 | * the locking issue in here and implementing this code ! | ||
1225 | * | ||
1226 | * The issue : wireless_send_event() is often called in interrupt context, | ||
1227 | * while the Netlink layer can never be called in interrupt context. | ||
1228 | * The fully formed RtNetlink events are queued, and then a tasklet is run | ||
1229 | * to feed those to Netlink. | ||
1230 | * The skb_queue is interrupt safe, and its lock is not held while calling | ||
1231 | * Netlink, so there is no possibility of dealock. | ||
1232 | * Jean II | ||
1233 | */ | ||
1234 | 1285 | ||
1235 | static struct sk_buff_head wireless_nlevent_queue; | 1286 | static struct pernet_operations wext_pernet_ops = { |
1287 | .init = wext_pernet_init, | ||
1288 | .exit = wext_pernet_exit, | ||
1289 | }; | ||
1236 | 1290 | ||
1237 | static int __init wireless_nlevent_init(void) | 1291 | static int __init wireless_nlevent_init(void) |
1238 | { | 1292 | { |
1239 | skb_queue_head_init(&wireless_nlevent_queue); | 1293 | return register_pernet_subsys(&wext_pernet_ops); |
1240 | return 0; | ||
1241 | } | 1294 | } |
1242 | 1295 | ||
1243 | subsys_initcall(wireless_nlevent_init); | 1296 | subsys_initcall(wireless_nlevent_init); |
1244 | 1297 | ||
1245 | static void wireless_nlevent_process(unsigned long data) | 1298 | /* Process events generated by the wireless layer or the driver. */ |
1299 | static void wireless_nlevent_process(struct work_struct *work) | ||
1246 | { | 1300 | { |
1247 | struct sk_buff *skb; | 1301 | struct sk_buff *skb; |
1302 | struct net *net; | ||
1303 | |||
1304 | rtnl_lock(); | ||
1248 | 1305 | ||
1249 | while ((skb = skb_dequeue(&wireless_nlevent_queue))) | 1306 | for_each_net(net) { |
1250 | rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); | 1307 | while ((skb = skb_dequeue(&net->wext_nlevents))) |
1308 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, | ||
1309 | GFP_KERNEL); | ||
1310 | } | ||
1311 | |||
1312 | rtnl_unlock(); | ||
1251 | } | 1313 | } |
1252 | 1314 | ||
1253 | static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); | 1315 | static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); |
1254 | 1316 | ||
1255 | /* ---------------------------------------------------------------- */ | 1317 | static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, |
1256 | /* | 1318 | struct sk_buff *skb) |
1257 | * Fill a rtnetlink message with our event data. | ||
1258 | * Note that we propage only the specified event and don't dump the | ||
1259 | * current wireless config. Dumping the wireless config is far too | ||
1260 | * expensive (for each parameter, the driver need to query the hardware). | ||
1261 | */ | ||
1262 | static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, | ||
1263 | int type, char *event, int event_len) | ||
1264 | { | 1319 | { |
1265 | struct ifinfomsg *r; | 1320 | struct ifinfomsg *r; |
1266 | struct nlmsghdr *nlh; | 1321 | struct nlmsghdr *nlh; |
1267 | 1322 | ||
1268 | nlh = nlmsg_put(skb, 0, 0, type, sizeof(*r), 0); | 1323 | nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); |
1269 | if (nlh == NULL) | 1324 | if (!nlh) |
1270 | return -EMSGSIZE; | 1325 | return NULL; |
1271 | 1326 | ||
1272 | r = nlmsg_data(nlh); | 1327 | r = nlmsg_data(nlh); |
1273 | r->ifi_family = AF_UNSPEC; | 1328 | r->ifi_family = AF_UNSPEC; |
@@ -1278,48 +1333,14 @@ static int rtnetlink_fill_iwinfo(struct sk_buff *skb, struct net_device *dev, | |||
1278 | r->ifi_change = 0; /* Wireless changes don't affect those flags */ | 1333 | r->ifi_change = 0; /* Wireless changes don't affect those flags */ |
1279 | 1334 | ||
1280 | NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); | 1335 | NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); |
1281 | /* Add the wireless events in the netlink packet */ | ||
1282 | NLA_PUT(skb, IFLA_WIRELESS, event_len, event); | ||
1283 | 1336 | ||
1284 | return nlmsg_end(skb, nlh); | 1337 | return nlh; |
1285 | 1338 | nla_put_failure: | |
1286 | nla_put_failure: | ||
1287 | nlmsg_cancel(skb, nlh); | 1339 | nlmsg_cancel(skb, nlh); |
1288 | return -EMSGSIZE; | 1340 | return NULL; |
1289 | } | 1341 | } |
1290 | 1342 | ||
1291 | /* ---------------------------------------------------------------- */ | ||
1292 | /* | ||
1293 | * Create and broadcast and send it on the standard rtnetlink socket | ||
1294 | * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c | ||
1295 | * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field | ||
1296 | * within a RTM_NEWLINK event. | ||
1297 | */ | ||
1298 | static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | ||
1299 | { | ||
1300 | struct sk_buff *skb; | ||
1301 | int err; | ||
1302 | |||
1303 | if (!net_eq(dev_net(dev), &init_net)) | ||
1304 | return; | ||
1305 | |||
1306 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
1307 | if (!skb) | ||
1308 | return; | ||
1309 | |||
1310 | err = rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, event, event_len); | ||
1311 | if (err < 0) { | ||
1312 | WARN_ON(err == -EMSGSIZE); | ||
1313 | kfree_skb(skb); | ||
1314 | return; | ||
1315 | } | ||
1316 | 1343 | ||
1317 | NETLINK_CB(skb).dst_group = RTNLGRP_LINK; | ||
1318 | skb_queue_tail(&wireless_nlevent_queue, skb); | ||
1319 | tasklet_schedule(&wireless_nlevent_tasklet); | ||
1320 | } | ||
1321 | |||
1322 | /* ---------------------------------------------------------------- */ | ||
1323 | /* | 1344 | /* |
1324 | * Main event dispatcher. Called from other parts and drivers. | 1345 | * Main event dispatcher. Called from other parts and drivers. |
1325 | * Send the event on the appropriate channels. | 1346 | * Send the event on the appropriate channels. |
@@ -1328,7 +1349,7 @@ static void rtmsg_iwinfo(struct net_device *dev, char *event, int event_len) | |||
1328 | void wireless_send_event(struct net_device * dev, | 1349 | void wireless_send_event(struct net_device * dev, |
1329 | unsigned int cmd, | 1350 | unsigned int cmd, |
1330 | union iwreq_data * wrqu, | 1351 | union iwreq_data * wrqu, |
1331 | char * extra) | 1352 | const char * extra) |
1332 | { | 1353 | { |
1333 | const struct iw_ioctl_description * descr = NULL; | 1354 | const struct iw_ioctl_description * descr = NULL; |
1334 | int extra_len = 0; | 1355 | int extra_len = 0; |
@@ -1338,6 +1359,25 @@ void wireless_send_event(struct net_device * dev, | |||
1338 | int wrqu_off = 0; /* Offset in wrqu */ | 1359 | int wrqu_off = 0; /* Offset in wrqu */ |
1339 | /* Don't "optimise" the following variable, it will crash */ | 1360 | /* Don't "optimise" the following variable, it will crash */ |
1340 | unsigned cmd_index; /* *MUST* be unsigned */ | 1361 | unsigned cmd_index; /* *MUST* be unsigned */ |
1362 | struct sk_buff *skb; | ||
1363 | struct nlmsghdr *nlh; | ||
1364 | struct nlattr *nla; | ||
1365 | #ifdef CONFIG_COMPAT | ||
1366 | struct __compat_iw_event *compat_event; | ||
1367 | struct compat_iw_point compat_wrqu; | ||
1368 | struct sk_buff *compskb; | ||
1369 | #endif | ||
1370 | |||
1371 | /* | ||
1372 | * Nothing in the kernel sends scan events with data, be safe. | ||
1373 | * This is necessary because we cannot fix up scan event data | ||
1374 | * for compat, due to being contained in 'extra', but normally | ||
1375 | * applications are required to retrieve the scan data anyway | ||
1376 | * and no data is included in the event, this codifies that | ||
1377 | * practice. | ||
1378 | */ | ||
1379 | if (WARN_ON(cmd == SIOCGIWSCAN && extra)) | ||
1380 | extra = NULL; | ||
1341 | 1381 | ||
1342 | /* Get the description of the Event */ | 1382 | /* Get the description of the Event */ |
1343 | if (cmd <= SIOCIWLAST) { | 1383 | if (cmd <= SIOCIWLAST) { |
@@ -1385,25 +1425,107 @@ void wireless_send_event(struct net_device * dev, | |||
1385 | hdr_len = event_type_size[descr->header_type]; | 1425 | hdr_len = event_type_size[descr->header_type]; |
1386 | event_len = hdr_len + extra_len; | 1426 | event_len = hdr_len + extra_len; |
1387 | 1427 | ||
1388 | /* Create temporary buffer to hold the event */ | 1428 | /* |
1389 | event = kmalloc(event_len, GFP_ATOMIC); | 1429 | * The problem for 64/32 bit. |
1390 | if (event == NULL) | 1430 | * |
1431 | * On 64-bit, a regular event is laid out as follows: | ||
1432 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | ||
1433 | * | event.len | event.cmd | p a d d i n g | | ||
1434 | * | wrqu data ... (with the correct size) | | ||
1435 | * | ||
1436 | * This padding exists because we manipulate event->u, | ||
1437 | * and 'event' is not packed. | ||
1438 | * | ||
1439 | * An iw_point event is laid out like this instead: | ||
1440 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | ||
1441 | * | event.len | event.cmd | p a d d i n g | | ||
1442 | * | iwpnt.len | iwpnt.flg | p a d d i n g | | ||
1443 | * | extra data ... | ||
1444 | * | ||
1445 | * The second padding exists because struct iw_point is extended, | ||
1446 | * but this depends on the platform... | ||
1447 | * | ||
1448 | * On 32-bit, all the padding shouldn't be there. | ||
1449 | */ | ||
1450 | |||
1451 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
1452 | if (!skb) | ||
1391 | return; | 1453 | return; |
1392 | 1454 | ||
1393 | /* Fill event */ | 1455 | /* Send via the RtNetlink event channel */ |
1456 | nlh = rtnetlink_ifinfo_prep(dev, skb); | ||
1457 | if (WARN_ON(!nlh)) { | ||
1458 | kfree_skb(skb); | ||
1459 | return; | ||
1460 | } | ||
1461 | |||
1462 | /* Add the wireless events in the netlink packet */ | ||
1463 | nla = nla_reserve(skb, IFLA_WIRELESS, event_len); | ||
1464 | if (!nla) { | ||
1465 | kfree_skb(skb); | ||
1466 | return; | ||
1467 | } | ||
1468 | event = nla_data(nla); | ||
1469 | |||
1470 | /* Fill event - first clear to avoid data leaking */ | ||
1471 | memset(event, 0, hdr_len); | ||
1394 | event->len = event_len; | 1472 | event->len = event_len; |
1395 | event->cmd = cmd; | 1473 | event->cmd = cmd; |
1396 | memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); | 1474 | memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); |
1397 | if (extra) | 1475 | if (extra_len) |
1398 | memcpy(((char *) event) + hdr_len, extra, extra_len); | 1476 | memcpy(((char *) event) + hdr_len, extra, extra_len); |
1399 | 1477 | ||
1478 | nlmsg_end(skb, nlh); | ||
1479 | #ifdef CONFIG_COMPAT | ||
1480 | hdr_len = compat_event_type_size[descr->header_type]; | ||
1481 | event_len = hdr_len + extra_len; | ||
1482 | |||
1483 | compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | ||
1484 | if (!compskb) { | ||
1485 | kfree_skb(skb); | ||
1486 | return; | ||
1487 | } | ||
1488 | |||
1400 | /* Send via the RtNetlink event channel */ | 1489 | /* Send via the RtNetlink event channel */ |
1401 | rtmsg_iwinfo(dev, (char *) event, event_len); | 1490 | nlh = rtnetlink_ifinfo_prep(dev, compskb); |
1491 | if (WARN_ON(!nlh)) { | ||
1492 | kfree_skb(skb); | ||
1493 | kfree_skb(compskb); | ||
1494 | return; | ||
1495 | } | ||
1496 | |||
1497 | /* Add the wireless events in the netlink packet */ | ||
1498 | nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); | ||
1499 | if (!nla) { | ||
1500 | kfree_skb(skb); | ||
1501 | kfree_skb(compskb); | ||
1502 | return; | ||
1503 | } | ||
1504 | compat_event = nla_data(nla); | ||
1402 | 1505 | ||
1403 | /* Cleanup */ | 1506 | compat_event->len = event_len; |
1404 | kfree(event); | 1507 | compat_event->cmd = cmd; |
1508 | if (descr->header_type == IW_HEADER_TYPE_POINT) { | ||
1509 | compat_wrqu.length = wrqu->data.length; | ||
1510 | compat_wrqu.flags = wrqu->data.flags; | ||
1511 | memcpy(&compat_event->pointer, | ||
1512 | ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, | ||
1513 | hdr_len - IW_EV_COMPAT_LCP_LEN); | ||
1514 | if (extra_len) | ||
1515 | memcpy(((char *) compat_event) + hdr_len, | ||
1516 | extra, extra_len); | ||
1517 | } else { | ||
1518 | /* extra_len must be zero, so no if (extra) needed */ | ||
1519 | memcpy(&compat_event->pointer, wrqu, | ||
1520 | hdr_len - IW_EV_COMPAT_LCP_LEN); | ||
1521 | } | ||
1522 | |||
1523 | nlmsg_end(compskb, nlh); | ||
1405 | 1524 | ||
1406 | return; /* Always success, I guess ;-) */ | 1525 | skb_shinfo(skb)->frag_list = compskb; |
1526 | #endif | ||
1527 | skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); | ||
1528 | schedule_work(&wireless_nlevent_work); | ||
1407 | } | 1529 | } |
1408 | EXPORT_SYMBOL(wireless_send_event); | 1530 | EXPORT_SYMBOL(wireless_send_event); |
1409 | 1531 | ||