Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-drm-fsl-dcu.git] / net / mac802154 / iface.c
1 /*
2  * Copyright 2007-2012 Siemens AG
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * Written by:
14  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
15  * Sergey Lapin <slapin@ossfans.org>
16  * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
17  * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
18  */
19
20 #include <linux/netdevice.h>
21 #include <linux/module.h>
22 #include <linux/if_arp.h>
23 #include <linux/ieee802154.h>
24
25 #include <net/nl802154.h>
26 #include <net/mac802154.h>
27 #include <net/ieee802154_netdev.h>
28 #include <net/cfg802154.h>
29
30 #include "ieee802154_i.h"
31 #include "driver-ops.h"
32
33 int mac802154_wpan_update_llsec(struct net_device *dev)
34 {
35         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
36         struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
37         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
38         int rc = 0;
39
40         if (ops->llsec) {
41                 struct ieee802154_llsec_params params;
42                 int changed = 0;
43
44                 params.pan_id = wpan_dev->pan_id;
45                 changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
46
47                 params.hwaddr = wpan_dev->extended_addr;
48                 changed |= IEEE802154_LLSEC_PARAM_HWADDR;
49
50                 rc = ops->llsec->set_params(dev, &params, changed);
51         }
52
53         return rc;
54 }
55
56 static int
57 mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
58 {
59         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
60         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
61         struct sockaddr_ieee802154 *sa =
62                 (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
63         int err = -ENOIOCTLCMD;
64
65         if (cmd != SIOCGIFADDR && cmd != SIOCSIFADDR)
66                 return err;
67
68         rtnl_lock();
69
70         switch (cmd) {
71         case SIOCGIFADDR:
72         {
73                 u16 pan_id, short_addr;
74
75                 pan_id = le16_to_cpu(wpan_dev->pan_id);
76                 short_addr = le16_to_cpu(wpan_dev->short_addr);
77                 if (pan_id == IEEE802154_PANID_BROADCAST ||
78                     short_addr == IEEE802154_ADDR_BROADCAST) {
79                         err = -EADDRNOTAVAIL;
80                         break;
81                 }
82
83                 sa->family = AF_IEEE802154;
84                 sa->addr.addr_type = IEEE802154_ADDR_SHORT;
85                 sa->addr.pan_id = pan_id;
86                 sa->addr.short_addr = short_addr;
87
88                 err = 0;
89                 break;
90         }
91         case SIOCSIFADDR:
92                 if (netif_running(dev)) {
93                         rtnl_unlock();
94                         return -EBUSY;
95                 }
96
97                 dev_warn(&dev->dev,
98                          "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
99                 if (sa->family != AF_IEEE802154 ||
100                     sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
101                     sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
102                     sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
103                     sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
104                         err = -EINVAL;
105                         break;
106                 }
107
108                 wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
109                 wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
110
111                 err = mac802154_wpan_update_llsec(dev);
112                 break;
113         }
114
115         rtnl_unlock();
116         return err;
117 }
118
119 static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
120 {
121         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
122         struct sockaddr *addr = p;
123         __le64 extended_addr;
124
125         if (netif_running(dev))
126                 return -EBUSY;
127
128         /* lowpan need to be down for update
129          * SLAAC address after ifup
130          */
131         if (sdata->wpan_dev.lowpan_dev) {
132                 if (netif_running(sdata->wpan_dev.lowpan_dev))
133                         return -EBUSY;
134         }
135
136         ieee802154_be64_to_le64(&extended_addr, addr->sa_data);
137         if (!ieee802154_is_valid_extended_unicast_addr(extended_addr))
138                 return -EINVAL;
139
140         memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
141         sdata->wpan_dev.extended_addr = extended_addr;
142
143         /* update lowpan interface mac address when
144          * wpan mac has been changed
145          */
146         if (sdata->wpan_dev.lowpan_dev)
147                 memcpy(sdata->wpan_dev.lowpan_dev->dev_addr, dev->dev_addr,
148                        dev->addr_len);
149
150         return mac802154_wpan_update_llsec(dev);
151 }
152
153 static int ieee802154_setup_hw(struct ieee802154_sub_if_data *sdata)
154 {
155         struct ieee802154_local *local = sdata->local;
156         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
157         int ret;
158
159         if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
160                 ret = drv_set_promiscuous_mode(local,
161                                                wpan_dev->promiscuous_mode);
162                 if (ret < 0)
163                         return ret;
164         }
165
166         if (local->hw.flags & IEEE802154_HW_AFILT) {
167                 ret = drv_set_pan_id(local, wpan_dev->pan_id);
168                 if (ret < 0)
169                         return ret;
170
171                 ret = drv_set_extended_addr(local, wpan_dev->extended_addr);
172                 if (ret < 0)
173                         return ret;
174
175                 ret = drv_set_short_addr(local, wpan_dev->short_addr);
176                 if (ret < 0)
177                         return ret;
178         }
179
180         if (local->hw.flags & IEEE802154_HW_LBT) {
181                 ret = drv_set_lbt_mode(local, wpan_dev->lbt);
182                 if (ret < 0)
183                         return ret;
184         }
185
186         if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
187                 ret = drv_set_csma_params(local, wpan_dev->min_be,
188                                           wpan_dev->max_be,
189                                           wpan_dev->csma_retries);
190                 if (ret < 0)
191                         return ret;
192         }
193
194         if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
195                 ret = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
196                 if (ret < 0)
197                         return ret;
198         }
199
200         return 0;
201 }
202
203 static int mac802154_slave_open(struct net_device *dev)
204 {
205         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
206         struct ieee802154_local *local = sdata->local;
207         int res;
208
209         ASSERT_RTNL();
210
211         set_bit(SDATA_STATE_RUNNING, &sdata->state);
212
213         if (!local->open_count) {
214                 res = ieee802154_setup_hw(sdata);
215                 if (res)
216                         goto err;
217
218                 res = drv_start(local);
219                 if (res)
220                         goto err;
221         }
222
223         local->open_count++;
224         netif_start_queue(dev);
225         return 0;
226 err:
227         /* might already be clear but that doesn't matter */
228         clear_bit(SDATA_STATE_RUNNING, &sdata->state);
229
230         return res;
231 }
232
233 static int
234 ieee802154_check_mac_settings(struct ieee802154_local *local,
235                               struct wpan_dev *wpan_dev,
236                               struct wpan_dev *nwpan_dev)
237 {
238         ASSERT_RTNL();
239
240         if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
241                 if (wpan_dev->promiscuous_mode != nwpan_dev->promiscuous_mode)
242                         return -EBUSY;
243         }
244
245         if (local->hw.flags & IEEE802154_HW_AFILT) {
246                 if (wpan_dev->pan_id != nwpan_dev->pan_id ||
247                     wpan_dev->short_addr != nwpan_dev->short_addr ||
248                     wpan_dev->extended_addr != nwpan_dev->extended_addr)
249                         return -EBUSY;
250         }
251
252         if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
253                 if (wpan_dev->min_be != nwpan_dev->min_be ||
254                     wpan_dev->max_be != nwpan_dev->max_be ||
255                     wpan_dev->csma_retries != nwpan_dev->csma_retries)
256                         return -EBUSY;
257         }
258
259         if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
260                 if (wpan_dev->frame_retries != nwpan_dev->frame_retries)
261                         return -EBUSY;
262         }
263
264         if (local->hw.flags & IEEE802154_HW_LBT) {
265                 if (wpan_dev->lbt != nwpan_dev->lbt)
266                         return -EBUSY;
267         }
268
269         return 0;
270 }
271
272 static int
273 ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata,
274                                   enum nl802154_iftype iftype)
275 {
276         struct ieee802154_local *local = sdata->local;
277         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
278         struct ieee802154_sub_if_data *nsdata;
279
280         /* we hold the RTNL here so can safely walk the list */
281         list_for_each_entry(nsdata, &local->interfaces, list) {
282                 if (nsdata != sdata && ieee802154_sdata_running(nsdata)) {
283                         int ret;
284
285                         /* TODO currently we don't support multiple node types
286                          * we need to run skb_clone at rx path. Check if there
287                          * exist really an use case if we need to support
288                          * multiple node types at the same time.
289                          */
290                         if (wpan_dev->iftype == NL802154_IFTYPE_NODE &&
291                             nsdata->wpan_dev.iftype == NL802154_IFTYPE_NODE)
292                                 return -EBUSY;
293
294                         /* check all phy mac sublayer settings are the same.
295                          * We have only one phy, different values makes trouble.
296                          */
297                         ret = ieee802154_check_mac_settings(local, wpan_dev,
298                                                             &nsdata->wpan_dev);
299                         if (ret < 0)
300                                 return ret;
301                 }
302         }
303
304         return 0;
305 }
306
307 static int mac802154_wpan_open(struct net_device *dev)
308 {
309         int rc;
310         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
311         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
312
313         rc = ieee802154_check_concurrent_iface(sdata, wpan_dev->iftype);
314         if (rc < 0)
315                 return rc;
316
317         return mac802154_slave_open(dev);
318 }
319
320 static int mac802154_slave_close(struct net_device *dev)
321 {
322         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
323         struct ieee802154_local *local = sdata->local;
324
325         ASSERT_RTNL();
326
327         netif_stop_queue(dev);
328         local->open_count--;
329
330         clear_bit(SDATA_STATE_RUNNING, &sdata->state);
331
332         if (!local->open_count)
333                 ieee802154_stop_device(local);
334
335         return 0;
336 }
337
338 static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
339                                          struct ieee802154_hdr *hdr,
340                                          const struct ieee802154_mac_cb *cb)
341 {
342         struct ieee802154_llsec_params params;
343         u8 level;
344
345         mac802154_llsec_get_params(&sdata->sec, &params);
346
347         if (!params.enabled && cb->secen_override && cb->secen)
348                 return -EINVAL;
349         if (!params.enabled ||
350             (cb->secen_override && !cb->secen) ||
351             !params.out_level)
352                 return 0;
353         if (cb->seclevel_override && !cb->seclevel)
354                 return -EINVAL;
355
356         level = cb->seclevel_override ? cb->seclevel : params.out_level;
357
358         hdr->fc.security_enabled = 1;
359         hdr->sec.level = level;
360         hdr->sec.key_id_mode = params.out_key.mode;
361         if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
362                 hdr->sec.short_src = params.out_key.short_source;
363         else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
364                 hdr->sec.extended_src = params.out_key.extended_source;
365         hdr->sec.key_id = params.out_key.id;
366
367         return 0;
368 }
369
370 static int mac802154_header_create(struct sk_buff *skb,
371                                    struct net_device *dev,
372                                    unsigned short type,
373                                    const void *daddr,
374                                    const void *saddr,
375                                    unsigned len)
376 {
377         struct ieee802154_hdr hdr;
378         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
379         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
380         struct ieee802154_mac_cb *cb = mac_cb(skb);
381         int hlen;
382
383         if (!daddr)
384                 return -EINVAL;
385
386         memset(&hdr.fc, 0, sizeof(hdr.fc));
387         hdr.fc.type = cb->type;
388         hdr.fc.security_enabled = cb->secen;
389         hdr.fc.ack_request = cb->ackreq;
390         hdr.seq = atomic_inc_return(&dev->ieee802154_ptr->dsn) & 0xFF;
391
392         if (mac802154_set_header_security(sdata, &hdr, cb) < 0)
393                 return -EINVAL;
394
395         if (!saddr) {
396                 if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
397                     wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
398                     wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
399                         hdr.source.mode = IEEE802154_ADDR_LONG;
400                         hdr.source.extended_addr = wpan_dev->extended_addr;
401                 } else {
402                         hdr.source.mode = IEEE802154_ADDR_SHORT;
403                         hdr.source.short_addr = wpan_dev->short_addr;
404                 }
405
406                 hdr.source.pan_id = wpan_dev->pan_id;
407         } else {
408                 hdr.source = *(const struct ieee802154_addr *)saddr;
409         }
410
411         hdr.dest = *(const struct ieee802154_addr *)daddr;
412
413         hlen = ieee802154_hdr_push(skb, &hdr);
414         if (hlen < 0)
415                 return -EINVAL;
416
417         skb_reset_mac_header(skb);
418         skb->mac_len = hlen;
419
420         if (len > ieee802154_max_payload(&hdr))
421                 return -EMSGSIZE;
422
423         return hlen;
424 }
425
426 static int
427 mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
428 {
429         struct ieee802154_hdr hdr;
430         struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
431
432         if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
433                 pr_debug("malformed packet\n");
434                 return 0;
435         }
436
437         *addr = hdr.source;
438         return sizeof(*addr);
439 }
440
441 static struct header_ops mac802154_header_ops = {
442         .create         = mac802154_header_create,
443         .parse          = mac802154_header_parse,
444 };
445
446 static const struct net_device_ops mac802154_wpan_ops = {
447         .ndo_open               = mac802154_wpan_open,
448         .ndo_stop               = mac802154_slave_close,
449         .ndo_start_xmit         = ieee802154_subif_start_xmit,
450         .ndo_do_ioctl           = mac802154_wpan_ioctl,
451         .ndo_set_mac_address    = mac802154_wpan_mac_addr,
452 };
453
454 static const struct net_device_ops mac802154_monitor_ops = {
455         .ndo_open               = mac802154_wpan_open,
456         .ndo_stop               = mac802154_slave_close,
457         .ndo_start_xmit         = ieee802154_monitor_start_xmit,
458 };
459
460 static void mac802154_wpan_free(struct net_device *dev)
461 {
462         struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
463
464         mac802154_llsec_destroy(&sdata->sec);
465
466         free_netdev(dev);
467 }
468
469 static void ieee802154_if_setup(struct net_device *dev)
470 {
471         dev->addr_len           = IEEE802154_EXTENDED_ADDR_LEN;
472         memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
473
474         dev->hard_header_len    = MAC802154_FRAME_HARD_HEADER_LEN;
475         dev->needed_tailroom    = 2 + 16; /* FCS + MIC */
476         dev->mtu                = IEEE802154_MTU;
477         dev->tx_queue_len       = 300;
478         dev->flags              = IFF_NOARP | IFF_BROADCAST;
479 }
480
481 static int
482 ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
483                        enum nl802154_iftype type)
484 {
485         struct wpan_dev *wpan_dev = &sdata->wpan_dev;
486         int ret;
487         u8 tmp;
488
489         /* set some type-dependent values */
490         sdata->wpan_dev.iftype = type;
491
492         get_random_bytes(&tmp, sizeof(tmp));
493         atomic_set(&wpan_dev->bsn, tmp);
494         get_random_bytes(&tmp, sizeof(tmp));
495         atomic_set(&wpan_dev->dsn, tmp);
496
497         /* defaults per 802.15.4-2011 */
498         wpan_dev->min_be = 3;
499         wpan_dev->max_be = 5;
500         wpan_dev->csma_retries = 4;
501         wpan_dev->frame_retries = 3;
502
503         wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
504         wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
505
506         switch (type) {
507         case NL802154_IFTYPE_NODE:
508                 ieee802154_be64_to_le64(&wpan_dev->extended_addr,
509                                         sdata->dev->dev_addr);
510
511                 sdata->dev->header_ops = &mac802154_header_ops;
512                 sdata->dev->destructor = mac802154_wpan_free;
513                 sdata->dev->netdev_ops = &mac802154_wpan_ops;
514                 sdata->dev->ml_priv = &mac802154_mlme_wpan;
515                 wpan_dev->promiscuous_mode = false;
516
517                 mutex_init(&sdata->sec_mtx);
518
519                 mac802154_llsec_init(&sdata->sec);
520                 ret = mac802154_wpan_update_llsec(sdata->dev);
521                 if (ret < 0)
522                         return ret;
523
524                 break;
525         case NL802154_IFTYPE_MONITOR:
526                 sdata->dev->destructor = free_netdev;
527                 sdata->dev->netdev_ops = &mac802154_monitor_ops;
528                 wpan_dev->promiscuous_mode = true;
529                 break;
530         default:
531                 BUG();
532         }
533
534         return 0;
535 }
536
537 struct net_device *
538 ieee802154_if_add(struct ieee802154_local *local, const char *name,
539                   unsigned char name_assign_type, enum nl802154_iftype type,
540                   __le64 extended_addr)
541 {
542         struct net_device *ndev = NULL;
543         struct ieee802154_sub_if_data *sdata = NULL;
544         int ret = -ENOMEM;
545
546         ASSERT_RTNL();
547
548         ndev = alloc_netdev(sizeof(*sdata), name,
549                             name_assign_type, ieee802154_if_setup);
550         if (!ndev)
551                 return ERR_PTR(-ENOMEM);
552
553         ndev->needed_headroom = local->hw.extra_tx_headroom;
554
555         ret = dev_alloc_name(ndev, ndev->name);
556         if (ret < 0)
557                 goto err;
558
559         ieee802154_le64_to_be64(ndev->perm_addr,
560                                 &local->hw.phy->perm_extended_addr);
561         switch (type) {
562         case NL802154_IFTYPE_NODE:
563                 ndev->type = ARPHRD_IEEE802154;
564                 if (ieee802154_is_valid_extended_unicast_addr(extended_addr))
565                         ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
566                 else
567                         memcpy(ndev->dev_addr, ndev->perm_addr,
568                                IEEE802154_EXTENDED_ADDR_LEN);
569                 break;
570         case NL802154_IFTYPE_MONITOR:
571                 ndev->type = ARPHRD_IEEE802154_MONITOR;
572                 break;
573         default:
574                 ret = -EINVAL;
575                 goto err;
576         }
577
578         /* TODO check this */
579         SET_NETDEV_DEV(ndev, &local->phy->dev);
580         sdata = netdev_priv(ndev);
581         ndev->ieee802154_ptr = &sdata->wpan_dev;
582         memcpy(sdata->name, ndev->name, IFNAMSIZ);
583         sdata->dev = ndev;
584         sdata->wpan_dev.wpan_phy = local->hw.phy;
585         sdata->local = local;
586
587         /* setup type-dependent data */
588         ret = ieee802154_setup_sdata(sdata, type);
589         if (ret)
590                 goto err;
591
592         ret = register_netdevice(ndev);
593         if (ret < 0)
594                 goto err;
595
596         mutex_lock(&local->iflist_mtx);
597         list_add_tail_rcu(&sdata->list, &local->interfaces);
598         mutex_unlock(&local->iflist_mtx);
599
600         return ndev;
601
602 err:
603         free_netdev(ndev);
604         return ERR_PTR(ret);
605 }
606
607 void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
608 {
609         ASSERT_RTNL();
610
611         mutex_lock(&sdata->local->iflist_mtx);
612         list_del_rcu(&sdata->list);
613         mutex_unlock(&sdata->local->iflist_mtx);
614
615         synchronize_rcu();
616         unregister_netdevice(sdata->dev);
617 }
618
619 void ieee802154_remove_interfaces(struct ieee802154_local *local)
620 {
621         struct ieee802154_sub_if_data *sdata, *tmp;
622
623         mutex_lock(&local->iflist_mtx);
624         list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
625                 list_del(&sdata->list);
626
627                 unregister_netdevice(sdata->dev);
628         }
629         mutex_unlock(&local->iflist_mtx);
630 }
631
632 static int netdev_notify(struct notifier_block *nb,
633                          unsigned long state, void *ptr)
634 {
635         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
636         struct ieee802154_sub_if_data *sdata;
637
638         if (state != NETDEV_CHANGENAME)
639                 return NOTIFY_DONE;
640
641         if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
642                 return NOTIFY_DONE;
643
644         if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
645                 return NOTIFY_DONE;
646
647         sdata = IEEE802154_DEV_TO_SUB_IF(dev);
648         memcpy(sdata->name, dev->name, IFNAMSIZ);
649
650         return NOTIFY_OK;
651 }
652
653 static struct notifier_block mac802154_netdev_notifier = {
654         .notifier_call = netdev_notify,
655 };
656
657 int ieee802154_iface_init(void)
658 {
659         return register_netdevice_notifier(&mac802154_netdev_notifier);
660 }
661
662 void ieee802154_iface_exit(void)
663 {
664         unregister_netdevice_notifier(&mac802154_netdev_notifier);
665 }