diff options
Diffstat (limited to 'net')
99 files changed, 6328 insertions, 2880 deletions
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c index 142eef55c9e2..aced97db62f0 100644 --- a/net/6lowpan/iphc.c +++ b/net/6lowpan/iphc.c @@ -171,37 +171,6 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb, return 0; } -static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr, - struct net_device *dev, skb_delivery_cb deliver_skb) -{ - struct sk_buff *new; - int stat; - - new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb), - GFP_ATOMIC); - kfree_skb(skb); - - if (!new) - return -ENOMEM; - - skb_push(new, sizeof(struct ipv6hdr)); - skb_reset_network_header(new); - skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr)); - - new->protocol = htons(ETH_P_IPV6); - new->pkt_type = PACKET_HOST; - new->dev = dev; - - raw_dump_table(__func__, "raw skb data dump before receiving", - new->data, new->len); - - stat = deliver_skb(new, dev); - - kfree_skb(new); - - return stat; -} - /* Uncompress function for multicast destination address, * when M bit is set. */ @@ -332,10 +301,12 @@ err: /* TTL uncompression values */ static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 }; -int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, - const u8 *saddr, const u8 saddr_type, const u8 saddr_len, - const u8 *daddr, const u8 daddr_type, const u8 daddr_len, - u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb) +int +lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev, + const u8 *saddr, const u8 saddr_type, + const u8 saddr_len, const u8 *daddr, + const u8 daddr_type, const u8 daddr_len, + u8 iphc0, u8 iphc1) { struct ipv6hdr hdr = {}; u8 tmp, num_context = 0; @@ -348,7 +319,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, if (iphc1 & LOWPAN_IPHC_CID) { pr_debug("CID flag is set, increase header with one\n"); if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context))) - goto drop; + return -EINVAL; } hdr.version = 6; @@ -360,7 +331,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, */ case 0: /* 00b */ if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp))) - goto drop; + return -EINVAL; memcpy(&hdr.flow_lbl, &skb->data[0], 3); skb_pull(skb, 3); @@ -373,7 +344,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, */ case 2: /* 10b */ if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp))) - goto drop; + return -EINVAL; hdr.priority = ((tmp >> 2) & 0x0f); hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30); @@ -383,7 +354,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, */ case 1: /* 01b */ if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp))) - goto drop; + return -EINVAL; hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30); memcpy(&hdr.flow_lbl[1], &skb->data[0], 2); @@ -400,7 +371,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) { /* Next header is carried inline */ if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr))) - goto drop; + return -EINVAL; pr_debug("NH flag is set, next header carried inline: %02x\n", hdr.nexthdr); @@ -412,7 +383,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, } else { if (lowpan_fetch_skb(skb, &hdr.hop_limit, sizeof(hdr.hop_limit))) - goto drop; + return -EINVAL; } /* Extract SAM to the tmp variable */ @@ -431,7 +402,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, /* Check on error of previous branch */ if (err) - goto drop; + return -EINVAL; /* Extract DAM to the tmp variable */ tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03; @@ -446,7 +417,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, tmp); if (err) - goto drop; + return -EINVAL; } } else { err = uncompress_addr(skb, &hdr.daddr, tmp, daddr, @@ -454,28 +425,23 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, pr_debug("dest: stateless compression mode %d dest %pI6c\n", tmp, &hdr.daddr); if (err) - goto drop; + return -EINVAL; } /* UDP data uncompression */ if (iphc0 & LOWPAN_IPHC_NH_C) { struct udphdr uh; - struct sk_buff *new; + const int needed = sizeof(struct udphdr) + sizeof(hdr); if (uncompress_udp_header(skb, &uh)) - goto drop; + return -EINVAL; /* replace the compressed UDP head by the uncompressed UDP * header */ - new = skb_copy_expand(skb, sizeof(struct udphdr), - skb_tailroom(skb), GFP_ATOMIC); - kfree_skb(skb); - - if (!new) - return -ENOMEM; - - skb = new; + err = skb_cow(skb, needed); + if (unlikely(err)) + return err; skb_push(skb, sizeof(struct udphdr)); skb_reset_transport_header(skb); @@ -485,6 +451,10 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, (u8 *)&uh, sizeof(uh)); hdr.nexthdr = UIP_PROTO_UDP; + } else { + err = skb_cow(skb, sizeof(hdr)); + if (unlikely(err)) + return err; } hdr.payload_len = htons(skb->len); @@ -497,15 +467,15 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev, hdr.version, ntohs(hdr.payload_len), hdr.nexthdr, hdr.hop_limit, &hdr.daddr); - raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr)); + skb_push(skb, sizeof(hdr)); + skb_reset_network_header(skb); + skb_copy_to_linear_data(skb, &hdr, sizeof(hdr)); - return skb_deliver(skb, &hdr, dev, deliver_skb); + raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr)); -drop: - kfree_skb(skb); - return -EINVAL; + return 0; } -EXPORT_SYMBOL_GPL(lowpan_process_data); +EXPORT_SYMBOL_GPL(lowpan_header_decompress); static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift, const struct in6_addr *ipaddr, @@ -535,9 +505,17 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift, static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb) { - struct udphdr *uh = udp_hdr(skb); + struct udphdr *uh; u8 tmp; + /* In the case of RAW sockets the transport header is not set by + * the ip6 stack so we must set it ourselves + */ + if (skb->transport_header == skb->network_header) + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + uh = udp_hdr(skb); + if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) == LOWPAN_NHC_UDP_4BIT_PORT) && ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) == diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index c2e0d14433df..bdcaefd2db12 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -53,7 +53,7 @@ struct skb_cb { * The list contains struct lowpan_dev elements. */ static LIST_HEAD(bt_6lowpan_devices); -static DEFINE_RWLOCK(devices_lock); +static DEFINE_SPINLOCK(devices_lock); /* If psm is set to 0 (default value), then 6lowpan is disabled. * Other values are used to indicate a Protocol Service Multiplexer @@ -67,6 +67,7 @@ static struct l2cap_chan *listen_chan; struct lowpan_peer { struct list_head list; + struct rcu_head rcu; struct l2cap_chan *chan; /* peer addresses in various formats */ @@ -93,13 +94,14 @@ static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev) static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer) { - list_add(&peer->list, &dev->peers); + list_add_rcu(&peer->list, &dev->peers); atomic_inc(&dev->peer_count); } static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) { - list_del(&peer->list); + list_del_rcu(&peer->list); + kfree_rcu(peer, rcu); module_put(THIS_MODULE); @@ -114,31 +116,37 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev, bdaddr_t *ba, __u8 type) { - struct lowpan_peer *peer, *tmp; + struct lowpan_peer *peer; BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count), ba, type); - list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + rcu_read_lock(); + + list_for_each_entry_rcu(peer, &dev->peers, list) { BT_DBG("dst addr %pMR dst type %d", &peer->chan->dst, peer->chan->dst_type); if (bacmp(&peer->chan->dst, ba)) continue; - if (type == peer->chan->dst_type) + if (type == peer->chan->dst_type) { + rcu_read_unlock(); return peer; + } } + rcu_read_unlock(); + return NULL; } -static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, - struct l2cap_chan *chan) +static inline struct lowpan_peer *__peer_lookup_chan(struct lowpan_dev *dev, + struct l2cap_chan *chan) { - struct lowpan_peer *peer, *tmp; + struct lowpan_peer *peer; - list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + list_for_each_entry_rcu(peer, &dev->peers, list) { if (peer->chan == chan) return peer; } @@ -146,12 +154,12 @@ static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, return NULL; } -static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev, - struct l2cap_conn *conn) +static inline struct lowpan_peer *__peer_lookup_conn(struct lowpan_dev *dev, + struct l2cap_conn *conn) { - struct lowpan_peer *peer, *tmp; + struct lowpan_peer *peer; - list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + list_for_each_entry_rcu(peer, &dev->peers, list) { if (peer->chan->conn == conn) return peer; } @@ -163,7 +171,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev, struct in6_addr *daddr, struct sk_buff *skb) { - struct lowpan_peer *peer, *tmp; + struct lowpan_peer *peer; struct in6_addr *nexthop; struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); int count = atomic_read(&dev->peer_count); @@ -174,9 +182,13 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev, * send the packet. If only one peer exists, then we can send the * packet right away. */ - if (count == 1) - return list_first_entry(&dev->peers, struct lowpan_peer, - list); + if (count == 1) { + rcu_read_lock(); + peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer, + list); + rcu_read_unlock(); + return peer; + } if (!rt) { nexthop = &lowpan_cb(skb)->gw; @@ -195,53 +207,57 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev, BT_DBG("gw %pI6c", nexthop); - list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + rcu_read_lock(); + + list_for_each_entry_rcu(peer, &dev->peers, list) { BT_DBG("dst addr %pMR dst type %d ip %pI6c", &peer->chan->dst, peer->chan->dst_type, &peer->peer_addr); - if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) + if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) { + rcu_read_unlock(); return peer; + } } + rcu_read_unlock(); + return NULL; } static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn) { - struct lowpan_dev *entry, *tmp; + struct lowpan_dev *entry; struct lowpan_peer *peer = NULL; - unsigned long flags; - read_lock_irqsave(&devices_lock, flags); + rcu_read_lock(); - list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { - peer = peer_lookup_conn(entry, conn); + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { + peer = __peer_lookup_conn(entry, conn); if (peer) break; } - read_unlock_irqrestore(&devices_lock, flags); + rcu_read_unlock(); return peer; } static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn) { - struct lowpan_dev *entry, *tmp; + struct lowpan_dev *entry; struct lowpan_dev *dev = NULL; - unsigned long flags; - read_lock_irqsave(&devices_lock, flags); + rcu_read_lock(); - list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { if (conn->hcon->hdev == entry->hdev) { dev = entry; break; } } - read_unlock_irqrestore(&devices_lock, flags); + rcu_read_unlock(); return dev; } @@ -249,59 +265,49 @@ static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn) static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *skb_cp; - int ret; skb_cp = skb_copy(skb, GFP_ATOMIC); if (!skb_cp) - return -ENOMEM; - - ret = netif_rx(skb_cp); - if (ret < 0) { - BT_DBG("receive skb %d", ret); return NET_RX_DROP; - } - return ret; + return netif_rx(skb_cp); } -static int process_data(struct sk_buff *skb, struct net_device *netdev, - struct l2cap_chan *chan) +static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, + struct l2cap_chan *chan) { const u8 *saddr, *daddr; u8 iphc0, iphc1; struct lowpan_dev *dev; struct lowpan_peer *peer; - unsigned long flags; dev = lowpan_dev(netdev); - read_lock_irqsave(&devices_lock, flags); - peer = peer_lookup_chan(dev, chan); - read_unlock_irqrestore(&devices_lock, flags); + rcu_read_lock(); + peer = __peer_lookup_chan(dev, chan); + rcu_read_unlock(); if (!peer) - goto drop; + return -EINVAL; saddr = peer->eui64_addr; daddr = dev->netdev->dev_addr; /* at least two bytes will be used for the encoding */ if (skb->len < 2) - goto drop; + return -EINVAL; if (lowpan_fetch_skb_u8(skb, &iphc0)) - goto drop; + return -EINVAL; if (lowpan_fetch_skb_u8(skb, &iphc1)) - goto drop; + return -EINVAL; - return lowpan_process_data(skb, netdev, - saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, - daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, - iphc0, iphc1, give_skb_to_upper); + return lowpan_header_decompress(skb, netdev, + saddr, IEEE802154_ADDR_LONG, + EUI64_ADDR_LEN, daddr, + IEEE802154_ADDR_LONG, EUI64_ADDR_LEN, + iphc0, iphc1); -drop: - kfree_skb(skb); - return -EINVAL; } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, @@ -316,6 +322,10 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, if (dev->type != ARPHRD_6LOWPAN) goto drop; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto drop; + /* check that it's our buffer */ if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { /* Copy the packet so that the IPv6 header is @@ -340,8 +350,8 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; - kfree_skb(local_skb); - kfree_skb(skb); + consume_skb(local_skb); + consume_skb(skb); } else { switch (skb->data[0] & 0xe0) { case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ @@ -349,14 +359,27 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, if (!local_skb) goto drop; - ret = process_data(local_skb, dev, chan); - if (ret != NET_RX_SUCCESS) + ret = iphc_decompress(local_skb, dev, chan); + if (ret < 0) { + kfree_skb(local_skb); goto drop; + } + + local_skb->protocol = htons(ETH_P_IPV6); + local_skb->pkt_type = PACKET_HOST; + local_skb->dev = dev; + + if (give_skb_to_upper(local_skb, dev) + != NET_RX_SUCCESS) { + kfree_skb(local_skb); + goto drop; + } dev->stats.rx_bytes += skb->len; dev->stats.rx_packets++; - kfree_skb(skb); + consume_skb(local_skb); + consume_skb(skb); break; default: break; @@ -443,7 +466,6 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, if (ipv6_addr_is_multicast(&ipv6_daddr)) { lowpan_cb(skb)->chan = NULL; } else { - unsigned long flags; u8 addr_type; /* Get destination BT device from skb. @@ -454,19 +476,14 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev, BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, addr_type, &ipv6_daddr); - read_lock_irqsave(&devices_lock, flags); peer = peer_lookup_ba(dev, &addr, addr_type); - read_unlock_irqrestore(&devices_lock, flags); - if (!peer) { /* The packet might be sent to 6lowpan interface * because of routing (either via default route * or user set route) so get peer according to * the destination address. */ - read_lock_irqsave(&devices_lock, flags); peer = peer_lookup_dst(dev, &ipv6_daddr, skb); - read_unlock_irqrestore(&devices_lock, flags); if (!peer) { BT_DBG("no such peer %pMR found", &addr); return -ENOENT; @@ -549,14 +566,13 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) { struct sk_buff *local_skb; - struct lowpan_dev *entry, *tmp; - unsigned long flags; + struct lowpan_dev *entry; int err = 0; - read_lock_irqsave(&devices_lock, flags); + rcu_read_lock(); - list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { - struct lowpan_peer *pentry, *ptmp; + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { + struct lowpan_peer *pentry; struct lowpan_dev *dev; if (entry->netdev != netdev) @@ -564,7 +580,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) dev = lowpan_dev(entry->netdev); - list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { + list_for_each_entry_rcu(pentry, &dev->peers, list) { int ret; local_skb = skb_clone(skb, GFP_ATOMIC); @@ -581,7 +597,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) } } - read_unlock_irqrestore(&devices_lock, flags); + rcu_read_unlock(); return err; } @@ -591,17 +607,13 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) int err = 0; bdaddr_t addr; u8 addr_type; - struct sk_buff *tmpskb; /* We must take a copy of the skb before we modify/replace the ipv6 * header as the header could be used elsewhere */ - tmpskb = skb_unshare(skb, GFP_ATOMIC); - if (!tmpskb) { - kfree_skb(skb); + skb = skb_unshare(skb, GFP_ATOMIC); + if (!skb) return NET_XMIT_DROP; - } - skb = tmpskb; /* Return values from setup_header() * <0 - error, packet is dropped @@ -638,7 +650,26 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) return err < 0 ? NET_XMIT_DROP : err; } +static struct lock_class_key bt_tx_busylock; +static struct lock_class_key bt_netdev_xmit_lock_key; + +static void bt_set_lockdep_class_one(struct net_device *dev, + struct netdev_queue *txq, + void *_unused) +{ + lockdep_set_class(&txq->_xmit_lock, &bt_netdev_xmit_lock_key); +} + +static int bt_dev_init(struct net_device *dev) +{ + netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL); + dev->qdisc_tx_busylock = &bt_tx_busylock; + + return 0; +} + static const struct net_device_ops netdev_ops = { + .ndo_init = bt_dev_init, .ndo_start_xmit = bt_xmit, }; @@ -783,7 +814,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, struct lowpan_dev *dev) { struct lowpan_peer *peer; - unsigned long flags; peer = kzalloc(sizeof(*peer), GFP_ATOMIC); if (!peer) @@ -806,10 +836,10 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, */ set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8); - write_lock_irqsave(&devices_lock, flags); + spin_lock(&devices_lock); INIT_LIST_HEAD(&peer->list); peer_add(dev, peer); - write_unlock_irqrestore(&devices_lock, flags); + spin_unlock(&devices_lock); /* Notifying peers about us needs to be done without locks held */ INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); @@ -822,7 +852,6 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) { struct net_device *netdev; int err = 0; - unsigned long flags; netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE, NET_NAME_UNKNOWN, netdev_setup); @@ -852,10 +881,10 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) (*dev)->hdev = chan->conn->hcon->hdev; INIT_LIST_HEAD(&(*dev)->peers); - write_lock_irqsave(&devices_lock, flags); + spin_lock(&devices_lock); INIT_LIST_HEAD(&(*dev)->list); - list_add(&(*dev)->list, &bt_6lowpan_devices); - write_unlock_irqrestore(&devices_lock, flags); + list_add_rcu(&(*dev)->list, &bt_6lowpan_devices); + spin_unlock(&devices_lock); return 0; @@ -909,11 +938,10 @@ static void delete_netdev(struct work_struct *work) static void chan_close_cb(struct l2cap_chan *chan) { - struct lowpan_dev *entry, *tmp; + struct lowpan_dev *entry; struct lowpan_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; - unsigned long flags; bool last = false, removed = true; BT_DBG("chan %p conn %p", chan, chan->conn); @@ -928,11 +956,11 @@ static void chan_close_cb(struct l2cap_chan *chan) removed = false; } - write_lock_irqsave(&devices_lock, flags); + spin_lock(&devices_lock); - list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { dev = lowpan_dev(entry->netdev); - peer = peer_lookup_chan(dev, chan); + peer = __peer_lookup_chan(dev, chan); if (peer) { last = peer_del(dev, peer); err = 0; @@ -943,13 +971,12 @@ static void chan_close_cb(struct l2cap_chan *chan) atomic_read(&chan->kref.refcount)); l2cap_chan_put(chan); - kfree(peer); break; } } if (!err && last && dev && !atomic_read(&dev->peer_count)) { - write_unlock_irqrestore(&devices_lock, flags); + spin_unlock(&devices_lock); cancel_delayed_work_sync(&dev->notify_peers); @@ -960,7 +987,7 @@ static void chan_close_cb(struct l2cap_chan *chan) schedule_work(&entry->delete_netdev); } } else { - write_unlock_irqrestore(&devices_lock, flags); + spin_unlock(&devices_lock); } return; @@ -1103,6 +1130,8 @@ static struct l2cap_chan *bt_6lowpan_listen(void) pchan->state = BT_LISTEN; pchan->src_type = BDADDR_LE_PUBLIC; + atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT); + BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan, pchan->src_type); @@ -1152,10 +1181,9 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, static void disconnect_all_peers(void) { - struct lowpan_dev *entry, *tmp_dev; + struct lowpan_dev *entry; struct lowpan_peer *peer, *tmp_peer, *new_peer; struct list_head peers; - unsigned long flags; INIT_LIST_HEAD(&peers); @@ -1164,10 +1192,10 @@ static void disconnect_all_peers(void) * with the same list at the same time. */ - read_lock_irqsave(&devices_lock, flags); + rcu_read_lock(); - list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { - list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) { + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { + list_for_each_entry_rcu(peer, &entry->peers, list) { new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); if (!new_peer) break; @@ -1179,26 +1207,36 @@ static void disconnect_all_peers(void) } } - read_unlock_irqrestore(&devices_lock, flags); + rcu_read_unlock(); + spin_lock(&devices_lock); list_for_each_entry_safe(peer, tmp_peer, &peers, list) { l2cap_chan_close(peer->chan, ENOENT); - kfree(peer); + + list_del_rcu(&peer->list); + kfree_rcu(peer, rcu); + + module_put(THIS_MODULE); } + spin_unlock(&devices_lock); } -static int lowpan_psm_set(void *data, u64 val) -{ +struct set_psm { + struct work_struct work; u16 psm; +}; - psm = val; - if (psm == 0 || psm_6lowpan != psm) +static void do_psm_set(struct work_struct *work) +{ + struct set_psm *set_psm = container_of(work, struct set_psm, work); + + if (set_psm->psm == 0 || psm_6lowpan != set_psm->psm) /* Disconnect existing connections if 6lowpan is * disabled (psm = 0), or if psm changes. */ disconnect_all_peers(); - psm_6lowpan = psm; + psm_6lowpan = set_psm->psm; if (listen_chan) { l2cap_chan_close(listen_chan, 0); @@ -1207,6 +1245,22 @@ static int lowpan_psm_set(void *data, u64 val) listen_chan = bt_6lowpan_listen(); + kfree(set_psm); +} + +static int lowpan_psm_set(void *data, u64 val) +{ + struct set_psm *set_psm; + + set_psm = kzalloc(sizeof(*set_psm), GFP_KERNEL); + if (!set_psm) + return -ENOMEM; + + set_psm->psm = val; + INIT_WORK(&set_psm->work, do_psm_set); + + schedule_work(&set_psm->work); + return 0; } @@ -1288,19 +1342,18 @@ static ssize_t lowpan_control_write(struct file *fp, static int lowpan_control_show(struct seq_file *f, void *ptr) { - struct lowpan_dev *entry, *tmp_dev; - struct lowpan_peer *peer, *tmp_peer; - unsigned long flags; + struct lowpan_dev *entry; + struct lowpan_peer *peer; - read_lock_irqsave(&devices_lock, flags); + spin_lock(&devices_lock); - list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { - list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) + list_for_each_entry(entry, &bt_6lowpan_devices, list) { + list_for_each_entry(peer, &entry->peers, list) seq_printf(f, "%pMR (type %u)\n", &peer->chan->dst, peer->chan->dst_type); } - read_unlock_irqrestore(&devices_lock, flags); + spin_unlock(&devices_lock); return 0; } @@ -1322,7 +1375,6 @@ static void disconnect_devices(void) { struct lowpan_dev *entry, *tmp, *new_dev; struct list_head devices; - unsigned long flags; INIT_LIST_HEAD(&devices); @@ -1331,9 +1383,9 @@ static void disconnect_devices(void) * devices list. */ - read_lock_irqsave(&devices_lock, flags); + rcu_read_lock(); - list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) { new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC); if (!new_dev) break; @@ -1341,10 +1393,10 @@ static void disconnect_devices(void) new_dev->netdev = entry->netdev; INIT_LIST_HEAD(&new_dev->list); - list_add(&new_dev->list, &devices); + list_add_rcu(&new_dev->list, &devices); } - read_unlock_irqrestore(&devices_lock, flags); + rcu_read_unlock(); list_for_each_entry_safe(entry, tmp, &devices, list) { ifdown(entry->netdev); @@ -1359,17 +1411,15 @@ static int device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct net_device *netdev = netdev_notifier_info_to_dev(ptr); - struct lowpan_dev *entry, *tmp; - unsigned long flags; + struct lowpan_dev *entry; if (netdev->type != ARPHRD_6LOWPAN) return NOTIFY_DONE; switch (event) { case NETDEV_UNREGISTER: - write_lock_irqsave(&devices_lock, flags); - list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, - list) { + spin_lock(&devices_lock); + list_for_each_entry(entry, &bt_6lowpan_devices, list) { if (entry->netdev == netdev) { BT_DBG("Unregistered netdev %s %p", netdev->name, netdev); @@ -1378,7 +1428,7 @@ static int device_event(struct notifier_block *unused, break; } } - write_unlock_irqrestore(&devices_lock, flags); + spin_unlock(&devices_lock); break; } diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 600fb29288f4..5e97a8ff850b 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -39,11 +39,10 @@ menuconfig BT to Bluetooth kernel modules are provided in the BlueZ packages. For more information, see <http://www.bluez.org/>. -config BT_6LOWPAN - tristate "Bluetooth 6LoWPAN support" - depends on BT && 6LOWPAN - help - IPv6 compression over Bluetooth Low Energy. +config BT_BREDR + bool "Bluetooth Classic (BR/EDR) features" + depends on BT + default y source "net/bluetooth/rfcomm/Kconfig" @@ -53,4 +52,15 @@ source "net/bluetooth/cmtp/Kconfig" source "net/bluetooth/hidp/Kconfig" +config BT_LE + bool "Bluetooth Low Energy (LE) features" + depends on BT + default y + +config BT_6LOWPAN + tristate "Bluetooth 6LoWPAN support" + depends on BT_LE && 6LOWPAN + help + IPv6 compression over Bluetooth Low Energy. + source "drivers/bluetooth/Kconfig" diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 2640d78f30b8..ee016f039100 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -134,6 +134,7 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) { struct crypto_shash *tfm; + struct shash_desc *shash; int ret; if (!ksize) @@ -148,18 +149,24 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) ret = crypto_shash_setkey(tfm, key, ksize); if (ret) { BT_DBG("crypto_ahash_setkey failed: err %d", ret); - } else { - char desc[sizeof(struct shash_desc) + - crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR; - struct shash_desc *shash = (struct shash_desc *)desc; - - shash->tfm = tfm; - shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + goto failed; + } - ret = crypto_shash_digest(shash, plaintext, psize, - output); + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) { + ret = -ENOMEM; + goto failed; } + shash->tfm = tfm; + shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_digest(shash, plaintext, psize, output); + + kfree(shash); + +failed: crypto_free_shash(tfm); return ret; } diff --git a/net/bluetooth/bnep/Kconfig b/net/bluetooth/bnep/Kconfig index 71791fc9f6b1..9b70317c49dc 100644 --- a/net/bluetooth/bnep/Kconfig +++ b/net/bluetooth/bnep/Kconfig @@ -1,6 +1,6 @@ config BT_BNEP tristate "BNEP protocol support" - depends on BT + depends on BT_BREDR select CRC32 help BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig index 94cbf42ce155..939da0fbdd88 100644 --- a/net/bluetooth/cmtp/Kconfig +++ b/net/bluetooth/cmtp/Kconfig @@ -1,6 +1,6 @@ config BT_CMTP tristate "CMTP protocol support" - depends on BT && ISDN_CAPI + depends on BT_BREDR && ISDN_CAPI help CMTP (CAPI Message Transport Protocol) is a transport layer for CAPI messages. CMTP is required for the Bluetooth Common diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b9517bd17190..96887ae8375b 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -141,10 +141,11 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason) */ if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) { struct hci_dev *hdev = conn->hdev; - struct hci_cp_read_clock_offset cp; + struct hci_cp_read_clock_offset clkoff_cp; - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp); + clkoff_cp.handle = cpu_to_le16(conn->handle); + hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp), + &clkoff_cp); } conn->state = BT_DISCONN; @@ -415,7 +416,7 @@ static void le_conn_timeout(struct work_struct *work) * happen with broken hardware or if low duty cycle was used * (which doesn't have a timeout of its own). */ - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + if (conn->role == HCI_ROLE_SLAVE) { u8 enable = 0x00; hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); @@ -517,7 +518,7 @@ int hci_conn_del(struct hci_conn *conn) /* Unacked frames */ hdev->acl_cnt += conn->sent; } else if (conn->type == LE_LINK) { - cancel_delayed_work_sync(&conn->le_conn_timeout); + cancel_delayed_work(&conn->le_conn_timeout); if (hdev->le_pkts) hdev->le_cnt += conn->sent; @@ -544,6 +545,9 @@ int hci_conn_del(struct hci_conn *conn) hci_conn_del_sysfs(conn); + if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags)) + hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type); + hci_dev_put(hdev); hci_conn_put(conn); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index cb05d7f16a34..d786958a1dec 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -200,31 +200,6 @@ static const struct file_operations blacklist_fops = { .release = single_release, }; -static int whitelist_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bdaddr_list *b; - - hci_dev_lock(hdev); - list_for_each_entry(b, &hdev->whitelist, list) - seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); - hci_dev_unlock(hdev); - - return 0; -} - -static int whitelist_open(struct inode *inode, struct file *file) -{ - return single_open(file, whitelist_show, inode->i_private); -} - -static const struct file_operations whitelist_fops = { - .open = whitelist_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static int uuids_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -773,16 +748,15 @@ static const struct file_operations white_list_fops = { static int identity_resolving_keys_show(struct seq_file *f, void *ptr) { struct hci_dev *hdev = f->private; - struct list_head *p, *n; + struct smp_irk *irk; - hci_dev_lock(hdev); - list_for_each_safe(p, n, &hdev->identity_resolving_keys) { - struct smp_irk *irk = list_entry(p, struct smp_irk, list); + rcu_read_lock(); + list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) { seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &irk->bdaddr, irk->addr_type, 16, irk->val, &irk->rpa); } - hci_dev_unlock(hdev); + rcu_read_unlock(); return 0; } @@ -803,17 +777,15 @@ static const struct file_operations identity_resolving_keys_fops = { static int long_term_keys_show(struct seq_file *f, void *ptr) { struct hci_dev *hdev = f->private; - struct list_head *p, *n; + struct smp_ltk *ltk; - hci_dev_lock(hdev); - list_for_each_safe(p, n, &hdev->long_term_keys) { - struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list); + rcu_read_lock(); + list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list) seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n", <k->bdaddr, ltk->bdaddr_type, ltk->authenticated, ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv), __le64_to_cpu(ltk->rand), 16, ltk->val); - } - hci_dev_unlock(hdev); + rcu_read_unlock(); return 0; } @@ -1030,10 +1002,13 @@ static int device_list_show(struct seq_file *f, void *ptr) { struct hci_dev *hdev = f->private; struct hci_conn_params *p; + struct bdaddr_list *b; hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->whitelist, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); list_for_each_entry(p, &hdev->le_conn_params, list) { - seq_printf(f, "%pMR %u %u\n", &p->addr, p->addr_type, + seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type, p->auto_connect); } hci_dev_unlock(hdev); @@ -1147,13 +1122,15 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen, hdev->req_status = HCI_REQ_PEND; - err = hci_req_run(&req, hci_req_sync_complete); - if (err < 0) - return ERR_PTR(err); - add_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); + err = hci_req_run(&req, hci_req_sync_complete); + if (err < 0) { + remove_wait_queue(&hdev->req_wait_q, &wait); + return ERR_PTR(err); + } + schedule_timeout(timeout); remove_wait_queue(&hdev->req_wait_q, &wait); @@ -1211,10 +1188,15 @@ static int __hci_req_sync(struct hci_dev *hdev, func(&req, opt); + add_wait_queue(&hdev->req_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + err = hci_req_run(&req, hci_req_sync_complete); if (err < 0) { hdev->req_status = 0; + remove_wait_queue(&hdev->req_wait_q, &wait); + /* ENODATA means the HCI request command queue is empty. * This can happen when a request with conditionals doesn't * trigger any commands to be sent. This is normal behavior @@ -1226,9 +1208,6 @@ static int __hci_req_sync(struct hci_dev *hdev, return err; } - add_wait_queue(&hdev->req_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(timeout); remove_wait_queue(&hdev->req_wait_q, &wait); @@ -1811,10 +1790,10 @@ static int __hci_init(struct hci_dev *hdev) &hdev->manufacturer); debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver); debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev); + debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, + &device_list_fops); debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, &blacklist_fops); - debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev, - &whitelist_fops); debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, @@ -1893,8 +1872,6 @@ static int __hci_init(struct hci_dev *hdev) hdev, &adv_min_interval_fops); debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, hdev, &adv_max_interval_fops); - debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, - &device_list_fops); debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, &hdev->discov_interleaved_timeout); @@ -2584,6 +2561,11 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (test_bit(HCI_MGMT, &hdev->dev_flags)) cancel_delayed_work_sync(&hdev->rpa_expired); + /* Avoid potential lockdep warnings from the *_flush() calls by + * ensuring the workqueue is empty up front. + */ + drain_workqueue(hdev->workqueue); + hci_dev_lock(hdev); hci_inquiry_cache_flush(hdev); hci_pend_le_actions_clear(hdev); @@ -2707,6 +2689,11 @@ int hci_dev_reset(__u16 dev) skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->cmd_q); + /* Avoid potential lockdep warnings from the *_flush() calls by + * ensuring the workqueue is empty up front. + */ + drain_workqueue(hdev->workqueue); + hci_dev_lock(hdev); hci_inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); @@ -3126,21 +3113,21 @@ void hci_link_keys_clear(struct hci_dev *hdev) void hci_smp_ltks_clear(struct hci_dev *hdev) { - struct smp_ltk *k, *tmp; + struct smp_ltk *k; - list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { - list_del(&k->list); - kfree(k); + list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { + list_del_rcu(&k->list); + kfree_rcu(k, rcu); } } void hci_smp_irks_clear(struct hci_dev *hdev) { - struct smp_irk *k, *tmp; + struct smp_irk *k; - list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) { - list_del(&k->list); - kfree(k); + list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) { + list_del_rcu(&k->list); + kfree_rcu(k, rcu); } } @@ -3204,15 +3191,18 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, { struct smp_ltk *k; - list_for_each_entry(k, &hdev->long_term_keys, list) { + rcu_read_lock(); + list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { if (k->ediv != ediv || k->rand != rand) continue; if (ltk_role(k->type) != role) continue; + rcu_read_unlock(); return k; } + rcu_read_unlock(); return NULL; } @@ -3222,11 +3212,16 @@ struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct smp_ltk *k; - list_for_each_entry(k, &hdev->long_term_keys, list) + rcu_read_lock(); + list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { if (addr_type == k->bdaddr_type && bacmp(bdaddr, &k->bdaddr) == 0 && - ltk_role(k->type) == role) + ltk_role(k->type) == role) { + rcu_read_unlock(); return k; + } + } + rcu_read_unlock(); return NULL; } @@ -3235,17 +3230,22 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa) { struct smp_irk *irk; - list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { - if (!bacmp(&irk->rpa, rpa)) + rcu_read_lock(); + list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) { + if (!bacmp(&irk->rpa, rpa)) { + rcu_read_unlock(); return irk; + } } - list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { + list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) { if (smp_irk_matches(hdev, irk->val, rpa)) { bacpy(&irk->rpa, rpa); + rcu_read_unlock(); return irk; } } + rcu_read_unlock(); return NULL; } @@ -3259,11 +3259,15 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0) return NULL; - list_for_each_entry(irk, &hdev->identity_resolving_keys, list) { + rcu_read_lock(); + list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) { if (addr_type == irk->addr_type && - bacmp(bdaddr, &irk->bdaddr) == 0) + bacmp(bdaddr, &irk->bdaddr) == 0) { + rcu_read_unlock(); return irk; + } } + rcu_read_unlock(); return NULL; } @@ -3329,7 +3333,7 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, key = kzalloc(sizeof(*key), GFP_KERNEL); if (!key) return NULL; - list_add(&key->list, &hdev->long_term_keys); + list_add_rcu(&key->list, &hdev->long_term_keys); } bacpy(&key->bdaddr, bdaddr); @@ -3358,7 +3362,7 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, bacpy(&irk->bdaddr, bdaddr); irk->addr_type = addr_type; - list_add(&irk->list, &hdev->identity_resolving_keys); + list_add_rcu(&irk->list, &hdev->identity_resolving_keys); } memcpy(irk->val, val, 16); @@ -3385,17 +3389,17 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) { - struct smp_ltk *k, *tmp; + struct smp_ltk *k; int removed = 0; - list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { + list_for_each_entry_rcu(k, &hdev->long_term_keys, list) { if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type) continue; BT_DBG("%s removing %pMR", hdev->name, bdaddr); - list_del(&k->list); - kfree(k); + list_del_rcu(&k->list); + kfree_rcu(k, rcu); removed++; } @@ -3404,16 +3408,16 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type) void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) { - struct smp_irk *k, *tmp; + struct smp_irk *k; - list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) { + list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) { if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type) continue; BT_DBG("%s removing %pMR", hdev->name, bdaddr); - list_del(&k->list); - kfree(k); + list_del_rcu(&k->list); + kfree_rcu(k, rcu); } } @@ -3475,7 +3479,7 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev) } int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash, u8 *randomizer) + u8 *hash, u8 *rand) { struct oob_data *data; @@ -3490,10 +3494,10 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, } memcpy(data->hash192, hash, sizeof(data->hash192)); - memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192)); + memcpy(data->rand192, rand, sizeof(data->rand192)); memset(data->hash256, 0, sizeof(data->hash256)); - memset(data->randomizer256, 0, sizeof(data->randomizer256)); + memset(data->rand256, 0, sizeof(data->rand256)); BT_DBG("%s for %pMR", hdev->name, bdaddr); @@ -3501,8 +3505,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, } int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 *hash192, u8 *randomizer192, - u8 *hash256, u8 *randomizer256) + u8 *hash192, u8 *rand192, + u8 *hash256, u8 *rand256) { struct oob_data *data; @@ -3517,10 +3521,10 @@ int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, } memcpy(data->hash192, hash192, sizeof(data->hash192)); - memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192)); + memcpy(data->rand192, rand192, sizeof(data->rand192)); memcpy(data->hash256, hash256, sizeof(data->hash256)); - memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256)); + memcpy(data->rand256, rand256, sizeof(data->rand256)); BT_DBG("%s for %pMR", hdev->name, bdaddr); @@ -4244,6 +4248,24 @@ int hci_resume_dev(struct hci_dev *hdev) } EXPORT_SYMBOL(hci_resume_dev); +/* Reset HCI device */ +int hci_reset_dev(struct hci_dev *hdev) +{ + const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, 0x00 }; + struct sk_buff *skb; + + skb = bt_skb_alloc(3, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + memcpy(skb_put(skb, 3), hw_err, 3); + + /* Send Hardware Error to upper stack */ + return hci_recv_frame(hdev, skb); +} +EXPORT_SYMBOL(hci_reset_dev); + /* Receive frame from HCI drivers */ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) { @@ -4477,7 +4499,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) BT_DBG("length %u", skb_queue_len(&req->cmd_q)); - /* If an error occured during request building, remove all HCI + /* If an error occurred during request building, remove all HCI * commands queued on the HCI request queue. */ if (req->err) { @@ -4546,7 +4568,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, return -ENOMEM; } - /* Stand-alone HCI commands must be flaged as + /* Stand-alone HCI commands must be flagged as * single-command requests. */ bt_cb(skb)->req.start = true; @@ -4566,7 +4588,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); - /* If an error occured during request building, there is no point in + /* If an error occurred during request building, there is no point in * queueing the HCI command. We can simply return. */ if (req->err) @@ -4661,8 +4683,12 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, skb_shinfo(skb)->frag_list = NULL; - /* Queue all fragments atomically */ - spin_lock(&queue->lock); + /* Queue all fragments atomically. We need to use spin_lock_bh + * here because of 6LoWPAN links, as there this function is + * called from softirq and using normal spin lock could cause + * deadlocks. + */ + spin_lock_bh(&queue->lock); __skb_queue_tail(queue, skb); @@ -4679,7 +4705,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, __skb_queue_tail(queue, skb); } while (list); - spin_unlock(&queue->lock); + spin_unlock_bh(&queue->lock); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8b0a2a6de419..844f7d1ff1cd 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -189,6 +189,9 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); + if (status) + return; + /* Reset all non-persistent flags */ hdev->dev_flags &= ~HCI_PERSISTENT_MASK; @@ -205,6 +208,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) hdev->le_scan_type = LE_SCAN_PASSIVE; hdev->ssp_debug_mode = 0; + + hci_bdaddr_list_clear(&hdev->le_white_list); } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -989,8 +994,8 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); - mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer, - NULL, NULL, rp->status); + mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL, + rp->status); hci_dev_unlock(hdev); } @@ -1002,8 +1007,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); hci_dev_lock(hdev); - mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192, - rp->hash256, rp->randomizer256, + mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192, + rp->hash256, rp->rand256, rp->status); hci_dev_unlock(hdev); } @@ -1045,7 +1050,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - /* If we're doing connection initation as peripheral. Set a + /* If we're doing connection initiation as peripheral. Set a * timeout in case something goes wrong. */ if (*sent) { @@ -1576,9 +1581,15 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, struct discovery_state *discov = &hdev->discovery; struct inquiry_entry *e; - if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name, - name_len, conn->dev_class); + /* Update the mgmt connected state if necessary. Be careful with + * conn objects that exist but are not (yet) connected however. + * Only those in BT_CONFIG or BT_CONNECTED states can be + * considered connected. + */ + if (conn && + (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) && + !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) + mgmt_device_connected(hdev, conn, 0, name, name_len); if (discov->state == DISCOVERY_STOPPED) return; @@ -1943,6 +1954,29 @@ unlock: hci_dev_unlock(hdev); } +static void hci_cs_switch_role(struct hci_dev *hdev, u8 status) +{ + struct hci_cp_switch_role *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (!status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); + if (conn) + clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); + + hci_dev_unlock(hdev); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2536,9 +2570,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev, cp.pscan_rep_mode = 0x02; hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); + mgmt_device_connected(hdev, conn, 0, NULL, 0); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; @@ -2848,6 +2880,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_create_conn(hdev, ev->status); break; + case HCI_OP_DISCONNECT: + hci_cs_disconnect(hdev, ev->status); + break; + case HCI_OP_ADD_SCO: hci_cs_add_sco(hdev, ev->status); break; @@ -2876,24 +2912,24 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_setup_sync_conn(hdev, ev->status); break; - case HCI_OP_SNIFF_MODE: - hci_cs_sniff_mode(hdev, ev->status); + case HCI_OP_CREATE_PHY_LINK: + hci_cs_create_phylink(hdev, ev->status); break; - case HCI_OP_EXIT_SNIFF_MODE: - hci_cs_exit_sniff_mode(hdev, ev->status); + case HCI_OP_ACCEPT_PHY_LINK: + hci_cs_accept_phylink(hdev, ev->status); break; - case HCI_OP_DISCONNECT: - hci_cs_disconnect(hdev, ev->status); + case HCI_OP_SNIFF_MODE: + hci_cs_sniff_mode(hdev, ev->status); break; - case HCI_OP_CREATE_PHY_LINK: - hci_cs_create_phylink(hdev, ev->status); + case HCI_OP_EXIT_SNIFF_MODE: + hci_cs_exit_sniff_mode(hdev, ev->status); break; - case HCI_OP_ACCEPT_PHY_LINK: - hci_cs_accept_phylink(hdev, ev->status); + case HCI_OP_SWITCH_ROLE: + hci_cs_switch_role(hdev, ev->status); break; case HCI_OP_LE_CREATE_CONN: @@ -2923,6 +2959,13 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_hardware_error *ev = (void *) skb->data; + + BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code); +} + static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_role_change *ev = (void *) skb->data; @@ -3434,9 +3477,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev, cp.pscan_rep_mode = 0x02; hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); } else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, - conn->dev_class); + mgmt_device_connected(hdev, conn, 0, NULL, 0); if (!hci_outgoing_auth_needed(hdev, conn)) { conn->state = BT_CONNECTED; @@ -3955,11 +3996,9 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, bacpy(&cp.bdaddr, &ev->bdaddr); memcpy(cp.hash192, data->hash192, sizeof(cp.hash192)); - memcpy(cp.randomizer192, data->randomizer192, - sizeof(cp.randomizer192)); + memcpy(cp.rand192, data->rand192, sizeof(cp.rand192)); memcpy(cp.hash256, data->hash256, sizeof(cp.hash256)); - memcpy(cp.randomizer256, data->randomizer256, - sizeof(cp.randomizer256)); + memcpy(cp.rand256, data->rand256, sizeof(cp.rand256)); hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY, sizeof(cp), &cp); @@ -3968,8 +4007,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev, bacpy(&cp.bdaddr, &ev->bdaddr); memcpy(cp.hash, data->hash192, sizeof(cp.hash)); - memcpy(cp.randomizer, data->randomizer192, - sizeof(cp.randomizer)); + memcpy(cp.rand, data->rand192, sizeof(cp.rand)); hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp), &cp); @@ -4214,8 +4252,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &conn->dst, conn->type, - conn->dst_type, 0, NULL, 0, NULL); + mgmt_device_connected(hdev, conn, 0, NULL, 0); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); @@ -4269,25 +4306,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, } /* This function requires the caller holds hdev->lock */ -static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, - u8 addr_type, u8 adv_type) +static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev, + bdaddr_t *addr, + u8 addr_type, u8 adv_type) { struct hci_conn *conn; struct hci_conn_params *params; /* If the event is not connectable don't proceed further */ if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND) - return; + return NULL; /* Ignore if the device is blocked */ if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type)) - return; + return NULL; /* Most controller will fail if we try to create new connections * while we have an existing one in slave role. */ if (hdev->conn_hash.le_num_slave > 0) - return; + return NULL; /* If we're not connectable only connect devices that we have in * our pend_le_conns list. @@ -4295,7 +4333,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, params = hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type); if (!params) - return; + return NULL; switch (params->auto_connect) { case HCI_AUTO_CONN_DIRECT: @@ -4304,7 +4342,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, * incoming connections from slave devices. */ if (adv_type != LE_ADV_DIRECT_IND) - return; + return NULL; break; case HCI_AUTO_CONN_ALWAYS: /* Devices advertising with ADV_IND or ADV_DIRECT_IND @@ -4315,7 +4353,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, */ break; default: - return; + return NULL; } conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, @@ -4328,7 +4366,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, * count consistent once the connection is established. */ params->conn = hci_conn_get(conn); - return; + return conn; } switch (PTR_ERR(conn)) { @@ -4341,7 +4379,10 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, break; default: BT_DBG("Failed to connect: err %ld", PTR_ERR(conn)); + return NULL; } + + return NULL; } static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, @@ -4349,6 +4390,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, { struct discovery_state *d = &hdev->discovery; struct smp_irk *irk; + struct hci_conn *conn; bool match; u32 flags; @@ -4360,7 +4402,14 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, } /* Check if we have been requested to connect to this device */ - check_pending_le_conn(hdev, bdaddr, bdaddr_type, type); + conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type); + if (conn && type == LE_ADV_IND) { + /* Store report for later inclusion by + * mgmt_device_connected + */ + memcpy(conn->le_adv_data, data, len); + conn->le_adv_data_len = len; + } /* Passive scanning shouldn't trigger any device found events, * except for devices marked as CONN_REPORT for which we do send @@ -4526,8 +4575,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) */ if (ltk->type == SMP_STK) { set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags); - list_del(<k->list); - kfree(ltk); + list_del_rcu(<k->list); + kfree_rcu(ltk, rcu); } else { clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags); } @@ -4735,6 +4784,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_cmd_status_evt(hdev, skb); break; + case HCI_EV_HARDWARE_ERROR: + hci_hardware_error_evt(hdev, skb); + break; + case HCI_EV_ROLE_CHANGE: hci_role_change_evt(hdev, skb); break; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 115f149362ba..bbc4ac748263 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -987,7 +987,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } else { - /* Stand-alone HCI commands must be flaged as + /* Stand-alone HCI commands must be flagged as * single-command requests. */ bt_cb(skb)->req.start = true; diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig index 9332bc7aa851..bc8610b24077 100644 --- a/net/bluetooth/hidp/Kconfig +++ b/net/bluetooth/hidp/Kconfig @@ -1,6 +1,6 @@ config BT_HIDP tristate "HIDP protocol support" - depends on BT && INPUT + depends on BT_BREDR && INPUT select HID help HIDP (Human Interface Device Protocol) is a transport layer diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 1b7d605706aa..cc25d0b74b36 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -736,14 +736,10 @@ static int hidp_setup_hid(struct hidp_session *session, struct hid_device *hid; int err; - session->rd_data = kzalloc(req->rd_size, GFP_KERNEL); - if (!session->rd_data) - return -ENOMEM; + session->rd_data = memdup_user(req->rd_data, req->rd_size); + if (IS_ERR(session->rd_data)) + return PTR_ERR(session->rd_data); - if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) { - err = -EFAULT; - goto fault; - } session->rd_size = req->rd_size; hid = hid_allocate_device(); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b6f9777e057d..8e1273173020 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -424,6 +424,9 @@ struct l2cap_chan *l2cap_chan_create(void) mutex_init(&chan->lock); + /* Set default lock nesting level */ + atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL); + write_lock(&chan_list_lock); list_add(&chan->global_l, &chan_list); write_unlock(&chan_list_lock); @@ -567,7 +570,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) __clear_chan_timer(chan); - BT_DBG("chan %p, conn %p, err %d", chan, conn, err); + BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err, + state_to_string(chan->state)); chan->ops->teardown(chan, err); @@ -3873,9 +3877,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn, hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->dev_flags) && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags)) - mgmt_device_connected(hdev, &hcon->dst, hcon->type, - hcon->dst_type, 0, NULL, 0, - hcon->dev_class); + mgmt_device_connected(hdev, hcon, 0, NULL, 0); hci_dev_unlock(hdev); l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0); @@ -4084,7 +4086,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, chan->num_conf_req++; } - /* Got Conf Rsp PENDING from remote side and asume we sent + /* Got Conf Rsp PENDING from remote side and assume we sent Conf Rsp PENDING in the code above */ if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { @@ -5217,9 +5219,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, u8 *data) { struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data; + struct hci_conn *hcon = conn->hcon; u16 dcid, mtu, mps, credits, result; struct l2cap_chan *chan; - int err; + int err, sec_level; if (cmd_len < sizeof(*rsp)) return -EPROTO; @@ -5258,6 +5261,26 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn, l2cap_chan_ready(chan); break; + case L2CAP_CR_AUTHENTICATION: + case L2CAP_CR_ENCRYPTION: + /* If we already have MITM protection we can't do + * anything. + */ + if (hcon->sec_level > BT_SECURITY_MEDIUM) { + l2cap_chan_del(chan, ECONNREFUSED); + break; + } + + sec_level = hcon->sec_level + 1; + if (chan->sec_level < sec_level) + chan->sec_level = sec_level; + + /* We'll need to send a new Connect Request */ + clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags); + + smp_conn_security(hcon, chan->sec_level); + break; + default: l2cap_chan_del(chan, ECONNREFUSED); break; @@ -5390,7 +5413,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn, mutex_lock(&conn->chan_lock); l2cap_chan_lock(pchan); - if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) { + if (!smp_sufficient_security(conn->hcon, pchan->sec_level, + SMP_ALLOW_STK)) { result = L2CAP_CR_AUTHENTICATION; chan = NULL; goto response_unlock; @@ -5494,6 +5518,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn, if (credits > max_credits) { BT_ERR("LE credits overflow"); l2cap_send_disconn_req(chan, ECONNRESET); + l2cap_chan_unlock(chan); /* Return 0 so that we don't trigger an unnecessary * command reject packet. @@ -7330,7 +7355,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) l2cap_start_connection(chan); else __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); - } else if (chan->state == BT_CONNECT2) { + } else if (chan->state == BT_CONNECT2 && + chan->mode != L2CAP_MODE_LE_FLOWCTL) { struct l2cap_conn_rsp rsp; __u16 res, stat; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 31f106e61ca2..b0efb7202957 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -285,6 +285,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; + /* Listening channels need to use nested locking in order not to + * cause lockdep warnings when the created child channels end up + * being locked in the same thread as the parent channel. + */ + atomic_set(&chan->nesting, L2CAP_NESTING_PARENT); + chan->state = BT_LISTEN; sk->sk_state = BT_LISTEN; @@ -301,7 +307,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, long timeo; int err = 0; - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + lock_sock_nested(sk, L2CAP_NESTING_PARENT); timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); @@ -333,7 +339,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, release_sock(sk); timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + lock_sock_nested(sk, L2CAP_NESTING_PARENT); } __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); @@ -1096,6 +1102,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how) chan = l2cap_pi(sk)->chan; conn = chan->conn; + BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); + if (conn) mutex_lock(&conn->chan_lock); @@ -1153,12 +1161,16 @@ static void l2cap_sock_cleanup_listen(struct sock *parent) { struct sock *sk; - BT_DBG("parent %p", parent); + BT_DBG("parent %p state %s", parent, + state_to_string(parent->sk_state)); /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; + BT_DBG("child chan %p state %s", chan, + state_to_string(chan->state)); + l2cap_chan_lock(chan); __clear_chan_timer(chan); l2cap_chan_close(chan, ECONNRESET); @@ -1246,7 +1258,16 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err) struct sock *sk = chan->data; struct sock *parent; - lock_sock(sk); + BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); + + /* This callback can be called both for server (BT_LISTEN) + * sockets as well as "normal" ones. To avoid lockdep warnings + * with child socket locking (through l2cap_sock_cleanup_listen) + * we need separation into separate nesting levels. The simplest + * way to accomplish this is to inherit the nesting level used + * for the channel. + */ + lock_sock_nested(sk, atomic_read(&chan->nesting)); parent = bt_sk(sk)->parent; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index efb71b022ab6..cbeef5f62f3b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2725,10 +2725,40 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, } if (cp->addr.type == BDADDR_BREDR) { + /* If disconnection is requested, then look up the + * connection. If the remote device is connected, it + * will be later used to terminate the link. + * + * Setting it to NULL explicitly will cause no + * termination of the link. + */ + if (cp->disconnect) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->addr.bdaddr); + else + conn = NULL; + err = hci_remove_link_key(hdev, &cp->addr.bdaddr); } else { u8 addr_type; + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, + &cp->addr.bdaddr); + if (conn) { + /* Defer clearing up the connection parameters + * until closing to give a chance of keeping + * them if a repairing happens. + */ + set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags); + + /* If disconnection is not requested, then + * clear the connection variable so that the + * link is not terminated. + */ + if (!cp->disconnect) + conn = NULL; + } + if (cp->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; else @@ -2736,8 +2766,6 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type); - hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); - err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type); } @@ -2747,17 +2775,9 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } - if (cp->disconnect) { - if (cp->addr.type == BDADDR_BREDR) - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, - &cp->addr.bdaddr); - else - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, - &cp->addr.bdaddr); - } else { - conn = NULL; - } - + /* If the connection variable is set, then termination of the + * link is requested. + */ if (!conn) { err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, &rp, sizeof(rp)); @@ -3062,6 +3082,11 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) hci_conn_put(conn); mgmt_pending_remove(cmd); + + /* The device is paired so there is no need to remove + * its connection parameters anymore. + */ + clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags); } void mgmt_smp_complete(struct hci_conn *conn, bool complete) @@ -3564,8 +3589,16 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, struct mgmt_cp_add_remote_oob_data *cp = data; u8 status; + if (cp->addr.type != BDADDR_BREDR) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_ADD_REMOTE_OOB_DATA, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, - cp->hash, cp->randomizer); + cp->hash, cp->rand); if (err < 0) status = MGMT_STATUS_FAILED; else @@ -3577,11 +3610,17 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, struct mgmt_cp_add_remote_oob_ext_data *cp = data; u8 status; + if (cp->addr.type != BDADDR_BREDR) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_ADD_REMOTE_OOB_DATA, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr, - cp->hash192, - cp->randomizer192, - cp->hash256, - cp->randomizer256); + cp->hash192, cp->rand192, + cp->hash256, cp->rand256); if (err < 0) status = MGMT_STATUS_FAILED; else @@ -3595,6 +3634,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, MGMT_STATUS_INVALID_PARAMS); } +unlock: hci_dev_unlock(hdev); return err; } @@ -3608,14 +3648,26 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); + if (cp->addr.type != BDADDR_BREDR) + return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + hci_dev_lock(hdev); + if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { + hci_remote_oob_data_clear(hdev); + status = MGMT_STATUS_SUCCESS; + goto done; + } + err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); if (err < 0) status = MGMT_STATUS_INVALID_PARAMS; else status = MGMT_STATUS_SUCCESS; +done: err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); @@ -3702,20 +3754,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_POWERED); + err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_POWERED, + &cp->type, sizeof(cp->type)); goto failed; } if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY); + err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_BUSY, &cp->type, + sizeof(cp->type)); goto failed; } if (hdev->discovery.state != DISCOVERY_STOPPED) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY); + err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_BUSY, &cp->type, + sizeof(cp->type)); goto failed; } @@ -3733,15 +3788,18 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, case DISCOV_TYPE_BREDR: status = mgmt_bredr_support(hdev); if (status) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - status); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_DISCOVERY, status, + &cp->type, sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } if (test_bit(HCI_INQUIRY, &hdev->flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_DISCOVERY, + MGMT_STATUS_BUSY, &cp->type, + sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } @@ -3758,16 +3816,19 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, case DISCOV_TYPE_INTERLEAVED: status = mgmt_le_support(hdev); if (status) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - status); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_DISCOVERY, status, + &cp->type, sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_SUPPORTED); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_SUPPORTED, + &cp->type, sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } @@ -3779,9 +3840,11 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, */ if (hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { - err = cmd_status(sk, hdev->id, - MGMT_OP_START_DISCOVERY, - MGMT_STATUS_REJECTED); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_DISCOVERY, + MGMT_STATUS_REJECTED, + &cp->type, + sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } @@ -3804,8 +3867,10 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, */ err = hci_update_random_address(&req, true, &own_addr_type); if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_FAILED); + err = cmd_complete(sk, hdev->id, + MGMT_OP_START_DISCOVERY, + MGMT_STATUS_FAILED, + &cp->type, sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } @@ -3825,8 +3890,9 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, break; default: - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_INVALID_PARAMS); + err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS, + &cp->type, sizeof(cp->type)); mgmt_pending_remove(cmd); goto failed; } @@ -6171,26 +6237,36 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, return eir_len; } -void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u32 flags, u8 *name, u8 name_len, - u8 *dev_class) +void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, + u32 flags, u8 *name, u8 name_len) { char buf[512]; struct mgmt_ev_device_connected *ev = (void *) buf; u16 eir_len = 0; - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_bdaddr(link_type, addr_type); + bacpy(&ev->addr.bdaddr, &conn->dst); + ev->addr.type = link_to_bdaddr(conn->type, conn->dst_type); ev->flags = __cpu_to_le32(flags); - if (name_len > 0) - eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, - name, name_len); + /* We must ensure that the EIR Data fields are ordered and + * unique. Keep it simple for now and avoid the problem by not + * adding any BR/EDR data to the LE adv. + */ + if (conn->le_adv_data_len > 0) { + memcpy(&ev->eir[eir_len], + conn->le_adv_data, conn->le_adv_data_len); + eir_len = conn->le_adv_data_len; + } else { + if (name_len > 0) + eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, + name, name_len); - if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0) - eir_len = eir_append_data(ev->eir, eir_len, - EIR_CLASS_OF_DEV, dev_class, 3); + if (memcmp(conn->dev_class, "\0\0\0", 3) != 0) + eir_len = eir_append_data(ev->eir, eir_len, + EIR_CLASS_OF_DEV, + conn->dev_class, 3); + } ev->eir_len = cpu_to_le16(eir_len); @@ -6693,8 +6769,8 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) } void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, - u8 *randomizer192, u8 *hash256, - u8 *randomizer256, u8 status) + u8 *rand192, u8 *hash256, u8 *rand256, + u8 status) { struct pending_cmd *cmd; @@ -6709,16 +6785,14 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, mgmt_status(status)); } else { if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) && - hash256 && randomizer256) { + hash256 && rand256) { struct mgmt_rp_read_local_oob_ext_data rp; memcpy(rp.hash192, hash192, sizeof(rp.hash192)); - memcpy(rp.randomizer192, randomizer192, - sizeof(rp.randomizer192)); + memcpy(rp.rand192, rand192, sizeof(rp.rand192)); memcpy(rp.hash256, hash256, sizeof(rp.hash256)); - memcpy(rp.randomizer256, randomizer256, - sizeof(rp.randomizer256)); + memcpy(rp.rand256, rand256, sizeof(rp.rand256)); cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 0, @@ -6727,8 +6801,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, struct mgmt_rp_read_local_oob_data rp; memcpy(rp.hash, hash192, sizeof(rp.hash)); - memcpy(rp.randomizer, randomizer192, - sizeof(rp.randomizer)); + memcpy(rp.rand, rand192, sizeof(rp.rand)); cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 0, diff --git a/net/bluetooth/rfcomm/Kconfig b/net/bluetooth/rfcomm/Kconfig index 18d352ea2bc7..335df7515220 100644 --- a/net/bluetooth/rfcomm/Kconfig +++ b/net/bluetooth/rfcomm/Kconfig @@ -1,6 +1,6 @@ config BT_RFCOMM tristate "RFCOMM protocol support" - depends on BT + depends on BT_BREDR help RFCOMM provides connection oriented stream transport. RFCOMM support is required for Dialup Networking, OBEX and other Bluetooth diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index af73bc3acb40..64e20dde4837 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -78,8 +78,10 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s); #define __get_type(b) ((b & 0xef)) #define __test_ea(b) ((b & 0x01)) -#define __test_cr(b) ((b & 0x02)) -#define __test_pf(b) ((b & 0x10)) +#define __test_cr(b) (!!(b & 0x02)) +#define __test_pf(b) (!!(b & 0x10)) + +#define __session_dir(s) ((s)->initiator ? 0x00 : 0x01) #define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) #define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) @@ -388,7 +390,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, return err; } - dlci = __dlci(!s->initiator, channel); + dlci = __dlci(__session_dir(s), channel); /* Check if DLCI already exists */ if (rfcomm_dlc_get(s, dlci)) @@ -543,7 +545,7 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel) rfcomm_lock(); s = rfcomm_session_get(src, dst); if (s) { - dlci = __dlci(!s->initiator, channel); + dlci = __dlci(__session_dir(s), channel); dlc = rfcomm_dlc_get(s, dlci); } rfcomm_unlock(); @@ -904,7 +906,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) hdr->len = __len8(sizeof(*mcc) + 1); mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_NSC); + mcc->type = __mcc_type(0, RFCOMM_NSC); mcc->len = __len8(1); /* Type that we didn't like */ diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f09b6b65cf6b..069b76e03b57 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -191,16 +191,13 @@ int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa) return 0; } -static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], - u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra, - u8 res[16]) +static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16], + u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, + bdaddr_t *ra, u8 res[16]) { - struct hci_dev *hdev = smp->conn->hcon->hdev; u8 p1[16], p2[16]; int err; - BT_DBG("%s", hdev->name); - memset(p1, 0, 16); /* p1 = pres || preq || _rat || _iat */ @@ -218,7 +215,7 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); /* res = e(k, res) */ - err = smp_e(smp->tfm_aes, k, res); + err = smp_e(tfm_aes, k, res); if (err) { BT_ERR("Encrypt data error"); return err; @@ -228,26 +225,23 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); /* res = e(k, res) */ - err = smp_e(smp->tfm_aes, k, res); + err = smp_e(tfm_aes, k, res); if (err) BT_ERR("Encrypt data error"); return err; } -static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16], - u8 _r[16]) +static int smp_s1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r1[16], + u8 r2[16], u8 _r[16]) { - struct hci_dev *hdev = smp->conn->hcon->hdev; int err; - BT_DBG("%s", hdev->name); - /* Just least significant octets from r1 and r2 are considered */ memcpy(_r, r2, 8); memcpy(_r + 8, r1, 8); - err = smp_e(smp->tfm_aes, k, _r); + err = smp_e(tfm_aes, k, _r); if (err) BT_ERR("Encrypt data error"); @@ -389,18 +383,18 @@ static void smp_chan_destroy(struct l2cap_conn *conn) /* If pairing failed clean up any keys we might have */ if (!complete) { if (smp->ltk) { - list_del(&smp->ltk->list); - kfree(smp->ltk); + list_del_rcu(&smp->ltk->list); + kfree_rcu(smp->ltk, rcu); } if (smp->slave_ltk) { - list_del(&smp->slave_ltk->list); - kfree(smp->slave_ltk); + list_del_rcu(&smp->slave_ltk->list); + kfree_rcu(smp->slave_ltk, rcu); } if (smp->remote_irk) { - list_del(&smp->remote_irk->list); - kfree(smp->remote_irk); + list_del_rcu(&smp->remote_irk->list); + kfree_rcu(smp->remote_irk, rcu); } } @@ -520,8 +514,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, set_bit(SMP_FLAG_TK_VALID, &smp->flags); } - hci_dev_lock(hcon->hdev); - if (method == REQ_PASSKEY) ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type); @@ -534,8 +526,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, hcon->type, hcon->dst_type, passkey, 0); - hci_dev_unlock(hcon->hdev); - return ret; } @@ -547,7 +537,7 @@ static u8 smp_confirm(struct smp_chan *smp) BT_DBG("conn %p", conn); - ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp, + ret = smp_c1(smp->tfm_aes, smp->tk, smp->prnd, smp->preq, smp->prsp, conn->hcon->init_addr_type, &conn->hcon->init_addr, conn->hcon->resp_addr_type, &conn->hcon->resp_addr, cp.confirm_val); @@ -578,7 +568,7 @@ static u8 smp_random(struct smp_chan *smp) BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp, + ret = smp_c1(smp->tfm_aes, smp->tk, smp->rrnd, smp->preq, smp->prsp, hcon->init_addr_type, &hcon->init_addr, hcon->resp_addr_type, &hcon->resp_addr, confirm); if (ret) @@ -594,7 +584,7 @@ static u8 smp_random(struct smp_chan *smp) __le64 rand = 0; __le16 ediv = 0; - smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk); + smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); @@ -613,7 +603,7 @@ static u8 smp_random(struct smp_chan *smp) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); - smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk); + smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); @@ -665,8 +655,8 @@ static void smp_notify_keys(struct l2cap_conn *conn) * just remove it. */ if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) { - list_del(&smp->remote_irk->list); - kfree(smp->remote_irk); + list_del_rcu(&smp->remote_irk->list); + kfree_rcu(smp->remote_irk, rcu); smp->remote_irk = NULL; } } @@ -970,7 +960,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (sec_level > conn->hcon->pending_sec_level) conn->hcon->pending_sec_level = sec_level; - /* If we need MITM check that it can be acheived */ + /* If we need MITM check that it can be achieved */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { u8 method; @@ -1028,7 +1018,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) auth = rsp->auth_req & AUTH_REQ_MASK; - /* If we need MITM check that it can be acheived */ + /* If we need MITM check that it can be achieved */ if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { u8 method; @@ -1132,18 +1122,20 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) return true; } -bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level) +bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level, + enum smp_key_pref key_pref) { if (sec_level == BT_SECURITY_LOW) return true; - /* If we're encrypted with an STK always claim insufficient - * security. This way we allow the connection to be re-encrypted - * with an LTK, even if the LTK provides the same level of - * security. Only exception is if we don't have an LTK (e.g. - * because of key distribution bits). + /* If we're encrypted with an STK but the caller prefers using + * LTK claim insufficient security. This way we allow the + * connection to be re-encrypted with an LTK, even if the LTK + * provides the same level of security. Only exception is if we + * don't have an LTK (e.g. because of key distribution bits). */ - if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) && + if (key_pref == SMP_USE_LTK && + test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) && hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role)) return false; @@ -1177,7 +1169,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) else sec_level = authreq_to_seclevel(auth); - if (smp_sufficient_security(hcon, sec_level)) + if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK)) return 0; if (sec_level > hcon->pending_sec_level) @@ -1227,7 +1219,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) return 1; - if (smp_sufficient_security(hcon, sec_level)) + if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK)) return 1; if (sec_level > hcon->pending_sec_level) @@ -1329,7 +1321,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rp)); - hci_dev_lock(hdev); authenticated = (hcon->sec_level == BT_SECURITY_HIGH); ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK, authenticated, smp->tk, smp->enc_key_size, @@ -1337,7 +1328,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) smp->ltk = ltk; if (!(smp->remote_key_dist & KEY_DIST_MASK)) smp_distribute_keys(smp); - hci_dev_unlock(hdev); return 0; } @@ -1384,8 +1374,6 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, skb_pull(skb, sizeof(*info)); - hci_dev_lock(hcon->hdev); - /* Strictly speaking the Core Specification (4.1) allows sending * an empty address which would force us to rely on just the IRK * as "identity information". However, since such @@ -1413,8 +1401,6 @@ distribute: if (!(smp->remote_key_dist & KEY_DIST_MASK)) smp_distribute_keys(smp); - hci_dev_unlock(hcon->hdev); - return 0; } @@ -1423,7 +1409,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) struct smp_cmd_sign_info *rp = (void *) skb->data; struct l2cap_chan *chan = conn->smp; struct smp_chan *smp = chan->data; - struct hci_dev *hdev = conn->hcon->hdev; struct smp_csrk *csrk; BT_DBG("conn %p", conn); @@ -1436,7 +1421,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rp)); - hci_dev_lock(hdev); csrk = kzalloc(sizeof(*csrk), GFP_KERNEL); if (csrk) { csrk->master = 0x01; @@ -1444,7 +1428,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb) } smp->csrk = csrk; smp_distribute_keys(smp); - hci_dev_unlock(hdev); return 0; } @@ -1668,6 +1651,13 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan) chan->omtu = pchan->omtu; chan->mode = pchan->mode; + /* Other L2CAP channels may request SMP routines in order to + * change the security level. This means that the SMP channel + * lock must be considered in its own category to avoid lockdep + * warnings. + */ + atomic_set(&chan->nesting, L2CAP_NESTING_SMP); + BT_DBG("created chan %p", chan); return chan; @@ -1699,7 +1689,7 @@ int smp_register(struct hci_dev *hdev) BT_DBG("%s", hdev->name); - tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0); if (IS_ERR(tfm_aes)) { int err = PTR_ERR(tfm_aes); BT_ERR("Unable to create crypto context"); @@ -1725,6 +1715,9 @@ int smp_register(struct hci_dev *hdev) chan->imtu = L2CAP_DEFAULT_MTU; chan->ops = &smp_root_chan_ops; + /* Set correct nesting level for a parent/listening channel */ + atomic_set(&chan->nesting, L2CAP_NESTING_PARENT); + hdev->smp_data = chan; return 0; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 86a683a8b491..f76083b85005 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -133,8 +133,15 @@ static inline u8 smp_ltk_sec_level(struct smp_ltk *key) return BT_SECURITY_MEDIUM; } +/* Key preferences for smp_sufficient security */ +enum smp_key_pref { + SMP_ALLOW_STK, + SMP_USE_LTK, +}; + /* SMP Commands */ -bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level); +bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level, + enum smp_key_pref key_pref); int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey); diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c index 44136297b673..290e14f2e92e 100644 --- a/net/ieee802154/6lowpan_rtnl.c +++ b/net/ieee802154/6lowpan_rtnl.c @@ -49,8 +49,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/netdevice.h> +#include <linux/ieee802154.h> #include <net/af_ieee802154.h> -#include <net/ieee802154.h> #include <net/ieee802154_netdev.h> #include <net/6lowpan.h> #include <net/ipv6.h> @@ -58,12 +58,13 @@ #include "reassembly.h" static LIST_HEAD(lowpan_devices); +static int lowpan_open_count; /* private device info */ struct lowpan_dev_info { struct net_device *real_dev; /* real WPAN device ptr */ struct mutex dev_list_mtx; /* mutex for list ops */ - __be16 fragment_tag; + u16 fragment_tag; }; struct lowpan_dev_record { @@ -140,24 +141,33 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb, struct sk_buff *skb_cp; int stat = NET_RX_SUCCESS; + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + rcu_read_lock(); list_for_each_entry_rcu(entry, &lowpan_devices, list) if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) { skb_cp = skb_copy(skb, GFP_ATOMIC); if (!skb_cp) { - stat = -ENOMEM; - break; + kfree_skb(skb); + rcu_read_unlock(); + return NET_RX_DROP; } skb_cp->dev = entry->ldev; stat = netif_rx(skb_cp); + if (stat == NET_RX_DROP) + break; } rcu_read_unlock(); + consume_skb(skb); + return stat; } -static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) +static int +iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr) { u8 iphc0, iphc1; struct ieee802154_addr_sa sa, da; @@ -166,13 +176,13 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); /* at least two bytes will be used for the encoding */ if (skb->len < 2) - goto drop; + return -EINVAL; if (lowpan_fetch_skb_u8(skb, &iphc0)) - goto drop; + return -EINVAL; if (lowpan_fetch_skb_u8(skb, &iphc1)) - goto drop; + return -EINVAL; ieee802154_addr_to_sa(&sa, &hdr->source); ieee802154_addr_to_sa(&da, &hdr->dest); @@ -187,27 +197,9 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr) else dap = &da.hwaddr; - return lowpan_process_data(skb, skb->dev, sap, sa.addr_type, - IEEE802154_ADDR_LEN, dap, da.addr_type, - IEEE802154_ADDR_LEN, iphc0, iphc1, - lowpan_give_skb_to_devices); - -drop: - kfree_skb(skb); - return -EINVAL; -} - -static int lowpan_set_address(struct net_device *dev, void *p) -{ - struct sockaddr *sa = p; - - if (netif_running(dev)) - return -EBUSY; - - /* TODO: validate addr */ - memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); - - return 0; + return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type, + IEEE802154_ADDR_LEN, dap, da.addr_type, + IEEE802154_ADDR_LEN, iphc0, iphc1); } static struct sk_buff* @@ -233,7 +225,7 @@ lowpan_alloc_frag(struct sk_buff *skb, int size, &master_hdr->source, size); if (rc < 0) { kfree_skb(frag); - return ERR_PTR(-rc); + return ERR_PTR(rc); } } else { frag = ERR_PTR(-ENOMEM); @@ -275,7 +267,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, dgram_size = lowpan_uncompress_size(skb, &dgram_offset) - skb->mac_len; - frag_tag = lowpan_dev_info(dev)->fragment_tag++; + frag_tag = htons(lowpan_dev_info(dev)->fragment_tag); + lowpan_dev_info(dev)->fragment_tag++; frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07); frag_hdr[1] = dgram_size & 0xff; @@ -294,7 +287,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, frag_len + skb_network_header_len(skb)); if (rc) { pr_debug("%s unable to send FRAG1 packet (tag: %d)", - __func__, frag_tag); + __func__, ntohs(frag_tag)); goto err; } @@ -315,7 +308,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev, frag_len); if (rc) { pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n", - __func__, frag_tag, skb_offset); + __func__, ntohs(frag_tag), skb_offset); goto err; } } while (skb_unprocessed > frag_cap); @@ -410,13 +403,6 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) } } -static struct wpan_phy *lowpan_get_phy(const struct net_device *dev) -{ - struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; - - return ieee802154_mlme_ops(real_dev)->get_phy(real_dev); -} - static __le16 lowpan_get_pan_id(const struct net_device *dev) { struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; @@ -464,12 +450,10 @@ static int lowpan_dev_init(struct net_device *dev) static const struct net_device_ops lowpan_netdev_ops = { .ndo_init = lowpan_dev_init, .ndo_start_xmit = lowpan_xmit, - .ndo_set_mac_address = lowpan_set_address, }; static struct ieee802154_mlme_ops lowpan_mlme = { .get_pan_id = lowpan_get_pan_id, - .get_phy = lowpan_get_phy, .get_short_addr = lowpan_get_short_addr, .get_dsn = lowpan_get_dsn, }; @@ -515,6 +499,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, if (!netif_running(dev)) goto drop_skb; + if (skb->pkt_type == PACKET_OTHERHOST) + goto drop_skb; + if (dev->type != ARPHRD_IEEE802154) goto drop_skb; @@ -523,55 +510,67 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, /* check that it's our buffer */ if (skb->data[0] == LOWPAN_DISPATCH_IPV6) { - skb->protocol = htons(ETH_P_IPV6); - skb->pkt_type = PACKET_HOST; - /* Pull off the 1-byte of 6lowpan header. */ skb_pull(skb, 1); - - ret = lowpan_give_skb_to_devices(skb, NULL); - if (ret == NET_RX_DROP) - goto drop; + return lowpan_give_skb_to_devices(skb, NULL); } else { switch (skb->data[0] & 0xe0) { case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */ - ret = process_data(skb, &hdr); - if (ret == NET_RX_DROP) - goto drop; - break; + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); case LOWPAN_DISPATCH_FRAG1: /* first fragment header */ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1); if (ret == 1) { - ret = process_data(skb, &hdr); - if (ret == NET_RX_DROP) - goto drop; + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + } else if (ret == -1) { + return NET_RX_DROP; + } else { + return NET_RX_SUCCESS; } - break; case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */ ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN); if (ret == 1) { - ret = process_data(skb, &hdr); - if (ret == NET_RX_DROP) - goto drop; + ret = iphc_decompress(skb, &hdr); + if (ret < 0) + goto drop_skb; + + return lowpan_give_skb_to_devices(skb, NULL); + } else if (ret == -1) { + return NET_RX_DROP; + } else { + return NET_RX_SUCCESS; } - break; default: break; } } - return NET_RX_SUCCESS; drop_skb: kfree_skb(skb); drop: return NET_RX_DROP; } +static struct packet_type lowpan_packet_type = { + .type = htons(ETH_P_IEEE802154), + .func = lowpan_rcv, +}; + static int lowpan_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { struct net_device *real_dev; struct lowpan_dev_record *entry; + int ret; + + ASSERT_RTNL(); pr_debug("adding new link\n"); @@ -606,9 +605,14 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, list_add_tail(&entry->list, &lowpan_devices); mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); - register_netdevice(dev); + ret = register_netdevice(dev); + if (ret >= 0) { + if (!lowpan_open_count) + dev_add_pack(&lowpan_packet_type); + lowpan_open_count++; + } - return 0; + return ret; } static void lowpan_dellink(struct net_device *dev, struct list_head *head) @@ -619,6 +623,10 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head) ASSERT_RTNL(); + lowpan_open_count--; + if (!lowpan_open_count) + dev_remove_pack(&lowpan_packet_type); + mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { if (entry->ldev == dev) { @@ -681,11 +689,6 @@ static struct notifier_block lowpan_dev_notifier = { .notifier_call = lowpan_device_event, }; -static struct packet_type lowpan_packet_type = { - .type = htons(ETH_P_IEEE802154), - .func = lowpan_rcv, -}; - static int __init lowpan_init_module(void) { int err = 0; @@ -698,8 +701,6 @@ static int __init lowpan_init_module(void) if (err < 0) goto out_frag; - dev_add_pack(&lowpan_packet_type); - err = register_netdevice_notifier(&lowpan_dev_notifier); if (err < 0) goto out_pack; @@ -707,7 +708,6 @@ static int __init lowpan_init_module(void) return 0; out_pack: - dev_remove_pack(&lowpan_packet_type); lowpan_netlink_fini(); out_frag: lowpan_net_frag_exit(); @@ -719,8 +719,6 @@ static void __exit lowpan_cleanup_module(void) { lowpan_netlink_fini(); - dev_remove_pack(&lowpan_packet_type); - lowpan_net_frag_exit(); unregister_netdevice_notifier(&lowpan_dev_notifier); diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index 3914b1ed4274..9f6970f2a28b 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -2,8 +2,8 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o -ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \ - header_ops.o +ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \ + header_ops.o sysfs.o nl802154.o af_802154-y := af_ieee802154.o raw.o dgram.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h index 8330a09bfc95..343b63e6f953 100644 --- a/net/ieee802154/af802154.h +++ b/net/ieee802154/af802154.h @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c index 29e0de63001b..26da1e179737 100644 --- a/net/ieee802154/af_ieee802154.c +++ b/net/ieee802154/af_ieee802154.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Maxim Gorbachyov <maxim.gorbachev@siemens.com> diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c new file mode 100644 index 000000000000..18bc7e738507 --- /dev/null +++ b/net/ieee802154/core.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2007, 2008, 2009 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> + +#include <net/cfg802154.h> +#include <net/rtnetlink.h> + +#include "ieee802154.h" +#include "nl802154.h" +#include "sysfs.h" +#include "core.h" + +/* RCU-protected (and RTNL for writers) */ +LIST_HEAD(cfg802154_rdev_list); +int cfg802154_rdev_list_generation; + +static int wpan_phy_match(struct device *dev, const void *data) +{ + return !strcmp(dev_name(dev), (const char *)data); +} + +struct wpan_phy *wpan_phy_find(const char *str) +{ + struct device *dev; + + if (WARN_ON(!str)) + return NULL; + + dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match); + if (!dev) + return NULL; + + return container_of(dev, struct wpan_phy, dev); +} +EXPORT_SYMBOL(wpan_phy_find); + +struct wpan_phy_iter_data { + int (*fn)(struct wpan_phy *phy, void *data); + void *data; +}; + +static int wpan_phy_iter(struct device *dev, void *_data) +{ + struct wpan_phy_iter_data *wpid = _data; + struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); + + return wpid->fn(phy, wpid->data); +} + +int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), + void *data) +{ + struct wpan_phy_iter_data wpid = { + .fn = fn, + .data = data, + }; + + return class_for_each_device(&wpan_phy_class, NULL, + &wpid, wpan_phy_iter); +} +EXPORT_SYMBOL(wpan_phy_for_each); + +struct cfg802154_registered_device * +cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx) +{ + struct cfg802154_registered_device *result = NULL, *rdev; + + ASSERT_RTNL(); + + list_for_each_entry(rdev, &cfg802154_rdev_list, list) { + if (rdev->wpan_phy_idx == wpan_phy_idx) { + result = rdev; + break; + } + } + + return result; +} + +struct wpan_phy * +wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size) +{ + static atomic_t wpan_phy_counter = ATOMIC_INIT(0); + struct cfg802154_registered_device *rdev; + size_t alloc_size; + + alloc_size = sizeof(*rdev) + priv_size; + rdev = kzalloc(alloc_size, GFP_KERNEL); + if (!rdev) + return NULL; + + rdev->ops = ops; + + rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter); + + if (unlikely(rdev->wpan_phy_idx < 0)) { + /* ugh, wrapped! */ + atomic_dec(&wpan_phy_counter); + kfree(rdev); + return NULL; + } + + /* atomic_inc_return makes it start at 1, make it start at 0 */ + rdev->wpan_phy_idx--; + + mutex_init(&rdev->wpan_phy.pib_lock); + + INIT_LIST_HEAD(&rdev->wpan_dev_list); + device_initialize(&rdev->wpan_phy.dev); + dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx); + + rdev->wpan_phy.dev.class = &wpan_phy_class; + rdev->wpan_phy.dev.platform_data = rdev; + + init_waitqueue_head(&rdev->dev_wait); + + return &rdev->wpan_phy; +} +EXPORT_SYMBOL(wpan_phy_new); + +int wpan_phy_register(struct wpan_phy *phy) +{ + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); + int ret; + + rtnl_lock(); + ret = device_add(&phy->dev); + if (ret) { + rtnl_unlock(); + return ret; + } + + list_add_rcu(&rdev->list, &cfg802154_rdev_list); + cfg802154_rdev_list_generation++; + + /* TODO phy registered lock */ + rtnl_unlock(); + + /* TODO nl802154 phy notify */ + + return 0; +} +EXPORT_SYMBOL(wpan_phy_register); + +void wpan_phy_unregister(struct wpan_phy *phy) +{ + struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy); + + wait_event(rdev->dev_wait, ({ + int __count; + rtnl_lock(); + __count = rdev->opencount; + rtnl_unlock(); + __count == 0; })); + + rtnl_lock(); + /* TODO nl802154 phy notify */ + /* TODO phy registered lock */ + + WARN_ON(!list_empty(&rdev->wpan_dev_list)); + + /* First remove the hardware from everywhere, this makes + * it impossible to find from userspace. + */ + list_del_rcu(&rdev->list); + synchronize_rcu(); + + cfg802154_rdev_list_generation++; + + device_del(&phy->dev); + + rtnl_unlock(); +} +EXPORT_SYMBOL(wpan_phy_unregister); + +void wpan_phy_free(struct wpan_phy *phy) +{ + put_device(&phy->dev); +} +EXPORT_SYMBOL(wpan_phy_free); + +void cfg802154_dev_free(struct cfg802154_registered_device *rdev) +{ + kfree(rdev); +} + +static void +cfg802154_update_iface_num(struct cfg802154_registered_device *rdev, + int iftype, int num) +{ + ASSERT_RTNL(); + + rdev->num_running_ifaces += num; +} + +static int cfg802154_netdev_notifier_call(struct notifier_block *nb, + unsigned long state, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + struct cfg802154_registered_device *rdev; + + if (!wpan_dev) + return NOTIFY_DONE; + + rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); + + /* TODO WARN_ON unspec type */ + + switch (state) { + /* TODO NETDEV_DEVTYPE */ + case NETDEV_REGISTER: + wpan_dev->identifier = ++rdev->wpan_dev_id; + list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list); + rdev->devlist_generation++; + + wpan_dev->netdev = dev; + break; + case NETDEV_DOWN: + cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1); + + rdev->opencount--; + wake_up(&rdev->dev_wait); + break; + case NETDEV_UP: + cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1); + + rdev->opencount++; + break; + case NETDEV_UNREGISTER: + /* It is possible to get NETDEV_UNREGISTER + * multiple times. To detect that, check + * that the interface is still on the list + * of registered interfaces, and only then + * remove and clean it up. + */ + if (!list_empty(&wpan_dev->list)) { + list_del_rcu(&wpan_dev->list); + rdev->devlist_generation++; + } + /* synchronize (so that we won't find this netdev + * from other code any more) and then clear the list + * head so that the above code can safely check for + * !list_empty() to avoid double-cleanup. + */ + synchronize_rcu(); + INIT_LIST_HEAD(&wpan_dev->list); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static struct notifier_block cfg802154_netdev_notifier = { + .notifier_call = cfg802154_netdev_notifier_call, +}; + +static int __init wpan_phy_class_init(void) +{ + int rc; + + rc = wpan_phy_sysfs_init(); + if (rc) + goto err; + + rc = register_netdevice_notifier(&cfg802154_netdev_notifier); + if (rc) + goto err_nl; + + rc = ieee802154_nl_init(); + if (rc) + goto err_notifier; + + rc = nl802154_init(); + if (rc) + goto err_ieee802154_nl; + + return 0; + +err_ieee802154_nl: + ieee802154_nl_exit(); + +err_notifier: + unregister_netdevice_notifier(&cfg802154_netdev_notifier); +err_nl: + wpan_phy_sysfs_exit(); +err: + return rc; +} +subsys_initcall(wpan_phy_class_init); + +static void __exit wpan_phy_class_exit(void) +{ + nl802154_exit(); + ieee802154_nl_exit(); + unregister_netdevice_notifier(&cfg802154_netdev_notifier); + wpan_phy_sysfs_exit(); +} +module_exit(wpan_phy_class_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface"); +MODULE_AUTHOR("Dmitry Eremin-Solenikov"); + diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h new file mode 100644 index 000000000000..f3e95580caee --- /dev/null +++ b/net/ieee802154/core.h @@ -0,0 +1,46 @@ +#ifndef __IEEE802154_CORE_H +#define __IEEE802154_CORE_H + +#include <net/cfg802154.h> + +struct cfg802154_registered_device { + const struct cfg802154_ops *ops; + struct list_head list; + + /* wpan_phy index, internal only */ + int wpan_phy_idx; + + /* also protected by devlist_mtx */ + int opencount; + wait_queue_head_t dev_wait; + + /* protected by RTNL only */ + int num_running_ifaces; + + /* associated wpan interfaces, protected by rtnl or RCU */ + struct list_head wpan_dev_list; + int devlist_generation, wpan_dev_id; + + /* must be last because of the way we do wpan_phy_priv(), + * and it should at least be aligned to NETDEV_ALIGN + */ + struct wpan_phy wpan_phy __aligned(NETDEV_ALIGN); +}; + +static inline struct cfg802154_registered_device * +wpan_phy_to_rdev(struct wpan_phy *wpan_phy) +{ + BUG_ON(!wpan_phy); + return container_of(wpan_phy, struct cfg802154_registered_device, + wpan_phy); +} + +extern struct list_head cfg802154_rdev_list; +extern int cfg802154_rdev_list_generation; + +/* free object */ +void cfg802154_dev_free(struct cfg802154_registered_device *rdev); +struct cfg802154_registered_device * +cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx); + +#endif /* __IEEE802154_CORE_H */ diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c index ef2ad8aaef13..3d58befef467 100644 --- a/net/ieee802154/dgram.c +++ b/net/ieee802154/dgram.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> @@ -27,9 +23,9 @@ #include <linux/if_arp.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/ieee802154.h> #include <net/sock.h> #include <net/af_ieee802154.h> -#include <net/ieee802154.h> #include <net/ieee802154_netdev.h> #include <asm/ioctls.h> diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c index c09294e39ca6..a051b6993177 100644 --- a/net/ieee802154/header_ops.c +++ b/net/ieee802154/header_ops.c @@ -14,8 +14,9 @@ * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> */ +#include <linux/ieee802154.h> + #include <net/mac802154.h> -#include <net/ieee802154.h> #include <net/ieee802154_netdev.h> static int diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h index 5d352f86979e..a5d7515b7f62 100644 --- a/net/ieee802154/ieee802154.h +++ b/net/ieee802154/ieee802154.h @@ -10,16 +10,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #ifndef IEEE_802154_LOCAL_H #define IEEE_802154_LOCAL_H int __init ieee802154_nl_init(void); -void __exit ieee802154_nl_exit(void); +void ieee802154_nl_exit(void); #define IEEE802154_OP(_cmd, _func) \ { \ diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 9222966f5e6d..63ee7d66950e 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> @@ -159,7 +155,7 @@ int __init ieee802154_nl_init(void) ieee802154_mcgrps); } -void __exit ieee802154_nl_exit(void) +void ieee802154_nl_exit(void) { genl_unregister_family(&nl802154_family); } diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c index c6bfe22bfa5e..fe77f0c770b8 100644 --- a/net/ieee802154/nl-mac.c +++ b/net/ieee802154/nl-mac.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> @@ -26,16 +22,15 @@ #include <linux/kernel.h> #include <linux/if_arp.h> #include <linux/netdevice.h> +#include <linux/ieee802154.h> #include <net/netlink.h> #include <net/genetlink.h> #include <net/sock.h> #include <linux/nl802154.h> #include <linux/export.h> #include <net/af_ieee802154.h> -#include <net/nl802154.h> -#include <net/ieee802154.h> #include <net/ieee802154_netdev.h> -#include <net/wpan-phy.h> +#include <net/cfg802154.h> #include "ieee802154.h" @@ -59,186 +54,7 @@ static __le16 nla_get_shortaddr(const struct nlattr *nla) return cpu_to_le16(nla_get_u16(nla)); } -int ieee802154_nl_assoc_indic(struct net_device *dev, - struct ieee802154_addr *addr, - u8 cap) -{ - struct sk_buff *msg; - - pr_debug("%s\n", __func__); - - if (addr->mode != IEEE802154_ADDR_LONG) { - pr_err("%s: received non-long source address!\n", __func__); - return -EINVAL; - } - - msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC); - if (!msg) - return -ENOBUFS; - - if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || - nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || - nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, - dev->dev_addr) || - nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, - addr->extended_addr) || - nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap)) - goto nla_put_failure; - - return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} -EXPORT_SYMBOL(ieee802154_nl_assoc_indic); - -int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr, - u8 status) -{ - struct sk_buff *msg; - - pr_debug("%s\n", __func__); - - msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF); - if (!msg) - return -ENOBUFS; - - if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || - nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || - nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, - dev->dev_addr) || - nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) || - nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) - goto nla_put_failure; - return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} -EXPORT_SYMBOL(ieee802154_nl_assoc_confirm); - -int ieee802154_nl_disassoc_indic(struct net_device *dev, - struct ieee802154_addr *addr, - u8 reason) -{ - struct sk_buff *msg; - - pr_debug("%s\n", __func__); - - msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC); - if (!msg) - return -ENOBUFS; - - if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || - nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || - nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, - dev->dev_addr)) - goto nla_put_failure; - if (addr->mode == IEEE802154_ADDR_LONG) { - if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR, - addr->extended_addr)) - goto nla_put_failure; - } else { - if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR, - addr->short_addr)) - goto nla_put_failure; - } - if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason)) - goto nla_put_failure; - return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} -EXPORT_SYMBOL(ieee802154_nl_disassoc_indic); - -int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status) -{ - struct sk_buff *msg; - - pr_debug("%s\n", __func__); - - msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF); - if (!msg) - return -ENOBUFS; - - if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || - nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || - nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, - dev->dev_addr) || - nla_put_u8(msg, IEEE802154_ATTR_STATUS, status)) - goto nla_put_failure; - return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} -EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm); - -int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid, - __le16 coord_addr) -{ - struct sk_buff *msg; - - pr_debug("%s\n", __func__); - - msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC); - if (!msg) - return -ENOBUFS; - - if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || - nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || - nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, - dev->dev_addr) || - nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, - coord_addr) || - nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid)) - goto nla_put_failure; - return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} -EXPORT_SYMBOL(ieee802154_nl_beacon_indic); - -int ieee802154_nl_scan_confirm(struct net_device *dev, - u8 status, u8 scan_type, - u32 unscanned, u8 page, - u8 *edl/* , struct list_head *pan_desc_list */) -{ - struct sk_buff *msg; - - pr_debug("%s\n", __func__); - - msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF); - if (!msg) - return -ENOBUFS; - - if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) || - nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) || - nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, - dev->dev_addr) || - nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) || - nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) || - nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) || - nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) || - (edl && - nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl))) - goto nla_put_failure; - return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} -EXPORT_SYMBOL(ieee802154_nl_scan_confirm); - -int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) +static int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) { struct sk_buff *msg; @@ -278,8 +94,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, goto out; ops = ieee802154_mlme_ops(dev); - phy = ops->get_phy(dev); + phy = dev->ieee802154_ptr->wpan_phy; BUG_ON(!phy); + get_device(&phy->dev); short_addr = ops->get_short_addr(dev); pan_id = ops->get_pan_id(dev); @@ -296,7 +113,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid, if (ops->get_mac_params) { struct ieee802154_mac_params params; + rtnl_lock(); ops->get_mac_params(dev, ¶ms); + rtnl_unlock(); if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER, params.transmit_power) || @@ -347,7 +166,10 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) if (!dev) return NULL; - if (dev->type != ARPHRD_IEEE802154) { + /* Check on mtu is currently a hacked solution because lowpan + * and wpan have the same ARPHRD type. + */ + if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) { dev_put(dev); return NULL; } @@ -481,7 +303,7 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) u8 channel, bcn_ord, sf_ord; u8 page; int pan_coord, blx, coord_realign; - int ret = -EOPNOTSUPP; + int ret = -EBUSY; if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] || !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] || @@ -497,8 +319,14 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) dev = ieee802154_nl_get_dev(info); if (!dev) return -ENODEV; - if (!ieee802154_mlme_ops(dev)->start_req) + + if (netif_running(dev)) + goto out; + + if (!ieee802154_mlme_ops(dev)->start_req) { + ret = -EOPNOTSUPP; goto out; + } addr.mode = IEEE802154_ADDR_SHORT; addr.short_addr = nla_get_shortaddr( @@ -525,8 +353,15 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + rtnl_lock(); ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, bcn_ord, sf_ord, pan_coord, blx, coord_realign); + rtnl_unlock(); + + /* FIXME: add validation for unused parameters to be sane + * for SoftMAC + */ + ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS); out: dev_put(dev); @@ -616,7 +451,11 @@ int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb) idx = 0; for_each_netdev(net, dev) { - if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) + /* Check on mtu is currently a hacked solution because lowpan + * and wpan have the same ARPHRD type. + */ + if (idx < s_idx || dev->type != ARPHRD_IEEE802154 || + dev->mtu != IEEE802154_MTU) goto cont; if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid, @@ -666,22 +505,10 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) !info->attrs[IEEE802154_ATTR_FRAME_RETRIES]) goto out; - phy = ops->get_phy(dev); - - if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) || - (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) || - (!phy->set_cca_ed_level && - info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) || - (!phy->set_csma_params && - (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] || - info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] || - info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) || - (!phy->set_frame_retries && - info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) { - rc = -EOPNOTSUPP; - goto out_phy; - } + phy = dev->ieee802154_ptr->wpan_phy; + get_device(&phy->dev); + rtnl_lock(); ops->get_mac_params(dev, ¶ms); if (info->attrs[IEEE802154_ATTR_TXPOWER]) @@ -709,13 +536,13 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info) params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]); rc = ops->set_mac_params(dev, ¶ms); + rtnl_unlock(); wpan_phy_put(phy); dev_put(dev); - return rc; -out_phy: - wpan_phy_put(phy); + return 0; + out: dev_put(dev); return rc; @@ -962,7 +789,11 @@ ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb, int rc; for_each_netdev(net, dev) { - if (idx < first_dev || dev->type != ARPHRD_IEEE802154) + /* Check on mtu is currently a hacked solution because lowpan + * and wpan have the same ARPHRD type. + */ + if (idx < first_dev || dev->type != ARPHRD_IEEE802154 || + dev->mtu != IEEE802154_MTU) goto skip; data.ops = ieee802154_mlme_ops(dev); diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c index 972baf83411a..80a946dddd90 100644 --- a/net/ieee802154/nl-phy.c +++ b/net/ieee802154/nl-phy.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> @@ -27,13 +23,15 @@ #include <linux/if_arp.h> #include <net/netlink.h> #include <net/genetlink.h> -#include <net/wpan-phy.h> +#include <net/cfg802154.h> #include <net/af_ieee802154.h> #include <net/ieee802154_netdev.h> #include <net/rtnetlink.h> /* for rtnl_{un,}lock */ #include <linux/nl802154.h> #include "ieee802154.h" +#include "rdev-ops.h" +#include "core.h" static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct wpan_phy *phy) @@ -207,11 +205,6 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info) if (!msg) goto out_dev; - if (!phy->add_iface) { - rc = -EINVAL; - goto nla_put_failure; - } - if (info->attrs[IEEE802154_ATTR_HW_ADDR] && nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) != IEEE802154_ADDR_LEN) { @@ -227,11 +220,13 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info) } } - dev = phy->add_iface(phy, devname, type); + dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname, + type); if (IS_ERR(dev)) { rc = PTR_ERR(dev); goto nla_put_failure; } + dev_hold(dev); if (info->attrs[IEEE802154_ATTR_HW_ADDR]) { struct sockaddr addr; @@ -261,7 +256,7 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info) dev_unregister: rtnl_lock(); /* del_iface must be called with RTNL lock */ - phy->del_iface(phy, dev); + rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev); dev_put(dev); rtnl_unlock(); nla_put_failure: @@ -292,8 +287,9 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info) if (!dev) return -ENODEV; - phy = ieee802154_mlme_ops(dev)->get_phy(dev); + phy = dev->ieee802154_ptr->wpan_phy; BUG_ON(!phy); + get_device(&phy->dev); rc = -EINVAL; /* phy name is optional, but should be checked if it's given */ @@ -323,13 +319,8 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info) if (!msg) goto out_dev; - if (!phy->del_iface) { - rc = -EINVAL; - goto nla_put_failure; - } - rtnl_lock(); - phy->del_iface(phy, dev); + rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev); /* We don't have device anymore */ dev_put(dev); diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c new file mode 100644 index 000000000000..889647744697 --- /dev/null +++ b/net/ieee802154/nl802154.c @@ -0,0 +1,957 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Authors: + * Alexander Aring <aar@pengutronix.de> + * + * Based on: net/wireless/nl80211.c + */ + +#include <linux/rtnetlink.h> + +#include <net/cfg802154.h> +#include <net/genetlink.h> +#include <net/mac802154.h> +#include <net/netlink.h> +#include <net/nl802154.h> +#include <net/sock.h> + +#include "nl802154.h" +#include "rdev-ops.h" +#include "core.h" + +static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info); + +static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info); + +/* the netlink family */ +static struct genl_family nl802154_fam = { + .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ + .name = NL802154_GENL_NAME, /* have users key off the name instead */ + .hdrsize = 0, /* no private header */ + .version = 1, /* no particular meaning now */ + .maxattr = NL802154_ATTR_MAX, + .netnsok = true, + .pre_doit = nl802154_pre_doit, + .post_doit = nl802154_post_doit, +}; + +/* multicast groups */ +enum nl802154_multicast_groups { + NL802154_MCGRP_CONFIG, +}; + +static const struct genl_multicast_group nl802154_mcgrps[] = { + [NL802154_MCGRP_CONFIG] = { .name = "config", }, +}; + +/* returns ERR_PTR values */ +static struct wpan_dev * +__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs) +{ + struct cfg802154_registered_device *rdev; + struct wpan_dev *result = NULL; + bool have_ifidx = attrs[NL802154_ATTR_IFINDEX]; + bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV]; + u64 wpan_dev_id; + int wpan_phy_idx = -1; + int ifidx = -1; + + ASSERT_RTNL(); + + if (!have_ifidx && !have_wpan_dev_id) + return ERR_PTR(-EINVAL); + + if (have_ifidx) + ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); + if (have_wpan_dev_id) { + wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); + wpan_phy_idx = wpan_dev_id >> 32; + } + + list_for_each_entry(rdev, &cfg802154_rdev_list, list) { + struct wpan_dev *wpan_dev; + + /* TODO netns compare */ + + if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx) + continue; + + list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { + if (have_ifidx && wpan_dev->netdev && + wpan_dev->netdev->ifindex == ifidx) { + result = wpan_dev; + break; + } + if (have_wpan_dev_id && + wpan_dev->identifier == (u32)wpan_dev_id) { + result = wpan_dev; + break; + } + } + + if (result) + break; + } + + if (result) + return result; + + return ERR_PTR(-ENODEV); +} + +static struct cfg802154_registered_device * +__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs) +{ + struct cfg802154_registered_device *rdev = NULL, *tmp; + struct net_device *netdev; + + ASSERT_RTNL(); + + if (!attrs[NL802154_ATTR_WPAN_PHY] && + !attrs[NL802154_ATTR_IFINDEX] && + !attrs[NL802154_ATTR_WPAN_DEV]) + return ERR_PTR(-EINVAL); + + if (attrs[NL802154_ATTR_WPAN_PHY]) + rdev = cfg802154_rdev_by_wpan_phy_idx( + nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY])); + + if (attrs[NL802154_ATTR_WPAN_DEV]) { + u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); + struct wpan_dev *wpan_dev; + bool found = false; + + tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32); + if (tmp) { + /* make sure wpan_dev exists */ + list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) { + if (wpan_dev->identifier != (u32)wpan_dev_id) + continue; + found = true; + break; + } + + if (!found) + tmp = NULL; + + if (rdev && tmp != rdev) + return ERR_PTR(-EINVAL); + rdev = tmp; + } + } + + if (attrs[NL802154_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); + + netdev = __dev_get_by_index(netns, ifindex); + if (netdev) { + if (netdev->ieee802154_ptr) + tmp = wpan_phy_to_rdev( + netdev->ieee802154_ptr->wpan_phy); + else + tmp = NULL; + + /* not wireless device -- return error */ + if (!tmp) + return ERR_PTR(-EINVAL); + + /* mismatch -- return error */ + if (rdev && tmp != rdev) + return ERR_PTR(-EINVAL); + + rdev = tmp; + } + } + + if (!rdev) + return ERR_PTR(-ENODEV); + + /* TODO netns compare */ + + return rdev; +} + +/* This function returns a pointer to the driver + * that the genl_info item that is passed refers to. + * + * The result of this can be a PTR_ERR and hence must + * be checked with IS_ERR() for errors. + */ +static struct cfg802154_registered_device * +cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info) +{ + return __cfg802154_rdev_from_attrs(netns, info->attrs); +} + +/* policy for the attributes */ +static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { + [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 }, + [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING, + .len = 20-1 }, + + [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 }, + [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 }, + [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, + + [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 }, + + [NL802154_ATTR_PAGE] = { .type = NLA_U8, }, + [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, }, + + [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, }, + + [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, }, + + [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, }, + + [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, }, + [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 }, + [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, }, + + [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, }, + [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, }, + [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, }, + + [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, }, + + [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, }, +}; + +/* message building helper */ +static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq, + int flags, u8 cmd) +{ + /* since there is no private header just add the generic one */ + return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd); +} + +static int +nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev, + struct sk_buff *msg) +{ + struct nlattr *nl_page; + unsigned long page; + + nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED); + if (!nl_page) + return -ENOBUFS; + + for (page = 0; page <= IEEE802154_MAX_PAGE; page++) { + if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL, + rdev->wpan_phy.channels_supported[page])) + return -ENOBUFS; + } + nla_nest_end(msg, nl_page); + + return 0; +} + +static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev, + enum nl802154_commands cmd, + struct sk_buff *msg, u32 portid, u32 seq, + int flags) +{ + void *hdr; + + hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); + if (!hdr) + return -ENOBUFS; + + if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || + nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME, + wpan_phy_name(&rdev->wpan_phy)) || + nla_put_u32(msg, NL802154_ATTR_GENERATION, + cfg802154_rdev_list_generation)) + goto nla_put_failure; + + if (cmd != NL802154_CMD_NEW_WPAN_PHY) + goto finish; + + /* DUMP PHY PIB */ + + /* current channel settings */ + if (nla_put_u8(msg, NL802154_ATTR_PAGE, + rdev->wpan_phy.current_page) || + nla_put_u8(msg, NL802154_ATTR_CHANNEL, + rdev->wpan_phy.current_channel)) + goto nla_put_failure; + + /* supported channels array */ + if (nl802154_send_wpan_phy_channels(rdev, msg)) + goto nla_put_failure; + + /* cca mode */ + if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE, + rdev->wpan_phy.cca_mode)) + goto nla_put_failure; + + if (nla_put_s8(msg, NL802154_ATTR_TX_POWER, + rdev->wpan_phy.transmit_power)) + goto nla_put_failure; + +finish: + return genlmsg_end(msg, hdr); + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +struct nl802154_dump_wpan_phy_state { + s64 filter_wpan_phy; + long start; + +}; + +static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb, + struct netlink_callback *cb, + struct nl802154_dump_wpan_phy_state *state) +{ + struct nlattr **tb = nl802154_fam.attrbuf; + int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize, + tb, nl802154_fam.maxattr, nl802154_policy); + + /* TODO check if we can handle error here, + * we have no backward compatibility + */ + if (ret) + return 0; + + if (tb[NL802154_ATTR_WPAN_PHY]) + state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]); + if (tb[NL802154_ATTR_WPAN_DEV]) + state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32; + if (tb[NL802154_ATTR_IFINDEX]) { + struct net_device *netdev; + struct cfg802154_registered_device *rdev; + int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]); + + /* TODO netns */ + netdev = __dev_get_by_index(&init_net, ifidx); + if (!netdev) + return -ENODEV; + if (netdev->ieee802154_ptr) { + rdev = wpan_phy_to_rdev( + netdev->ieee802154_ptr->wpan_phy); + state->filter_wpan_phy = rdev->wpan_phy_idx; + } + } + + return 0; +} + +static int +nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb) +{ + int idx = 0, ret; + struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0]; + struct cfg802154_registered_device *rdev; + + rtnl_lock(); + if (!state) { + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) { + rtnl_unlock(); + return -ENOMEM; + } + state->filter_wpan_phy = -1; + ret = nl802154_dump_wpan_phy_parse(skb, cb, state); + if (ret) { + kfree(state); + rtnl_unlock(); + return ret; + } + cb->args[0] = (long)state; + } + + list_for_each_entry(rdev, &cfg802154_rdev_list, list) { + /* TODO net ns compare */ + if (++idx <= state->start) + continue; + if (state->filter_wpan_phy != -1 && + state->filter_wpan_phy != rdev->wpan_phy_idx) + continue; + /* attempt to fit multiple wpan_phy data chunks into the skb */ + ret = nl802154_send_wpan_phy(rdev, + NL802154_CMD_NEW_WPAN_PHY, + skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI); + if (ret < 0) { + if ((ret == -ENOBUFS || ret == -EMSGSIZE) && + !skb->len && cb->min_dump_alloc < 4096) { + cb->min_dump_alloc = 4096; + rtnl_unlock(); + return 1; + } + idx--; + break; + } + break; + } + rtnl_unlock(); + + state->start = idx; + + return skb->len; +} + +static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb) +{ + kfree((void *)cb->args[0]); + return 0; +} + +static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg, + info->snd_portid, info->snd_seq, 0) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev) +{ + return (u64)wpan_dev->identifier | + ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32); +} + +static int +nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, + struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + struct net_device *dev = wpan_dev->netdev; + void *hdr; + + hdr = nl802154hdr_put(msg, portid, seq, flags, + NL802154_CMD_NEW_INTERFACE); + if (!hdr) + return -1; + + if (dev && + (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) || + nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name))) + goto nla_put_failure; + + if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || + nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) || + nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) || + nla_put_u32(msg, NL802154_ATTR_GENERATION, + rdev->devlist_generation ^ + (cfg802154_rdev_list_generation << 2))) + goto nla_put_failure; + + /* address settings */ + if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR, + wpan_dev->extended_addr) || + nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR, + wpan_dev->short_addr) || + nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id)) + goto nla_put_failure; + + /* ARET handling */ + if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES, + wpan_dev->frame_retries) || + nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) || + nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS, + wpan_dev->csma_retries) || + nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be)) + goto nla_put_failure; + + /* listen before transmit */ + if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt)) + goto nla_put_failure; + + return genlmsg_end(msg, hdr); + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return -EMSGSIZE; +} + +static int +nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) +{ + int wp_idx = 0; + int if_idx = 0; + int wp_start = cb->args[0]; + int if_start = cb->args[1]; + struct cfg802154_registered_device *rdev; + struct wpan_dev *wpan_dev; + + rtnl_lock(); + list_for_each_entry(rdev, &cfg802154_rdev_list, list) { + /* TODO netns compare */ + if (wp_idx < wp_start) { + wp_idx++; + continue; + } + if_idx = 0; + + list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { + if (if_idx < if_start) { + if_idx++; + continue; + } + if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + rdev, wpan_dev) < 0) { + goto out; + } + if_idx++; + } + + wp_idx++; + } +out: + rtnl_unlock(); + + cb->args[0] = wp_idx; + cb->args[1] = if_idx; + + return skb->len; +} + +static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *msg; + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct wpan_dev *wdev = info->user_ptr[1]; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0, + rdev, wdev) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC; + __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL); + + /* TODO avoid failing a new interface + * creation due to pending removal? + */ + + if (!info->attrs[NL802154_ATTR_IFNAME]) + return -EINVAL; + + if (info->attrs[NL802154_ATTR_IFTYPE]) { + type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]); + if (type > NL802154_IFTYPE_MAX) + return -EINVAL; + } + + /* TODO add nla_get_le64 to netlink */ + if (info->attrs[NL802154_ATTR_EXTENDED_ADDR]) + extended_addr = (__force __le64)nla_get_u64( + info->attrs[NL802154_ATTR_EXTENDED_ADDR]); + + if (!rdev->ops->add_virtual_intf) + return -EOPNOTSUPP; + + return rdev_add_virtual_intf(rdev, + nla_data(info->attrs[NL802154_ATTR_IFNAME]), + type, extended_addr); +} + +static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct wpan_dev *wpan_dev = info->user_ptr[1]; + + if (!rdev->ops->del_virtual_intf) + return -EOPNOTSUPP; + + /* If we remove a wpan device without a netdev then clear + * user_ptr[1] so that nl802154_post_doit won't dereference it + * to check if it needs to do dev_put(). Otherwise it crashes + * since the wpan_dev has been freed, unlike with a netdev where + * we need the dev_put() for the netdev to really be freed. + */ + if (!wpan_dev->netdev) + info->user_ptr[1] = NULL; + + return rdev_del_virtual_intf(rdev, wpan_dev); +} + +static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + u8 channel, page; + + if (!info->attrs[NL802154_ATTR_PAGE] || + !info->attrs[NL802154_ATTR_CHANNEL]) + return -EINVAL; + + page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]); + channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]); + + /* check 802.15.4 constraints */ + if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL) + return -EINVAL; + + return rdev_set_channel(rdev, page, channel); +} + +static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + __le16 pan_id; + + /* conflict here while tx/rx calls */ + if (netif_running(dev)) + return -EBUSY; + + /* don't change address fields on monitor */ + if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) + return -EINVAL; + + if (!info->attrs[NL802154_ATTR_PAN_ID]) + return -EINVAL; + + pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]); + + return rdev_set_pan_id(rdev, wpan_dev, pan_id); +} + +static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + __le16 short_addr; + + /* conflict here while tx/rx calls */ + if (netif_running(dev)) + return -EBUSY; + + /* don't change address fields on monitor */ + if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) + return -EINVAL; + + if (!info->attrs[NL802154_ATTR_SHORT_ADDR]) + return -EINVAL; + + short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]); + + return rdev_set_short_addr(rdev, wpan_dev, short_addr); +} + +static int +nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + u8 min_be, max_be; + + /* should be set on netif open inside phy settings */ + if (netif_running(dev)) + return -EBUSY; + + if (!info->attrs[NL802154_ATTR_MIN_BE] || + !info->attrs[NL802154_ATTR_MAX_BE]) + return -EINVAL; + + min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]); + max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]); + + /* check 802.15.4 constraints */ + if (max_be < 3 || max_be > 8 || min_be > max_be) + return -EINVAL; + + return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be); +} + +static int +nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + u8 max_csma_backoffs; + + /* conflict here while other running iface settings */ + if (netif_running(dev)) + return -EBUSY; + + if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]) + return -EINVAL; + + max_csma_backoffs = nla_get_u8( + info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]); + + /* check 802.15.4 constraints */ + if (max_csma_backoffs > 5) + return -EINVAL; + + return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs); +} + +static int +nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + s8 max_frame_retries; + + if (netif_running(dev)) + return -EBUSY; + + if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]) + return -EINVAL; + + max_frame_retries = nla_get_s8( + info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]); + + /* check 802.15.4 constraints */ + if (max_frame_retries < -1 || max_frame_retries > 7) + return -EINVAL; + + return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries); +} + +static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg802154_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wpan_dev *wpan_dev = dev->ieee802154_ptr; + bool mode; + + if (netif_running(dev)) + return -EBUSY; + + if (!info->attrs[NL802154_ATTR_LBT_MODE]) + return -EINVAL; + + mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]); + return rdev_set_lbt_mode(rdev, wpan_dev, mode); +} + +#define NL802154_FLAG_NEED_WPAN_PHY 0x01 +#define NL802154_FLAG_NEED_NETDEV 0x02 +#define NL802154_FLAG_NEED_RTNL 0x04 +#define NL802154_FLAG_CHECK_NETDEV_UP 0x08 +#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\ + NL802154_FLAG_CHECK_NETDEV_UP) +#define NL802154_FLAG_NEED_WPAN_DEV 0x10 +#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\ + NL802154_FLAG_CHECK_NETDEV_UP) + +static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg802154_registered_device *rdev; + struct wpan_dev *wpan_dev; + struct net_device *dev; + bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL; + + if (rtnl) + rtnl_lock(); + + if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) { + rdev = cfg802154_get_dev_from_info(genl_info_net(info), info); + if (IS_ERR(rdev)) { + if (rtnl) + rtnl_unlock(); + return PTR_ERR(rdev); + } + info->user_ptr[0] = rdev; + } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV || + ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { + ASSERT_RTNL(); + wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info), + info->attrs); + if (IS_ERR(wpan_dev)) { + if (rtnl) + rtnl_unlock(); + return PTR_ERR(wpan_dev); + } + + dev = wpan_dev->netdev; + rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); + + if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) { + if (!dev) { + if (rtnl) + rtnl_unlock(); + return -EINVAL; + } + + info->user_ptr[1] = dev; + } else { + info->user_ptr[1] = wpan_dev; + } + + if (dev) { + if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP && + !netif_running(dev)) { + if (rtnl) + rtnl_unlock(); + return -ENETDOWN; + } + + dev_hold(dev); + } + + info->user_ptr[0] = rdev; + } + + return 0; +} + +static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb, + struct genl_info *info) +{ + if (info->user_ptr[1]) { + if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { + struct wpan_dev *wpan_dev = info->user_ptr[1]; + + if (wpan_dev->netdev) + dev_put(wpan_dev->netdev); + } else { + dev_put(info->user_ptr[1]); + } + } + + if (ops->internal_flags & NL802154_FLAG_NEED_RTNL) + rtnl_unlock(); +} + +static const struct genl_ops nl802154_ops[] = { + { + .cmd = NL802154_CMD_GET_WPAN_PHY, + .doit = nl802154_get_wpan_phy, + .dumpit = nl802154_dump_wpan_phy, + .done = nl802154_dump_wpan_phy_done, + .policy = nl802154_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_GET_INTERFACE, + .doit = nl802154_get_interface, + .dumpit = nl802154_dump_interface, + .policy = nl802154_policy, + /* can be retrieved by unprivileged users */ + .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_NEW_INTERFACE, + .doit = nl802154_new_interface, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_DEL_INTERFACE, + .doit = nl802154_del_interface, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_CHANNEL, + .doit = nl802154_set_channel, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_PAN_ID, + .doit = nl802154_set_pan_id, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_SHORT_ADDR, + .doit = nl802154_set_short_addr, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT, + .doit = nl802154_set_backoff_exponent, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS, + .doit = nl802154_set_max_csma_backoffs, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES, + .doit = nl802154_set_max_frame_retries, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, + { + .cmd = NL802154_CMD_SET_LBT_MODE, + .doit = nl802154_set_lbt_mode, + .policy = nl802154_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL802154_FLAG_NEED_NETDEV | + NL802154_FLAG_NEED_RTNL, + }, +}; + +/* initialisation/exit functions */ +int nl802154_init(void) +{ + return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops, + nl802154_mcgrps); +} + +void nl802154_exit(void) +{ + genl_unregister_family(&nl802154_fam); +} diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h new file mode 100644 index 000000000000..3846a89d0958 --- /dev/null +++ b/net/ieee802154/nl802154.h @@ -0,0 +1,7 @@ +#ifndef __IEEE802154_NL802154_H +#define __IEEE802154_NL802154_H + +int nl802154_init(void); +void nl802154_exit(void); + +#endif /* __IEEE802154_NL802154_H */ diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c index 3a703ab88348..35c432668454 100644 --- a/net/ieee802154/nl_policy.c +++ b/net/ieee802154/nl_policy.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <linux/kernel.h> diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c index 9d1f64806f02..3ffcf4a9de01 100644 --- a/net/ieee802154/raw.c +++ b/net/ieee802154/raw.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h new file mode 100644 index 000000000000..aff54fbd9264 --- /dev/null +++ b/net/ieee802154/rdev-ops.h @@ -0,0 +1,89 @@ +#ifndef __CFG802154_RDEV_OPS +#define __CFG802154_RDEV_OPS + +#include <net/cfg802154.h> + +#include "core.h" + +static inline struct net_device * +rdev_add_virtual_intf_deprecated(struct cfg802154_registered_device *rdev, + const char *name, int type) +{ + return rdev->ops->add_virtual_intf_deprecated(&rdev->wpan_phy, name, + type); +} + +static inline void +rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev, + struct net_device *dev) +{ + rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev); +} + +static inline int +rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name, + enum nl802154_iftype type, __le64 extended_addr) +{ + return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type, + extended_addr); +} + +static inline int +rdev_del_virtual_intf(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev) +{ + return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev); +} + +static inline int +rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel) +{ + return rdev->ops->set_channel(&rdev->wpan_phy, page, channel); +} + +static inline int +rdev_set_pan_id(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, __le16 pan_id) +{ + return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id); +} + +static inline int +rdev_set_short_addr(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, __le16 short_addr) +{ + return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr); +} + +static inline int +rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, u8 min_be, u8 max_be) +{ + return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev, + min_be, max_be); +} + +static inline int +rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, u8 max_csma_backoffs) +{ + return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev, + max_csma_backoffs); +} + +static inline int +rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, s8 max_frame_retries) +{ + return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev, + max_frame_retries); +} + +static inline int +rdev_set_lbt_mode(struct cfg802154_registered_device *rdev, + struct wpan_dev *wpan_dev, bool mode) +{ + return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode); +} + +#endif /* __CFG802154_RDEV_OPS */ diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c index 7cfcd6885225..9d980ed3ffe2 100644 --- a/net/ieee802154/reassembly.c +++ b/net/ieee802154/reassembly.c @@ -33,7 +33,7 @@ static const char lowpan_frags_cache_name[] = "lowpan-frags"; struct lowpan_frag_info { - __be16 d_tag; + u16 d_tag; u16 d_size; u8 d_offset; }; @@ -48,7 +48,7 @@ static struct inet_frags lowpan_frags; static int lowpan_frag_reasm(struct lowpan_frag_queue *fq, struct sk_buff *prev, struct net_device *dev); -static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size, +static unsigned int lowpan_hash_frag(u16 tag, u16 d_size, const struct ieee802154_addr *saddr, const struct ieee802154_addr *daddr) { @@ -330,11 +330,13 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type, { bool fail; u8 pattern = 0, low = 0; + __be16 d_tag = 0; fail = lowpan_fetch_skb(skb, &pattern, 1); fail |= lowpan_fetch_skb(skb, &low, 1); frag_info->d_size = (pattern & 7) << 8 | low; - fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2); + fail |= lowpan_fetch_skb(skb, &d_tag, 2); + frag_info->d_tag = ntohs(d_tag); if (frag_type == LOWPAN_DISPATCH_FRAGN) { fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1); diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h index 74e4a7c98191..836b16fa001f 100644 --- a/net/ieee802154/reassembly.h +++ b/net/ieee802154/reassembly.h @@ -4,7 +4,7 @@ #include <net/inet_frag.h> struct lowpan_create_arg { - __be16 tag; + u16 tag; u16 d_size; const struct ieee802154_addr *src; const struct ieee802154_addr *dst; @@ -15,7 +15,7 @@ struct lowpan_create_arg { struct lowpan_frag_queue { struct inet_frag_queue q; - __be16 tag; + u16 tag; u16 d_size; struct ieee802154_addr saddr; struct ieee802154_addr daddr; diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c new file mode 100644 index 000000000000..1613b9c65dfa --- /dev/null +++ b/net/ieee802154/sysfs.c @@ -0,0 +1,128 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Authors: + * Alexander Aring <aar@pengutronix.de> + * + * Based on: net/wireless/sysfs.c + */ + +#include <linux/device.h> + +#include <net/cfg802154.h> + +#include "core.h" +#include "sysfs.h" + +static inline struct cfg802154_registered_device * +dev_to_rdev(struct device *dev) +{ + return container_of(dev, struct cfg802154_registered_device, + wpan_phy.dev); +} + +#define SHOW_FMT(name, fmt, member) \ +static ssize_t name ## _show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \ +} \ +static DEVICE_ATTR_RO(name) + +SHOW_FMT(index, "%d", wpan_phy_idx); + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy; + + return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev)); +} +static DEVICE_ATTR_RO(name); + +#define MASTER_SHOW_COMPLEX(name, format_string, args...) \ +static ssize_t name ## _show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \ + int ret; \ + \ + mutex_lock(&phy->pib_lock); \ + ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \ + mutex_unlock(&phy->pib_lock); \ + return ret; \ +} \ +static DEVICE_ATTR_RO(name) + +#define MASTER_SHOW(field, format_string) \ + MASTER_SHOW_COMPLEX(field, format_string, phy->field) + +MASTER_SHOW(current_channel, "%d"); +MASTER_SHOW(current_page, "%d"); +MASTER_SHOW(transmit_power, "%d +- 1 dB"); +MASTER_SHOW(cca_mode, "%d"); + +static ssize_t channels_supported_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); + int ret; + int i, len = 0; + + mutex_lock(&phy->pib_lock); + for (i = 0; i < 32; i++) { + ret = snprintf(buf + len, PAGE_SIZE - len, + "%#09x\n", phy->channels_supported[i]); + if (ret < 0) + break; + len += ret; + } + mutex_unlock(&phy->pib_lock); + return len; +} +static DEVICE_ATTR_RO(channels_supported); + +static void wpan_phy_release(struct device *dev) +{ + struct cfg802154_registered_device *rdev = dev_to_rdev(dev); + + cfg802154_dev_free(rdev); +} + +static struct attribute *pmib_attrs[] = { + &dev_attr_index.attr, + &dev_attr_name.attr, + /* below will be removed soon */ + &dev_attr_current_channel.attr, + &dev_attr_current_page.attr, + &dev_attr_channels_supported.attr, + &dev_attr_transmit_power.attr, + &dev_attr_cca_mode.attr, + NULL, +}; +ATTRIBUTE_GROUPS(pmib); + +struct class wpan_phy_class = { + .name = "ieee802154", + .dev_release = wpan_phy_release, + .dev_groups = pmib_groups, +}; + +int wpan_phy_sysfs_init(void) +{ + return class_register(&wpan_phy_class); +} + +void wpan_phy_sysfs_exit(void) +{ + class_unregister(&wpan_phy_class); +} diff --git a/net/ieee802154/sysfs.h b/net/ieee802154/sysfs.h new file mode 100644 index 000000000000..aa42e39ecbec --- /dev/null +++ b/net/ieee802154/sysfs.h @@ -0,0 +1,9 @@ +#ifndef __IEEE802154_SYSFS_H +#define __IEEE802154_SYSFS_H + +int wpan_phy_sysfs_init(void); +void wpan_phy_sysfs_exit(void); + +extern struct class wpan_phy_class; + +#endif /* __IEEE802154_SYSFS_H */ diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c deleted file mode 100644 index 4955e0fe5883..000000000000 --- a/net/ieee802154/wpan-class.c +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2007, 2008, 2009 Siemens AG - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/device.h> - -#include <net/wpan-phy.h> - -#include "ieee802154.h" - -#define MASTER_SHOW_COMPLEX(name, format_string, args...) \ -static ssize_t name ## _show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \ - int ret; \ - \ - mutex_lock(&phy->pib_lock); \ - ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \ - mutex_unlock(&phy->pib_lock); \ - return ret; \ -} \ -static DEVICE_ATTR_RO(name); - -#define MASTER_SHOW(field, format_string) \ - MASTER_SHOW_COMPLEX(field, format_string, phy->field) - -MASTER_SHOW(current_channel, "%d"); -MASTER_SHOW(current_page, "%d"); -MASTER_SHOW(transmit_power, "%d +- 1 dB"); -MASTER_SHOW(cca_mode, "%d"); - -static ssize_t channels_supported_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); - int ret; - int i, len = 0; - - mutex_lock(&phy->pib_lock); - for (i = 0; i < 32; i++) { - ret = snprintf(buf + len, PAGE_SIZE - len, - "%#09x\n", phy->channels_supported[i]); - if (ret < 0) - break; - len += ret; - } - mutex_unlock(&phy->pib_lock); - return len; -} -static DEVICE_ATTR_RO(channels_supported); - -static struct attribute *pmib_attrs[] = { - &dev_attr_current_channel.attr, - &dev_attr_current_page.attr, - &dev_attr_channels_supported.attr, - &dev_attr_transmit_power.attr, - &dev_attr_cca_mode.attr, - NULL, -}; -ATTRIBUTE_GROUPS(pmib); - -static void wpan_phy_release(struct device *d) -{ - struct wpan_phy *phy = container_of(d, struct wpan_phy, dev); - - kfree(phy); -} - -static struct class wpan_phy_class = { - .name = "ieee802154", - .dev_release = wpan_phy_release, - .dev_groups = pmib_groups, -}; - -static DEFINE_MUTEX(wpan_phy_mutex); -static int wpan_phy_idx; - -static int wpan_phy_match(struct device *dev, const void *data) -{ - return !strcmp(dev_name(dev), (const char *)data); -} - -struct wpan_phy *wpan_phy_find(const char *str) -{ - struct device *dev; - - if (WARN_ON(!str)) - return NULL; - - dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match); - if (!dev) - return NULL; - - return container_of(dev, struct wpan_phy, dev); -} -EXPORT_SYMBOL(wpan_phy_find); - -struct wpan_phy_iter_data { - int (*fn)(struct wpan_phy *phy, void *data); - void *data; -}; - -static int wpan_phy_iter(struct device *dev, void *_data) -{ - struct wpan_phy_iter_data *wpid = _data; - struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); - - return wpid->fn(phy, wpid->data); -} - -int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data), - void *data) -{ - struct wpan_phy_iter_data wpid = { - .fn = fn, - .data = data, - }; - - return class_for_each_device(&wpan_phy_class, NULL, - &wpid, wpan_phy_iter); -} -EXPORT_SYMBOL(wpan_phy_for_each); - -static int wpan_phy_idx_valid(int idx) -{ - return idx >= 0; -} - -struct wpan_phy *wpan_phy_alloc(size_t priv_size) -{ - struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size, - GFP_KERNEL); - - if (!phy) - goto out; - mutex_lock(&wpan_phy_mutex); - phy->idx = wpan_phy_idx++; - if (unlikely(!wpan_phy_idx_valid(phy->idx))) { - wpan_phy_idx--; - mutex_unlock(&wpan_phy_mutex); - kfree(phy); - goto out; - } - mutex_unlock(&wpan_phy_mutex); - - mutex_init(&phy->pib_lock); - - device_initialize(&phy->dev); - dev_set_name(&phy->dev, "wpan-phy%d", phy->idx); - - phy->dev.class = &wpan_phy_class; - - phy->current_channel = -1; /* not initialised */ - phy->current_page = 0; /* for compatibility */ - - return phy; - -out: - return NULL; -} -EXPORT_SYMBOL(wpan_phy_alloc); - -int wpan_phy_register(struct wpan_phy *phy) -{ - return device_add(&phy->dev); -} -EXPORT_SYMBOL(wpan_phy_register); - -void wpan_phy_unregister(struct wpan_phy *phy) -{ - device_del(&phy->dev); -} -EXPORT_SYMBOL(wpan_phy_unregister); - -void wpan_phy_free(struct wpan_phy *phy) -{ - put_device(&phy->dev); -} -EXPORT_SYMBOL(wpan_phy_free); - -static int __init wpan_phy_class_init(void) -{ - int rc; - - rc = class_register(&wpan_phy_class); - if (rc) - goto err; - - rc = ieee802154_nl_init(); - if (rc) - goto err_nl; - - return 0; -err_nl: - class_unregister(&wpan_phy_class); -err: - return rc; -} -subsys_initcall(wpan_phy_class_init); - -static void __exit wpan_phy_class_exit(void) -{ - ieee802154_nl_exit(); - class_unregister(&wpan_phy_class); -} -module_exit(wpan_phy_class_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface"); -MODULE_AUTHOR("Dmitry Eremin-Solenikov"); - diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index aeb6a483b3bc..75cc6801a431 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -33,6 +33,13 @@ config MAC80211_RC_MINSTREL_HT ---help--- This option enables the 'minstrel_ht' TX rate control algorithm +config MAC80211_RC_MINSTREL_VHT + bool "Minstrel 802.11ac support" if EXPERT + depends on MAC80211_RC_MINSTREL_HT + default n + ---help--- + This option enables VHT in the 'minstrel_ht' TX rate control algorithm + choice prompt "Default rate control algorithm" depends on MAC80211_HAS_RC @@ -169,6 +176,17 @@ config MAC80211_HT_DEBUG Do not select this option. +config MAC80211_OCB_DEBUG + bool "Verbose OCB debugging" + depends on MAC80211_DEBUG_MENU + ---help--- + Selecting this option causes mac80211 to print out + very verbose OCB debugging messages. It should not + be selected on production systems as those messages + are remotely triggerable. + + Do not select this option. + config MAC80211_IBSS_DEBUG bool "Verbose IBSS debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 7273d2796dd1..e53671b1105e 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile @@ -27,7 +27,8 @@ mac80211-y := \ event.o \ chan.o \ trace.o mlme.o \ - tdls.o + tdls.o \ + ocb.o mac80211-$(CONFIG_MAC80211_LEDS) += led.o mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index d6986f3aa5c4..9242c60048cf 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -149,11 +149,6 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx); } -static inline int ieee80211_ac_from_tid(int tid) -{ - return ieee802_1d_to_ac[tid & 7]; -} - /* * When multiple aggregation sessions on multiple stations * are being created/destroyed simultaneously, we need to diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 343da1e35025..06185940cbb6 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -20,6 +20,7 @@ #include "cfg.h" #include "rate.h" #include "mesh.h" +#include "wme.h" static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, const char *name, @@ -190,7 +191,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, * receive the key. When wpa_supplicant has roamed * using FT, it attempts to set the key before * association has completed, this rejects that attempt - * so it will set the key again after assocation. + * so it will set the key again after association. * * TODO: accept the key if we have a station entry and * add it to the device after the station. @@ -229,6 +230,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_OCB: /* shouldn't happen */ WARN_ON_ONCE(1); break; @@ -1225,14 +1227,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac) + struct station_del_parameters *params) { struct ieee80211_sub_if_data *sdata; sdata = IEEE80211_DEV_TO_SUB_IF(dev); - if (mac) - return sta_info_destroy_addr_bss(sdata, mac); + if (params->mac) + return sta_info_destroy_addr_bss(sdata, params->mac); sta_info_flush(sdata); return 0; @@ -1516,6 +1518,57 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, return 0; } +static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp, + struct mpath_info *pinfo) +{ + memset(pinfo, 0, sizeof(*pinfo)); + memcpy(mpp, mpath->mpp, ETH_ALEN); + + pinfo->generation = mpp_paths_generation; +} + +static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev, + u8 *dst, u8 *mpp, struct mpath_info *pinfo) + +{ + struct ieee80211_sub_if_data *sdata; + struct mesh_path *mpath; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + rcu_read_lock(); + mpath = mpp_path_lookup(sdata, dst); + if (!mpath) { + rcu_read_unlock(); + return -ENOENT; + } + memcpy(dst, mpath->dst, ETH_ALEN); + mpp_set_pinfo(mpath, mpp, pinfo); + rcu_read_unlock(); + return 0; +} + +static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *dst, u8 *mpp, + struct mpath_info *pinfo) +{ + struct ieee80211_sub_if_data *sdata; + struct mesh_path *mpath; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + rcu_read_lock(); + mpath = mpp_path_lookup_by_idx(sdata, idx); + if (!mpath) { + rcu_read_unlock(); + return -ENOENT; + } + memcpy(dst, mpath->dst, ETH_ALEN); + mpp_set_pinfo(mpath, mpp, pinfo); + rcu_read_unlock(); + return 0; +} + static int ieee80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev, struct mesh_config *conf) @@ -1966,6 +2019,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev)); } +static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev, + struct ocb_setup *setup) +{ + return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup); +} + +static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev) +{ + return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev)); +} + static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev, int rate[IEEE80211_NUM_BANDS]) { @@ -2081,6 +2145,9 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + if (local->ops->get_txpower) + return drv_get_txpower(local, sdata, dbm); + if (!local->use_chanctx) *dbm = local->hw.conf.power_level; else @@ -2850,11 +2917,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) if (sdata->reserved_ready) return 0; - err = ieee80211_vif_use_reserved_context(sdata); - if (err) - return err; - - return 0; + return ieee80211_vif_use_reserved_context(sdata); } if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef, @@ -2868,7 +2931,6 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) return err; ieee80211_bss_info_change_notify(sdata, changed); - cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, @@ -2876,6 +2938,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata) sdata->csa_block_tx = false; } + err = drv_post_channel_switch(sdata); + if (err) + return err; + + cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef); + return 0; } @@ -3053,9 +3121,11 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + struct ieee80211_channel_switch ch_switch; struct ieee80211_chanctx_conf *conf; struct ieee80211_chanctx *chanctx; - int err, changed = 0; + u32 changed = 0; + int err; sdata_assert_lock(sdata); lockdep_assert_held(&local->mtx); @@ -3088,6 +3158,10 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } + err = drv_pre_channel_switch(sdata, &ch_switch); + if (err) + goto out; + err = ieee80211_vif_reserve_chanctx(sdata, ¶ms->chandef, chanctx->mode, params->radar_required); @@ -3101,6 +3175,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, goto out; } + ch_switch.timestamp = 0; + ch_switch.device_timestamp = 0; + ch_switch.block_tx = params->block_tx; + ch_switch.chandef = params->chandef; + ch_switch.count = params->count; + err = ieee80211_set_csa_beacon(sdata, params, &changed); if (err) { ieee80211_vif_unreserve_chanctx(sdata); @@ -3521,6 +3601,76 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy, return ret; } +static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev, + u8 tsid, const u8 *peer, u8 up, + u16 admitted_time) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + int ac = ieee802_1d_to_ac[up]; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + if (!(sdata->wmm_acm & BIT(up))) + return -EINVAL; + + if (ifmgd->tx_tspec[ac].admitted_time) + return -EBUSY; + + if (admitted_time) { + ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time; + ifmgd->tx_tspec[ac].tsid = tsid; + ifmgd->tx_tspec[ac].up = up; + } + + return 0; +} + +static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev, + u8 tsid, const u8 *peer) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = wiphy_priv(wiphy); + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; + + /* skip unused entries */ + if (!tx_tspec->admitted_time) + continue; + + if (tx_tspec->tsid != tsid) + continue; + + /* due to this new packets will be reassigned to non-ACM ACs */ + tx_tspec->up = -1; + + /* Make sure that all packets have been sent to avoid to + * restore the QoS params on packets that are still on the + * queues. + */ + synchronize_net(); + ieee80211_flush_queues(local, sdata); + + /* restore the normal QoS parameters + * (unconditionally to avoid races) + */ + tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; + tx_tspec->downgraded = false; + ieee80211_sta_handle_tspec_ac_params(sdata); + + /* finally clear all the data */ + memset(tx_tspec, 0, sizeof(*tx_tspec)); + + return 0; + } + + return -ENOENT; +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -3547,11 +3697,15 @@ const struct cfg80211_ops mac80211_config_ops = { .change_mpath = ieee80211_change_mpath, .get_mpath = ieee80211_get_mpath, .dump_mpath = ieee80211_dump_mpath, + .get_mpp = ieee80211_get_mpp, + .dump_mpp = ieee80211_dump_mpp, .update_mesh_config = ieee80211_update_mesh_config, .get_mesh_config = ieee80211_get_mesh_config, .join_mesh = ieee80211_join_mesh, .leave_mesh = ieee80211_leave_mesh, #endif + .join_ocb = ieee80211_join_ocb, + .leave_ocb = ieee80211_leave_ocb, .change_bss = ieee80211_change_bss, .set_txq_params = ieee80211_set_txq_params, .set_monitor_channel = ieee80211_set_monitor_channel, @@ -3597,4 +3751,6 @@ const struct cfg80211_ops mac80211_config_ops = { .channel_switch = ieee80211_channel_switch, .set_qos_map = ieee80211_set_qos_map, .set_ap_chanwidth = ieee80211_set_ap_chanwidth, + .add_tx_ts = ieee80211_add_tx_ts, + .del_tx_ts = ieee80211_del_tx_ts, }; diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 4c74e8da64b9..c7c514220298 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -270,6 +270,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: width = vif->bss_conf.chandef.width; break; case NL80211_IFTYPE_UNSPECIFIED: @@ -674,6 +675,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: break; default: WARN_ON_ONCE(1); @@ -909,6 +911,7 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: ieee80211_queue_work(&sdata->local->hw, &sdata->csa_finalize_work); break; @@ -1634,7 +1637,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, } break; case IEEE80211_CHANCTX_WILL_BE_REPLACED: - /* TODO: Perhaps the bandwith change could be treated as a + /* TODO: Perhaps the bandwidth change could be treated as a * reservation itself? */ ret = -EBUSY; goto out; diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 493d68061f0c..1956b3115dd5 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -2,6 +2,12 @@ #define __MAC80211_DEBUG_H #include <net/cfg80211.h> +#ifdef CONFIG_MAC80211_OCB_DEBUG +#define MAC80211_OCB_DEBUG 1 +#else +#define MAC80211_OCB_DEBUG 0 +#endif + #ifdef CONFIG_MAC80211_IBSS_DEBUG #define MAC80211_IBSS_DEBUG 1 #else @@ -131,6 +137,10 @@ do { \ _sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \ sdata, fmt, ##__VA_ARGS__) +#define ocb_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_OCB_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + #define ibss_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_IBSS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 1521cabad3d6..5523b94c7c90 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -300,10 +300,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) lockdep_assert_held(&sdata->local->key_mtx); - if (sdata->debugfs.default_unicast_key) { - debugfs_remove(sdata->debugfs.default_unicast_key); - sdata->debugfs.default_unicast_key = NULL; - } + debugfs_remove(sdata->debugfs.default_unicast_key); + sdata->debugfs.default_unicast_key = NULL; if (sdata->default_unicast_key) { key = key_mtx_dereference(sdata->local, @@ -314,10 +312,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata) sdata->vif.debugfs_dir, buf); } - if (sdata->debugfs.default_multicast_key) { - debugfs_remove(sdata->debugfs.default_multicast_key); - sdata->debugfs.default_multicast_key = NULL; - } + debugfs_remove(sdata->debugfs.default_multicast_key); + sdata->debugfs.default_multicast_key = NULL; if (sdata->default_multicast_key) { key = key_mtx_dereference(sdata->local, diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 196d48c68134..9759dd1f0734 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, BSS_CHANGED_BEACON_ENABLED) && sdata->vif.type != NL80211_IFTYPE_AP && sdata->vif.type != NL80211_IFTYPE_ADHOC && - sdata->vif.type != NL80211_IFTYPE_MESH_POINT)) + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && + sdata->vif.type != NL80211_IFTYPE_OCB)) return; if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE || @@ -631,6 +632,12 @@ static inline int drv_conf_tx(struct ieee80211_local *local, if (!check_sdata_in_driver(sdata)) return -EIO; + if (WARN_ONCE(params->cw_min == 0 || + params->cw_min > params->cw_max, + "%s: invalid CW_min/CW_max: %d/%d\n", + sdata->name, params->cw_min, params->cw_max)) + return -EINVAL; + trace_drv_conf_tx(local, sdata, ac, params); if (local->ops->conf_tx) ret = local->ops->conf_tx(&local->hw, &sdata->vif, @@ -764,12 +771,13 @@ static inline void drv_flush(struct ieee80211_local *local, } static inline void drv_channel_switch(struct ieee80211_local *local, - struct ieee80211_channel_switch *ch_switch) + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch) { might_sleep(); - trace_drv_channel_switch(local, ch_switch); - local->ops->channel_switch(&local->hw, ch_switch); + trace_drv_channel_switch(local, sdata, ch_switch); + local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch); trace_drv_return_void(local); } @@ -1144,13 +1152,15 @@ static inline void drv_stop_ap(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline void drv_restart_complete(struct ieee80211_local *local) +static inline void +drv_reconfig_complete(struct ieee80211_local *local, + enum ieee80211_reconfig_type reconfig_type) { might_sleep(); - trace_drv_restart_complete(local); - if (local->ops->restart_complete) - local->ops->restart_complete(&local->hw); + trace_drv_reconfig_complete(local, reconfig_type); + if (local->ops->reconfig_complete) + local->ops->reconfig_complete(&local->hw, reconfig_type); trace_drv_return_void(local); } @@ -1196,6 +1206,40 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata, } } +static inline int +drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch) +{ + struct ieee80211_local *local = sdata->local; + int ret = 0; + + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_pre_channel_switch(local, sdata, ch_switch); + if (local->ops->pre_channel_switch) + ret = local->ops->pre_channel_switch(&local->hw, &sdata->vif, + ch_switch); + trace_drv_return_int(local, ret); + return ret; +} + +static inline int +drv_post_channel_switch(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + int ret = 0; + + if (!check_sdata_in_driver(sdata)) + return -EIO; + + trace_drv_post_channel_switch(local, sdata); + if (local->ops->post_channel_switch) + ret = local->ops->post_channel_switch(&local->hw, &sdata->vif); + trace_drv_return_int(local, ret); + return ret; +} + static inline int drv_join_ibss(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { @@ -1238,4 +1282,18 @@ static inline u32 drv_get_expected_throughput(struct ieee80211_local *local, return ret; } +static inline int drv_get_txpower(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, int *dbm) +{ + int ret; + + if (!local->ops->get_txpower) + return -EOPNOTSUPP; + + ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm); + trace_drv_get_txpower(local, sdata, *dbm, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8c68da30595d..842e0661fb57 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -131,7 +131,7 @@ enum ieee80211_bss_corrupt_data_flags { * * These are bss flags that are attached to a bss in the * @valid_data field of &struct ieee80211_bss. They show which parts - * of the data structure were recieved as a result of an un-corrupted + * of the data structure were received as a result of an un-corrupted * beacon/probe response. */ enum ieee80211_bss_valid_data_flags { @@ -399,6 +399,24 @@ struct ieee80211_mgd_assoc_data { u8 ie[]; }; +struct ieee80211_sta_tx_tspec { + /* timestamp of the first packet in the time slice */ + unsigned long time_slice_start; + + u32 admitted_time; /* in usecs, unlike over the air */ + u8 tsid; + s8 up; /* signed to be able to invalidate with -1 during teardown */ + + /* consumed TX time in microseconds in the time slice */ + u32 consumed_tx_time; + enum { + TX_TSPEC_ACTION_NONE = 0, + TX_TSPEC_ACTION_DOWNGRADE, + TX_TSPEC_ACTION_STOP_DOWNGRADE, + } action; + bool downgraded; +}; + struct ieee80211_if_managed { struct timer_list timer; struct timer_list conn_mon_timer; @@ -434,6 +452,8 @@ struct ieee80211_if_managed { unsigned int flags; + bool csa_waiting_bcn; + bool beacon_crc_valid; u32 beacon_crc; @@ -507,6 +527,16 @@ struct ieee80211_if_managed { u8 tdls_peer[ETH_ALEN] __aligned(2); struct delayed_work tdls_peer_del_work; + + /* WMM-AC TSPEC support */ + struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS]; + /* Use a separate work struct so that we can do something here + * while the sdata->work is flushing the queues, for example. + * otherwise, in scenarios where we hardly get any traffic out + * on the BE queue, but there's a lot of VO traffic, we might + * get stuck in a downgraded situation and flush takes forever. + */ + struct delayed_work tx_tspec_wk; }; struct ieee80211_if_ibss { @@ -547,6 +577,25 @@ struct ieee80211_if_ibss { }; /** + * struct ieee80211_if_ocb - OCB mode state + * + * @housekeeping_timer: timer for periodic invocation of a housekeeping task + * @wrkq_flags: OCB deferred task action + * @incomplete_lock: delayed STA insertion lock + * @incomplete_stations: list of STAs waiting for delayed insertion + * @joined: indication if the interface is connected to an OCB network + */ +struct ieee80211_if_ocb { + struct timer_list housekeeping_timer; + unsigned long wrkq_flags; + + spinlock_t incomplete_lock; + struct list_head incomplete_stations; + + bool joined; +}; + +/** * struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface * * these declarations define the interface, which enables @@ -839,6 +888,7 @@ struct ieee80211_sub_if_data { struct ieee80211_if_managed mgd; struct ieee80211_if_ibss ibss; struct ieee80211_if_mesh mesh; + struct ieee80211_if_ocb ocb; u32 mntr_flags; } u; @@ -1307,6 +1357,9 @@ struct ieee80211_local { /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; struct cfg80211_chan_def monitor_chandef; + + /* extended capabilities provided by mac80211 */ + u8 ext_capa[8]; }; static inline struct ieee80211_sub_if_data * @@ -1454,6 +1507,7 @@ void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata, __le16 fc, bool acked); void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata); void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata); +void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); @@ -1471,6 +1525,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata); +/* OCB code */ +void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata); +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, u32 supp_rates); +void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata); +int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, + struct ocb_setup *setup); +int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata); + /* mesh code */ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, @@ -1757,6 +1820,13 @@ static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames) return true; } +extern const int ieee802_1d_to_ac[8]; + +static inline int ieee80211_ac_from_tid(int tid) +{ + return ieee802_1d_to_ac[tid & 7]; +} + void ieee80211_dynamic_ps_enable_work(struct work_struct *work); void ieee80211_dynamic_ps_disable_work(struct work_struct *work); void ieee80211_dynamic_ps_timer(unsigned long data); @@ -1766,7 +1836,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, - struct ieee80211_hdr *hdr, bool ack); + struct ieee80211_hdr *hdr, bool ack, u16 tx_time); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, unsigned long queues, @@ -1832,8 +1902,10 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset); +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 653f5eb07a27..9df26adb864a 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -259,6 +259,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata, list_for_each_entry(nsdata, &local->interfaces, list) { if (nsdata != sdata && ieee80211_sdata_running(nsdata)) { /* + * Only OCB and monitor mode may coexist + */ + if ((sdata->vif.type == NL80211_IFTYPE_OCB && + nsdata->vif.type != NL80211_IFTYPE_MONITOR) || + (sdata->vif.type != NL80211_IFTYPE_MONITOR && + nsdata->vif.type == NL80211_IFTYPE_OCB)) + return -EBUSY; + + /* * Allow only a single IBSS interface to be up at any * time. This is restricted because beacon distribution * cannot work properly if both are in the same IBSS. @@ -521,6 +530,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_OCB: /* no special treatment */ break; case NL80211_IFTYPE_UNSPECIFIED: @@ -631,6 +641,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_OCB: netif_carrier_off(dev); break; case NL80211_IFTYPE_WDS: @@ -844,6 +855,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, sdata_lock(sdata); mutex_lock(&local->mtx); sdata->vif.csa_active = false; + if (sdata->vif.type == NL80211_IFTYPE_STATION) + sdata->u.mgd.csa_waiting_bcn = false; if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -1285,6 +1298,9 @@ static void ieee80211_iface_work(struct work_struct *work) break; ieee80211_mesh_work(sdata); break; + case NL80211_IFTYPE_OCB: + ieee80211_ocb_work(sdata); + break; default: break; } @@ -1304,6 +1320,9 @@ static void ieee80211_recalc_smps_work(struct work_struct *work) static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, enum nl80211_iftype type) { + static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff, + 0xff, 0xff, 0xff}; + /* clear type-dependent union */ memset(&sdata->u, 0, sizeof(sdata->u)); @@ -1355,6 +1374,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid; ieee80211_sta_setup_sdata(sdata); break; + case NL80211_IFTYPE_OCB: + sdata->vif.bss_conf.bssid = bssid_wildcard; + ieee80211_ocb_setup_sdata(sdata); + break; case NL80211_IFTYPE_ADHOC: sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid; ieee80211_ibss_setup_sdata(sdata); @@ -1402,6 +1425,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_OCB: /* * Could maybe also all others here? * Just not sure how that interacts @@ -1417,6 +1441,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_OCB: /* * Could probably support everything * but WDS here (WDS do_open can fail @@ -1675,7 +1700,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, } ieee80211_assign_perm_addr(local, ndev->perm_addr, type); - memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); + if (params && is_valid_ether_addr(params->macaddr)) + memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN); + else + memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN); SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy)); /* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */ diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 4712150dc210..434a91ad12c8 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -94,8 +94,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) might_sleep(); - if (key->flags & KEY_FLAG_TAINTED) + if (key->flags & KEY_FLAG_TAINTED) { + /* If we get here, it's during resume and the key is + * tainted so shouldn't be used/programmed any more. + * However, its flags may still indicate that it was + * programmed into the device (since we're in resume) + * so clear that flag now to avoid trying to remove + * it again later. + */ + key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; return -EINVAL; + } if (!key->local->ops->set_key) goto out_unsupported; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 0de7c93bf62b..282a4f36eb92 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -478,13 +478,9 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = { }, }; -static const u8 extended_capabilities[] = { - 0, 0, 0, 0, 0, 0, 0, - WLAN_EXT_CAPA8_OPMODE_NOTIF, -}; - -struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, - const struct ieee80211_ops *ops) +struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, + const struct ieee80211_ops *ops, + const char *requested_name) { struct ieee80211_local *local; int priv_size, i; @@ -524,7 +520,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, */ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; - wiphy = wiphy_new(&mac80211_config_ops, priv_size); + wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name); if (!wiphy) return NULL; @@ -539,10 +535,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, WIPHY_FLAG_REPORTS_OBSS | WIPHY_FLAG_OFFCHAN_TX; - wiphy->extended_capabilities = extended_capabilities; - wiphy->extended_capabilities_mask = extended_capabilities; - wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities); - if (ops->remain_on_channel) wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; @@ -550,6 +542,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, NL80211_FEATURE_SAE | NL80211_FEATURE_HT_IBSS | NL80211_FEATURE_VIF_TXPOWER | + NL80211_FEATURE_MAC_ON_CREATE | NL80211_FEATURE_USERSPACE_MPM; if (!ops->hw_scan) @@ -591,6 +584,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask; + local->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF; + + wiphy->extended_capabilities = local->ext_capa; + wiphy->extended_capabilities_mask = local->ext_capa; + wiphy->extended_capabilities_len = + ARRAY_SIZE(local->ext_capa); + INIT_LIST_HEAD(&local->interfaces); __hw_addr_init(&local->mc_list); @@ -651,7 +651,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, return &local->hw; } -EXPORT_SYMBOL(ieee80211_alloc_hw); +EXPORT_SYMBOL(ieee80211_alloc_hw_nm); static int ieee80211_init_cipher_suites(struct ieee80211_local *local) { @@ -787,13 +787,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) return -EINVAL; - /* DFS currently not supported with channel context drivers */ + /* DFS is not supported with multi-channel combinations yet */ for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { const struct ieee80211_iface_combination *comb; comb = &local->hw.wiphy->iface_combinations[i]; - if (comb->radar_detect_widths) + if (comb->radar_detect_widths && + comb->num_different_channels > 1) return -EINVAL; } } @@ -958,6 +959,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + /* mac80211 supports eCSA, if the driver supports STA CSA at all */ + if (local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA) + local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING; + local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM; result = wiphy_register(local->hw.wiphy); @@ -1019,7 +1024,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) } /* add one default STA interface if supported */ - if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) { + if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) && + !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) { result = ieee80211_if_add(local, "wlan%d", NULL, NL80211_IFTYPE_STATION, NULL); if (result) diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index f39a19f9090f..50c8473cf9dc 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -270,6 +270,8 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, const u8 *dst, const u8 *mpp); struct mesh_path * mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx); +struct mesh_path * +mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx); void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop); void mesh_path_expire(struct ieee80211_sub_if_data *sdata); void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata, @@ -317,6 +319,7 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata); bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt); extern int mesh_paths_generation; +extern int mpp_paths_generation; #ifdef CONFIG_MAC80211_MESH static inline diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index a6699dceae7c..b890e225a8f1 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -44,6 +44,7 @@ static struct mesh_table __rcu *mesh_paths; static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */ int mesh_paths_generation; +int mpp_paths_generation; /* This lock will have the grow table function as writer and add / delete nodes * as readers. RCU provides sufficient protection only when reading the table @@ -410,6 +411,33 @@ mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) } /** + * mpp_path_lookup_by_idx - look up a path in the proxy path table by its index + * @idx: index + * @sdata: local subif, or NULL for all entries + * + * Returns: pointer to the proxy path structure, or NULL if not found. + * + * Locking: must be called within a read rcu section. + */ +struct mesh_path * +mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx) +{ + struct mesh_table *tbl = rcu_dereference(mpp_paths); + struct mpath_node *node; + int i; + int j = 0; + + for_each_mesh_entry(tbl, node, i) { + if (sdata && node->mpath->sdata != sdata) + continue; + if (j++ == idx) + return node->mpath; + } + + return NULL; +} + +/** * mesh_path_add_gate - add the given mpath to a mesh gate to our path table * @mpath: gate path to add to table */ @@ -691,6 +719,9 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata, spin_unlock(&tbl->hashwlock[hash_idx]); read_unlock_bh(&pathtbl_resize_lock); + + mpp_paths_generation++; + if (grow) { set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags); ieee80211_queue_work(&local->hw, &sdata->work); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 93af0f1c9d99..0d166e766dad 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -775,11 +775,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) WLAN_EID_QOS_CAPA, WLAN_EID_RRM_ENABLED_CAPABILITIES, WLAN_EID_MOBILITY_DOMAIN, + WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */ + WLAN_EID_RIC_DATA, /* reassoc only */ WLAN_EID_SUPPORTED_REGULATORY_CLASSES, }; - noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, - before_ht, ARRAY_SIZE(before_ht), - offset); + static const u8 after_ric[] = { + WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + WLAN_EID_HT_CAPABILITY, + WLAN_EID_BSS_COEX_2040, + WLAN_EID_EXT_CAPABILITY, + WLAN_EID_QOS_TRAFFIC_CAPA, + WLAN_EID_TIM_BCAST_REQ, + WLAN_EID_INTERWORKING, + /* 60GHz doesn't happen right now */ + WLAN_EID_VHT_CAPABILITY, + WLAN_EID_OPMODE_NOTIF, + }; + + noffset = ieee80211_ie_split_ric(assoc_data->ie, + assoc_data->ie_len, + before_ht, + ARRAY_SIZE(before_ht), + after_ric, + ARRAY_SIZE(after_ric), + offset); pos = skb_put(skb, noffset - offset); memcpy(pos, assoc_data->ie + offset, noffset - offset); offset = noffset; @@ -813,6 +832,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) WLAN_EID_TIM_BCAST_REQ, WLAN_EID_INTERWORKING, }; + + /* RIC already taken above, so no need to handle here anymore */ noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len, before_vht, ARRAY_SIZE(before_vht), offset); @@ -1001,14 +1022,7 @@ static void ieee80211_chswitch_work(struct work_struct *work) /* XXX: shouldn't really modify cfg80211-owned data! */ ifmgd->associated->channel = sdata->csa_chandef.chan; - sdata->vif.csa_active = false; - - /* XXX: wait for a beacon first? */ - if (sdata->csa_block_tx) { - ieee80211_wake_vif_queues(local, sdata, - IEEE80211_QUEUE_STOP_REASON_CSA); - sdata->csa_block_tx = false; - } + ifmgd->csa_waiting_bcn = true; ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); @@ -1019,6 +1033,35 @@ out: sdata_unlock(sdata); } +static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + int ret; + + sdata_assert_lock(sdata); + + WARN_ON(!sdata->vif.csa_active); + + if (sdata->csa_block_tx) { + ieee80211_wake_vif_queues(local, sdata, + IEEE80211_QUEUE_STOP_REASON_CSA); + sdata->csa_block_tx = false; + } + + sdata->vif.csa_active = false; + ifmgd->csa_waiting_bcn = false; + + ret = drv_post_channel_switch(sdata); + if (ret) { + sdata_info(sdata, + "driver post channel switch failed, disconnecting\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + return; + } +} + void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); @@ -1046,7 +1089,8 @@ static void ieee80211_chswitch_timer(unsigned long data) static void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, - u64 timestamp, struct ieee802_11_elems *elems, + u64 timestamp, u32 device_timestamp, + struct ieee802_11_elems *elems, bool beacon) { struct ieee80211_local *local = sdata->local; @@ -1056,6 +1100,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx *chanctx; enum ieee80211_band current_band; struct ieee80211_csa_ie csa_ie; + struct ieee80211_channel_switch ch_switch; int res; sdata_assert_lock(sdata); @@ -1110,21 +1155,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, chanctx = container_of(conf, struct ieee80211_chanctx, conf); - if (local->use_chanctx) { - u32 num_chanctx = 0; - list_for_each_entry(chanctx, &local->chanctx_list, list) - num_chanctx++; + if (local->use_chanctx && + !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { + sdata_info(sdata, + "driver doesn't support chan-switch with channel contexts\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); + return; + } - if (num_chanctx > 1 || - !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { - sdata_info(sdata, - "not handling chan-switch with channel contexts\n"); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - return; - } + ch_switch.timestamp = timestamp; + ch_switch.device_timestamp = device_timestamp; + ch_switch.block_tx = csa_ie.mode; + ch_switch.chandef = csa_ie.chandef; + ch_switch.count = csa_ie.count; + + if (drv_pre_channel_switch(sdata, &ch_switch)) { + sdata_info(sdata, + "preparing for channel switch failed, disconnecting\n"); + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); + return; } res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef, @@ -1152,14 +1207,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (local->ops->channel_switch) { /* use driver's channel switch callback */ - struct ieee80211_channel_switch ch_switch = { - .timestamp = timestamp, - .block_tx = csa_ie.mode, - .chandef = csa_ie.chandef, - .count = csa_ie.count, - }; - - drv_channel_switch(local, &ch_switch); + drv_channel_switch(local, sdata, &ch_switch); return; } @@ -1580,6 +1628,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work) mutex_unlock(&sdata->local->mtx); } +static bool +__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + bool ret; + int ac; + + if (local->hw.queues < IEEE80211_NUM_ACS) + return false; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; + int non_acm_ac; + unsigned long now = jiffies; + + if (tx_tspec->action == TX_TSPEC_ACTION_NONE && + tx_tspec->admitted_time && + time_after(now, tx_tspec->time_slice_start + HZ)) { + tx_tspec->consumed_tx_time = 0; + tx_tspec->time_slice_start = now; + + if (tx_tspec->downgraded) + tx_tspec->action = + TX_TSPEC_ACTION_STOP_DOWNGRADE; + } + + switch (tx_tspec->action) { + case TX_TSPEC_ACTION_STOP_DOWNGRADE: + /* take the original parameters */ + if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac])) + sdata_err(sdata, + "failed to set TX queue parameters for queue %d\n", + ac); + tx_tspec->action = TX_TSPEC_ACTION_NONE; + tx_tspec->downgraded = false; + ret = true; + break; + case TX_TSPEC_ACTION_DOWNGRADE: + if (time_after(now, tx_tspec->time_slice_start + HZ)) { + tx_tspec->action = TX_TSPEC_ACTION_NONE; + ret = true; + break; + } + /* downgrade next lower non-ACM AC */ + for (non_acm_ac = ac + 1; + non_acm_ac < IEEE80211_NUM_ACS; + non_acm_ac++) + if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac))) + break; + /* The loop will result in using BK even if it requires + * admission control, such configuration makes no sense + * and we have to transmit somehow - the AC selection + * does the same thing. + */ + if (drv_conf_tx(local, sdata, ac, + &sdata->tx_conf[non_acm_ac])) + sdata_err(sdata, + "failed to set TX queue parameters for queue %d\n", + ac); + tx_tspec->action = TX_TSPEC_ACTION_NONE; + ret = true; + schedule_delayed_work(&ifmgd->tx_tspec_wk, + tx_tspec->time_slice_start + HZ - now + 1); + break; + case TX_TSPEC_ACTION_NONE: + /* nothing now */ + break; + } + } + + return ret; +} + +void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata) +{ + if (__ieee80211_sta_handle_tspec_ac_params(sdata)) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS); +} + +static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata; + + sdata = container_of(work, struct ieee80211_sub_if_data, + u.mgd.tx_tspec_wk.work); + ieee80211_sta_handle_tspec_ac_params(sdata); +} + /* MLME */ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -1664,12 +1801,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local, params.uapsd = uapsd; mlme_dbg(sdata, - "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n", + "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n", queue, aci, acm, params.aifs, params.cw_min, params.cw_max, - params.txop, params.uapsd); + params.txop, params.uapsd, + ifmgd->tx_tspec[queue].downgraded); sdata->tx_conf[queue] = params; - if (drv_conf_tx(local, sdata, queue, ¶ms)) + if (!ifmgd->tx_tspec[queue].downgraded && + drv_conf_tx(local, sdata, queue, ¶ms)) sdata_err(sdata, "failed to set TX queue parameters for queue %d\n", queue); @@ -1924,6 +2063,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_vif_release_channel(sdata); sdata->vif.csa_active = false; + ifmgd->csa_waiting_bcn = false; if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -1931,6 +2071,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, } mutex_unlock(&local->mtx); + /* existing TX TSPEC sessions no longer exist */ + memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec)); + cancel_delayed_work_sync(&ifmgd->tx_tspec_wk); + sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; } @@ -1983,9 +2127,46 @@ out: mutex_unlock(&local->mtx); } +static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr, + u16 tx_time) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + int ac = ieee80211_ac_from_tid(tid); + struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac]; + unsigned long now = jiffies; + + if (likely(!tx_tspec->admitted_time)) + return; + + if (time_after(now, tx_tspec->time_slice_start + HZ)) { + tx_tspec->consumed_tx_time = 0; + tx_tspec->time_slice_start = now; + + if (tx_tspec->downgraded) { + tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE; + schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); + } + } + + if (tx_tspec->downgraded) + return; + + tx_tspec->consumed_tx_time += tx_time; + + if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) { + tx_tspec->downgraded = true; + tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE; + schedule_delayed_work(&ifmgd->tx_tspec_wk, 0); + } +} + void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, - struct ieee80211_hdr *hdr, bool ack) + struct ieee80211_hdr *hdr, bool ack, u16 tx_time) { + ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time); + if (!ieee80211_is_data(hdr->frame_control)) return; @@ -2048,8 +2229,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); run_again(sdata, ifmgd->probe_timeout); - if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) - ieee80211_flush_queues(sdata->local, sdata); } static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, @@ -2172,6 +2351,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) true, frame_buf); mutex_lock(&local->mtx); sdata->vif.csa_active = false; + ifmgd->csa_waiting_bcn = false; if (sdata->csa_block_tx) { ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -3196,6 +3376,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } } + if (ifmgd->csa_waiting_bcn) + ieee80211_chswitch_post_beacon(sdata); + if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) return; ifmgd->beacon_crc = ncrc; @@ -3204,6 +3387,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems); ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, + rx_status->device_timestamp, &elems, true); if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && @@ -3335,8 +3519,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, break; ieee80211_sta_process_chanswitch(sdata, - rx_status->mactime, - &elems, false); + rx_status->mactime, + rx_status->device_timestamp, + &elems, false); } else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { ies_len = skb->len - offsetof(struct ieee80211_mgmt, @@ -3357,8 +3542,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, &mgmt->u.action.u.ext_chan_switch.data; ieee80211_sta_process_chanswitch(sdata, - rx_status->mactime, - &elems, false); + rx_status->mactime, + rx_status->device_timestamp, + &elems, false); } break; } @@ -3665,11 +3851,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; if (local->quiescing) return; - if (sdata->vif.csa_active) + if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) return; sdata->u.mgd.connection_loss = false; @@ -3687,7 +3874,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data) if (local->quiescing) return; - if (sdata->vif.csa_active) + if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) return; ieee80211_queue_work(&local->hw, &ifmgd->monitor_work); @@ -3799,6 +3986,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, (unsigned long) sdata); + INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk, + ieee80211_sta_handle_tspec_ac_params_wk); ifmgd->flags = 0; ifmgd->powersave = sdata->wdev.ps; diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c new file mode 100644 index 000000000000..358d5f9d8207 --- /dev/null +++ b/net/mac80211/ocb.c @@ -0,0 +1,250 @@ +/* + * OCB mode implementation + * + * Copyright: (c) 2014 Czech Technical University in Prague + * (c) 2014 Volkswagen Group Research + * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> + * Funded by: Volkswagen Group Research + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/if_ether.h> +#include <linux/skbuff.h> +#include <linux/if_arp.h> +#include <linux/etherdevice.h> +#include <linux/rtnetlink.h> +#include <net/mac80211.h> +#include <asm/unaligned.h> + +#include "ieee80211_i.h" +#include "driver-ops.h" +#include "rate.h" + +#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ) +#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ) +#define IEEE80211_OCB_MAX_STA_ENTRIES 128 + +/** + * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks + * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks + * + * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb + */ +enum ocb_deferred_task_flags { + OCB_WORK_HOUSEKEEPING, +}; + +void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, + u32 supp_rates) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + struct ieee80211_local *local = sdata->local; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_supported_band *sband; + enum nl80211_bss_scan_width scan_width; + struct sta_info *sta; + int band; + + /* XXX: Consider removing the least recently used entry and + * allow new one to be added. + */ + if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) { + net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n", + sdata->name, addr); + return; + } + + ocb_dbg(sdata, "Adding new OCB station %pM\n", addr); + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (WARN_ON_ONCE(!chanctx_conf)) { + rcu_read_unlock(); + return; + } + band = chanctx_conf->def.chan->band; + scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def); + rcu_read_unlock(); + + sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); + if (!sta) + return; + + sta->last_rx = jiffies; + + /* Add only mandatory rates for now */ + sband = local->hw.wiphy->bands[band]; + sta->sta.supp_rates[band] = + ieee80211_mandatory_rates(sband, scan_width); + + spin_lock(&ifocb->incomplete_lock); + list_add(&sta->list, &ifocb->incomplete_stations); + spin_unlock(&ifocb->incomplete_lock); + ieee80211_queue_work(&local->hw, &sdata->work); +} + +static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta) + __acquires(RCU) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u8 addr[ETH_ALEN]; + + memcpy(addr, sta->sta.addr, ETH_ALEN); + + ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n", + addr, sdata->name); + + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + + rate_control_rate_init(sta); + + /* If it fails, maybe we raced another insertion? */ + if (sta_info_insert_rcu(sta)) + return sta_info_get(sdata, addr); + return sta; +} + +static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + + ocb_dbg(sdata, "Running ocb housekeeping\n"); + + ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT); + + mod_timer(&ifocb->housekeeping_timer, + round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL)); +} + +void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + struct sta_info *sta; + + if (ifocb->joined != true) + return; + + sdata_lock(sdata); + + spin_lock_bh(&ifocb->incomplete_lock); + while (!list_empty(&ifocb->incomplete_stations)) { + sta = list_first_entry(&ifocb->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifocb->incomplete_lock); + + ieee80211_ocb_finish_sta(sta); + rcu_read_unlock(); + spin_lock_bh(&ifocb->incomplete_lock); + } + spin_unlock_bh(&ifocb->incomplete_lock); + + if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags)) + ieee80211_ocb_housekeeping(sdata); + + sdata_unlock(sdata); +} + +static void ieee80211_ocb_housekeeping_timer(unsigned long data) +{ + struct ieee80211_sub_if_data *sdata = (void *)data; + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + + set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); + + ieee80211_queue_work(&local->hw, &sdata->work); +} + +void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + + setup_timer(&ifocb->housekeeping_timer, + ieee80211_ocb_housekeeping_timer, + (unsigned long)sdata); + INIT_LIST_HEAD(&ifocb->incomplete_stations); + spin_lock_init(&ifocb->incomplete_lock); +} + +int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata, + struct ocb_setup *setup) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + u32 changed = BSS_CHANGED_OCB; + int err; + + if (ifocb->joined == true) + return -EINVAL; + + sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; + sdata->smps_mode = IEEE80211_SMPS_OFF; + sdata->needed_rx_chains = sdata->local->rx_chains; + + mutex_lock(&sdata->local->mtx); + err = ieee80211_vif_use_channel(sdata, &setup->chandef, + IEEE80211_CHANCTX_SHARED); + mutex_unlock(&sdata->local->mtx); + if (err) + return err; + + ieee80211_bss_info_change_notify(sdata, changed); + + ifocb->joined = true; + + set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags); + ieee80211_queue_work(&local->hw, &sdata->work); + + netif_carrier_on(sdata->dev); + return 0; +} + +int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ocb *ifocb = &sdata->u.ocb; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + ifocb->joined = false; + sta_info_flush(sdata); + + spin_lock_bh(&ifocb->incomplete_lock); + while (!list_empty(&ifocb->incomplete_stations)) { + sta = list_first_entry(&ifocb->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifocb->incomplete_lock); + + sta_info_free(local, sta); + spin_lock_bh(&ifocb->incomplete_lock); + } + spin_unlock_bh(&ifocb->incomplete_lock); + + netif_carrier_off(sdata->dev); + clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB); + + mutex_lock(&sdata->local->mtx); + ieee80211_vif_release_channel(sdata); + mutex_unlock(&sdata->local->mtx); + + skb_queue_purge(&sdata->skb_queue); + + del_timer_sync(&sdata->u.ocb.housekeeping_timer); + /* If the timer fired while we waited for it, it will have + * requeued the work. Now the work will be running again + * but will not rearm the timer again because it checks + * whether we are connected to the network or not -- at this + * point we shouldn't be anymore. + */ + + return 0; +} diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 2baa7ed8789d..c2b91bf47f6d 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -191,7 +191,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) * (1) if any success probabilitiy >= 95%, out of those rates * choose the maximum throughput rate as max_prob_rate * (2) if all success probabilities < 95%, the rate with - * highest success probability is choosen as max_prob_rate */ + * highest success probability is chosen as max_prob_rate */ if (mrs->probability >= MINSTREL_FRAC(95, 100)) { if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp) tmp_prob_rate = i; diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index df90ce2db00c..c50fd94d2aef 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -10,6 +10,7 @@ #include <linux/skbuff.h> #include <linux/debugfs.h> #include <linux/random.h> +#include <linux/moduleparam.h> #include <linux/ieee80211.h> #include <net/mac80211.h> #include "rate.h" @@ -34,12 +35,17 @@ /* Transmit duration for the raw data part of an average sized packet */ #define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) +#define BW_20 0 +#define BW_40 1 +#define BW_80 2 + /* * Define group sort order: HT40 -> SGI -> #streams */ #define GROUP_IDX(_streams, _sgi, _ht40) \ + MINSTREL_HT_GROUP_0 + \ MINSTREL_MAX_STREAMS * 2 * _ht40 + \ - MINSTREL_MAX_STREAMS * _sgi + \ + MINSTREL_MAX_STREAMS * _sgi + \ _streams - 1 /* MCS rate information for an MCS group */ @@ -47,6 +53,7 @@ [GROUP_IDX(_streams, _sgi, _ht40)] = { \ .streams = _streams, \ .flags = \ + IEEE80211_TX_RC_MCS | \ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ (_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ .duration = { \ @@ -61,6 +68,47 @@ } \ } +#define VHT_GROUP_IDX(_streams, _sgi, _bw) \ + (MINSTREL_VHT_GROUP_0 + \ + MINSTREL_MAX_STREAMS * 2 * (_bw) + \ + MINSTREL_MAX_STREAMS * (_sgi) + \ + (_streams) - 1) + +#define BW2VBPS(_bw, r3, r2, r1) \ + (_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1) + +#define VHT_GROUP(_streams, _sgi, _bw) \ + [VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \ + .streams = _streams, \ + .flags = \ + IEEE80211_TX_RC_VHT_MCS | \ + (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ + (_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \ + _bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ + .duration = { \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 117, 54, 26)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 234, 108, 52)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 351, 162, 78)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 468, 216, 104)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 702, 324, 156)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 936, 432, 208)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1053, 486, 234)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1170, 540, 260)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1404, 648, 312)), \ + MCS_DURATION(_streams, _sgi, \ + BW2VBPS(_bw, 1560, 720, 346)) \ + } \ +} + #define CCK_DURATION(_bitrate, _short, _len) \ (1000 * (10 /* SIFS */ + \ (_short ? 72 + 24 : 144 + 48) + \ @@ -76,53 +124,96 @@ CCK_ACK_DURATION(55, _short), \ CCK_ACK_DURATION(110, _short) -#define CCK_GROUP \ - [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \ - .streams = 0, \ - .duration = { \ - CCK_DURATION_LIST(false), \ - CCK_DURATION_LIST(true) \ - } \ +#define CCK_GROUP \ + [MINSTREL_CCK_GROUP] = { \ + .streams = 0, \ + .flags = 0, \ + .duration = { \ + CCK_DURATION_LIST(false), \ + CCK_DURATION_LIST(true) \ + } \ } +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT +static bool minstrel_vht_only = true; +module_param(minstrel_vht_only, bool, 0644); +MODULE_PARM_DESC(minstrel_vht_only, + "Use only VHT rates when VHT is supported by sta."); +#endif + /* * To enable sufficiently targeted rate sampling, MCS rates are divided into * groups, based on the number of streams and flags (HT40, SGI) that they * use. * * Sortorder has to be fixed for GROUP_IDX macro to be applicable: - * HT40 -> SGI -> #streams + * BW -> SGI -> #streams */ const struct mcs_group minstrel_mcs_groups[] = { - MCS_GROUP(1, 0, 0), - MCS_GROUP(2, 0, 0), + MCS_GROUP(1, 0, BW_20), + MCS_GROUP(2, 0, BW_20), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 0, 0), + MCS_GROUP(3, 0, BW_20), #endif - MCS_GROUP(1, 1, 0), - MCS_GROUP(2, 1, 0), + MCS_GROUP(1, 1, BW_20), + MCS_GROUP(2, 1, BW_20), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 1, 0), + MCS_GROUP(3, 1, BW_20), #endif - MCS_GROUP(1, 0, 1), - MCS_GROUP(2, 0, 1), + MCS_GROUP(1, 0, BW_40), + MCS_GROUP(2, 0, BW_40), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 0, 1), + MCS_GROUP(3, 0, BW_40), #endif - MCS_GROUP(1, 1, 1), - MCS_GROUP(2, 1, 1), + MCS_GROUP(1, 1, BW_40), + MCS_GROUP(2, 1, BW_40), #if MINSTREL_MAX_STREAMS >= 3 - MCS_GROUP(3, 1, 1), + MCS_GROUP(3, 1, BW_40), #endif - /* must be last */ - CCK_GROUP -}; + CCK_GROUP, -#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1) +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT + VHT_GROUP(1, 0, BW_20), + VHT_GROUP(2, 0, BW_20), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 0, BW_20), +#endif + + VHT_GROUP(1, 1, BW_20), + VHT_GROUP(2, 1, BW_20), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 1, BW_20), +#endif + + VHT_GROUP(1, 0, BW_40), + VHT_GROUP(2, 0, BW_40), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 0, BW_40), +#endif + + VHT_GROUP(1, 1, BW_40), + VHT_GROUP(2, 1, BW_40), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 1, BW_40), +#endif + + VHT_GROUP(1, 0, BW_80), + VHT_GROUP(2, 0, BW_80), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 0, BW_80), +#endif + + VHT_GROUP(1, 1, BW_80), + VHT_GROUP(2, 1, BW_80), +#if MINSTREL_MAX_STREAMS >= 3 + VHT_GROUP(3, 1, BW_80), +#endif +#endif +}; static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly; @@ -130,16 +221,64 @@ static void minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi); /* + * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer) + * e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1 + * + * Returns the valid mcs map for struct minstrel_mcs_group_data.supported + */ +static u16 +minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map) +{ + u16 mask = 0; + + if (bw == BW_20) { + if (nss != 3 && nss != 6) + mask = BIT(9); + } else if (bw == BW_80) { + if (nss == 3 || nss == 7) + mask = BIT(6); + else if (nss == 6) + mask = BIT(9); + } else { + WARN_ON(bw != BW_40); + } + + switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) { + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mask |= 0x300; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mask |= 0x200; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + break; + default: + mask = 0x3ff; + } + + return 0x3ff & ~mask; +} + +/* * Look up an MCS group index based on mac80211 rate information */ static int minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) { - return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1, + return GROUP_IDX((rate->idx / 8) + 1, !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); } +static int +minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate) +{ + return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate), + !!(rate->flags & IEEE80211_TX_RC_SHORT_GI), + !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + + 2*!!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)); +} + static struct minstrel_rate_stats * minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_tx_rate *rate) @@ -149,6 +288,9 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, if (rate->flags & IEEE80211_TX_RC_MCS) { group = minstrel_ht_get_group_idx(rate); idx = rate->idx % 8; + } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + group = minstrel_vht_get_group_idx(rate); + idx = ieee80211_rate_get_vht_mcs(rate); } else { group = MINSTREL_CCK_GROUP; @@ -240,8 +382,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) * MCS groups, CCK rates do not provide aggregation and are therefore at last. */ static void -minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, - u8 *tp_list) +minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, + u16 *tp_list) { int cur_group, cur_idx, cur_thr, cur_prob; int tmp_group, tmp_idx, tmp_thr, tmp_prob; @@ -278,7 +420,7 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index, * Find and set the topmost probability rate per sta and per group */ static void -minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index) +minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) { struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mr; @@ -321,8 +463,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index) */ static void minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, - u8 tmp_mcs_tp_rate[MAX_THR_RATES], - u8 tmp_cck_tp_rate[MAX_THR_RATES]) + u16 tmp_mcs_tp_rate[MAX_THR_RATES], + u16 tmp_cck_tp_rate[MAX_THR_RATES]) { unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp; int i; @@ -386,8 +528,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mr; int group, i, j; - u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; - u8 tmp_cck_tp_rate[MAX_THR_RATES], index; + u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; + u16 tmp_cck_tp_rate[MAX_THR_RATES], index; if (mi->ampdu_packets > 0) { mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, @@ -485,7 +627,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat if (!rate->count) return false; - if (rate->flags & IEEE80211_TX_RC_MCS) + if (rate->flags & IEEE80211_TX_RC_MCS || + rate->flags & IEEE80211_TX_RC_VHT_MCS) return true; return rate->idx == mp->cck_rates[0] || @@ -517,7 +660,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi) } static void -minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary) +minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary) { int group, orig_group; @@ -714,7 +857,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; struct minstrel_rate_stats *mr; u8 idx; - u16 flags; + u16 flags = group->flags; mr = minstrel_get_ratestats(mi, index); if (!mr->retry_updated) @@ -730,13 +873,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ratetbl->rate[offset].count_rts = mr->retry_count_rtscts; } - if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { + if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; - flags = 0; - } else { + else if (flags & IEEE80211_TX_RC_VHT_MCS) + idx = ((group->streams - 1) << 4) | + ((index % MCS_GROUP_RATES) & 0xF); + else idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8; - flags = IEEE80211_TX_RC_MCS | group->flags; - } if (offset > 0) { ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts; @@ -916,13 +1059,15 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { int idx = sample_idx % ARRAY_SIZE(mp->cck_rates); rate->idx = mp->cck_rates[idx]; - rate->flags = 0; - return; + } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) { + ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES, + sample_group->streams); + } else { + rate->idx = sample_idx % MCS_GROUP_RATES + + (sample_group->streams - 1) * 8; } - rate->idx = sample_idx % MCS_GROUP_RATES + - (sample_group->streams - 1) * 8; - rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags; + rate->flags = sample_group->flags; } static void @@ -962,6 +1107,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, struct minstrel_ht_sta *mi = &msp->ht; struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; u16 sta_cap = sta->ht_cap.cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + int use_vht; int n_supported = 0; int ack_dur; int stbc; @@ -971,8 +1118,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, if (!sta->ht_cap.ht_supported) goto use_legacy; - BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != - MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1); + BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB); + +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT + if (vht_cap->vht_supported) + use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0); + else +#endif + use_vht = 0; msp->is_ht = true; memset(mi, 0, sizeof(*mi)); @@ -997,22 +1150,28 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, } mi->sample_tries = 4; - stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >> - IEEE80211_HT_CAP_RX_STBC_SHIFT; - mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT; + /* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */ + if (!use_vht) { + stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >> + IEEE80211_HT_CAP_RX_STBC_SHIFT; + mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT; - if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING) - mi->tx_flags |= IEEE80211_TX_CTL_LDPC; + if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING) + mi->tx_flags |= IEEE80211_TX_CTL_LDPC; + } for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { + u32 gflags = minstrel_mcs_groups[i].flags; + int bw, nss; + mi->groups[i].supported = 0; if (i == MINSTREL_CCK_GROUP) { minstrel_ht_update_cck(mp, mi, sband, sta); continue; } - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) { + if (gflags & IEEE80211_TX_RC_SHORT_GI) { + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) { if (!(sta_cap & IEEE80211_HT_CAP_SGI_40)) continue; } else { @@ -1021,17 +1180,51 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, } } - if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH && + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH && sta->bandwidth < IEEE80211_STA_RX_BW_40) continue; + nss = minstrel_mcs_groups[i].streams; + /* Mark MCS > 7 as unsupported if STA is in static SMPS mode */ - if (sta->smps_mode == IEEE80211_SMPS_STATIC && - minstrel_mcs_groups[i].streams > 1) + if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1) + continue; + + /* HT rate */ + if (gflags & IEEE80211_TX_RC_MCS) { +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT + if (use_vht && minstrel_vht_only) + continue; +#endif + mi->groups[i].supported = mcs->rx_mask[nss - 1]; + if (mi->groups[i].supported) + n_supported++; continue; + } + + /* VHT rate */ + if (!vht_cap->vht_supported || + WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) || + WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH)) + continue; + + if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) { + if (sta->bandwidth < IEEE80211_STA_RX_BW_80 || + ((gflags & IEEE80211_TX_RC_SHORT_GI) && + !(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) { + continue; + } + } + + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = BW_40; + else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = BW_80; + else + bw = BW_20; - mi->groups[i].supported = - mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; + mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss, + vht_cap->vht_mcs.tx_mcs_map); if (mi->groups[i].supported) n_supported++; diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 01570e0e014b..f2217d6aa0c2 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -13,10 +13,32 @@ * The number of streams can be changed to 2 to reduce code * size and memory footprint. */ -#define MINSTREL_MAX_STREAMS 3 -#define MINSTREL_STREAM_GROUPS 4 +#define MINSTREL_MAX_STREAMS 3 +#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */ +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT +#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */ +#else +#define MINSTREL_VHT_STREAM_GROUPS 0 +#endif -#define MCS_GROUP_RATES 8 +#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \ + MINSTREL_HT_STREAM_GROUPS) +#define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \ + MINSTREL_VHT_STREAM_GROUPS) +#define MINSTREL_CCK_GROUPS_NB 1 +#define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \ + MINSTREL_VHT_GROUPS_NB + \ + MINSTREL_CCK_GROUPS_NB) + +#define MINSTREL_HT_GROUP_0 0 +#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB) +#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1) + +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT +#define MCS_GROUP_RATES 10 +#else +#define MCS_GROUP_RATES 8 +#endif struct mcs_group { u32 flags; @@ -31,11 +53,11 @@ struct minstrel_mcs_group_data { u8 column; /* bitfield of supported MCS rates of this group */ - u8 supported; + u16 supported; /* sorted rate set within a MCS group*/ - u8 max_group_tp_rate[MAX_THR_RATES]; - u8 max_group_prob_rate; + u16 max_group_tp_rate[MAX_THR_RATES]; + u16 max_group_prob_rate; /* MCS rate statistics */ struct minstrel_rate_stats rates[MCS_GROUP_RATES]; @@ -52,8 +74,8 @@ struct minstrel_ht_sta { unsigned int avg_ampdu_len; /* overall sorted rate set */ - u8 max_tp_rate[MAX_THR_RATES]; - u8 max_prob_rate; + u16 max_tp_rate[MAX_THR_RATES]; + u16 max_prob_rate; /* time of last status update */ unsigned long stats_update; @@ -80,7 +102,7 @@ struct minstrel_ht_sta { u8 cck_supported_short; /* MCS rate group info and statistics */ - struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1]; + struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB]; }; struct minstrel_ht_sta_priv { diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index d537bec93754..20c676b8e5b6 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -18,19 +18,23 @@ static char * minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) { - unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; const struct mcs_group *mg; unsigned int j, tp, prob, eprob; char htmode = '2'; char gimode = 'L'; + u32 gflags; if (!mi->groups[i].supported) return p; mg = &minstrel_mcs_groups[i]; - if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + gflags = mg->flags; + + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) htmode = '4'; - if (mg->flags & IEEE80211_TX_RC_SHORT_GI) + else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + htmode = '8'; + if (gflags & IEEE80211_TX_RC_SHORT_GI) gimode = 'S'; for (j = 0; j < MCS_GROUP_RATES; j++) { @@ -41,10 +45,12 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) if (!(mi->groups[i].supported & BIT(j))) continue; - if (i == max_mcs) - p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S'); + if (gflags & IEEE80211_TX_RC_MCS) + p += sprintf(p, " HT%c0/%cGI ", htmode, gimode); + else if (gflags & IEEE80211_TX_RC_VHT_MCS) + p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode); else - p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); + p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S'); *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' '; *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' '; @@ -52,11 +58,14 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) *(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' '; *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; - if (i == max_mcs) { - int r = bitrates[j % 4]; - p += sprintf(p, " %2u.%1uM", r / 10, r % 10); + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j); + } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { + p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); } else { - p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); + int r = bitrates[j % 4]; + + p += sprintf(p, " %2u.%1uM ", r / 10, r % 10); } tp = mr->cur_tp / 10; @@ -85,7 +94,6 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) struct minstrel_ht_sta *mi = &msp->ht; struct minstrel_debugfs_info *ms; unsigned int i; - unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; char *p; int ret; @@ -96,18 +104,19 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) return ret; } - ms = kmalloc(8192, GFP_KERNEL); + ms = kmalloc(32768, GFP_KERNEL); if (!ms) return -ENOMEM; file->private_data = ms; p = ms->buf; - p += sprintf(p, "type rate tpt eprob *prob " + p += sprintf(p, " type rate tpt eprob *prob " "ret *ok(*cum) ok( cum)\n"); - - p = minstrel_ht_stats_dump(mi, max_mcs, p); - for (i = 0; i < max_mcs; i++) + p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); + for (i = 0; i < MINSTREL_CCK_GROUP; i++) + p = minstrel_ht_stats_dump(mi, i, p); + for (i++; i < ARRAY_SIZE(mi->groups); i++) p = minstrel_ht_stats_dump(mi, i, p); p += sprintf(p, "\nTotal packet count:: ideal %d " @@ -119,7 +128,7 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); ms->len = p - ms->buf; - WARN_ON(ms->len + sizeof(*ms) > 8192); + WARN_ON(ms->len + sizeof(*ms) > 32768); return nonseekable_open(inode, file); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index a37f9af634cb..a726bb169302 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1032,6 +1032,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) ieee80211_is_pspoll(hdr->frame_control)) && rx->sdata->vif.type != NL80211_IFTYPE_ADHOC && rx->sdata->vif.type != NL80211_IFTYPE_WDS && + rx->sdata->vif.type != NL80211_IFTYPE_OCB && (!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) { /* * accept port control frames from the AP even when it's not @@ -1272,6 +1273,12 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) sta->last_rx_rate_vht_nss = status->vht_nss; } } + } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) { + u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, + NL80211_IFTYPE_OCB); + /* OCB uses wild-card BSSID */ + if (is_broadcast_ether_addr(bssid)) + sta->last_rx = jiffies; } else if (!is_multicast_ether_addr(hdr->addr1)) { /* * Mesh beacons will update last_rx when if they are found to @@ -2820,6 +2827,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx) if (!ieee80211_vif_is_mesh(&sdata->vif) && sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_OCB && sdata->vif.type != NL80211_IFTYPE_STATION) return RX_DROP_MONITOR; @@ -3130,6 +3138,33 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx, BIT(rate_idx)); } break; + case NL80211_IFTYPE_OCB: + if (!bssid) + return false; + if (ieee80211_is_beacon(hdr->frame_control)) { + return false; + } else if (!is_broadcast_ether_addr(bssid)) { + ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n"); + return false; + } else if (!multicast && + !ether_addr_equal(sdata->dev->dev_addr, + hdr->addr1)) { + /* if we are in promisc mode we also accept + * packets not destined for us + */ + if (!(sdata->dev->flags & IFF_PROMISC)) + return false; + rx->flags &= ~IEEE80211_RX_RA_MATCH; + } else if (!rx->sta) { + int rate_idx; + if (status->flag & RX_FLAG_HT) + rate_idx = 0; /* TODO: HT rates */ + else + rate_idx = status->rate_idx; + ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2, + BIT(rate_idx)); + } + break; case NL80211_IFTYPE_MESH_POINT: if (!multicast && !ether_addr_equal(sdata->vif.addr, hdr->addr1)) { diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index de494df3bab8..adc25371b171 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -501,7 +501,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) /* make the station visible */ sta_info_hash_add(local, sta); - list_add_rcu(&sta->list, &local->sta_list); + list_add_tail_rcu(&sta->list, &local->sta_list); /* notify driver */ err = sta_info_insert_drv_state(local, sdata, sta); @@ -1531,7 +1531,7 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta) break; case 0: /* XXX: what is a good value? */ - n_frames = 8; + n_frames = 128; break; } diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 89290e33dafe..9612d89fad56 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -704,7 +704,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) && (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) - ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked); + ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, + acked, info->status.tx_time); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) { if (info->flags & IEEE80211_TX_STAT_ACK) { diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 4ea25dec0698..b4f368e2cb3b 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -562,8 +562,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, /* infer the initiator if we can, to support old userspace */ switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: - if (sta) + if (sta) { set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); + sta->sta.tdls_initiator = false; + } /* fall-through */ case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_DISCOVERY_REQUEST: @@ -575,8 +577,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, * Make the last packet sent take effect for the initiator * value. */ - if (sta) + if (sta) { clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); + sta->sta.tdls_initiator = true; + } /* fall-through */ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: initiator = false; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 38fae7ebe984..809a4983eb4a 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -987,29 +987,34 @@ TRACE_EVENT(drv_flush, TRACE_EVENT(drv_channel_switch, TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, struct ieee80211_channel_switch *ch_switch), - TP_ARGS(local, ch_switch), + TP_ARGS(local, sdata, ch_switch), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY CHANDEF_ENTRY __field(u64, timestamp) + __field(u32, device_timestamp) __field(bool, block_tx) __field(u8, count) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; CHANDEF_ASSIGN(&ch_switch->chandef) __entry->timestamp = ch_switch->timestamp; + __entry->device_timestamp = ch_switch->device_timestamp; __entry->block_tx = ch_switch->block_tx; __entry->count = ch_switch->count; ), TP_printk( - LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d", - LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count + LOCAL_PR_FMT VIF_PR_FMT " new " CHANDEF_PR_FMT " count:%d", + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count ) ); @@ -1557,9 +1562,26 @@ DEFINE_EVENT(local_sdata_evt, drv_stop_ap, TP_ARGS(local, sdata) ); -DEFINE_EVENT(local_only_evt, drv_restart_complete, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +TRACE_EVENT(drv_reconfig_complete, + TP_PROTO(struct ieee80211_local *local, + enum ieee80211_reconfig_type reconfig_type), + TP_ARGS(local, reconfig_type), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u8, reconfig_type) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->reconfig_type = reconfig_type; + ), + + TP_printk( + LOCAL_PR_FMT " reconfig_type:%d", + LOCAL_PR_ARG, __entry->reconfig_type + ) + ); #if IS_ENABLED(CONFIG_IPV6) @@ -2106,6 +2128,72 @@ TRACE_EVENT(drv_channel_switch_beacon, ) ); +TRACE_EVENT(drv_pre_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel_switch *ch_switch), + + TP_ARGS(local, sdata, ch_switch), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + CHANDEF_ENTRY + __field(u64, timestamp) + __field(bool, block_tx) + __field(u8, count) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + CHANDEF_ASSIGN(&ch_switch->chandef) + __entry->timestamp = ch_switch->timestamp; + __entry->block_tx = ch_switch->block_tx; + __entry->count = ch_switch->count; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " prepare channel switch to " + CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu", + LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count, + __entry->block_tx, __entry->timestamp + ) +); + +DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + +TRACE_EVENT(drv_get_txpower, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + int dbm, int ret), + + TP_ARGS(local, sdata, dbm, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(int, dbm) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->dbm = dbm; + __entry->ret = ret; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret + ) +); + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 900632a250ec..3ffd91f295a6 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) */ return TX_DROP; + if (tx->sdata->vif.type == NL80211_IFTYPE_OCB) + return TX_CONTINUE; + if (tx->sdata->vif.type == NL80211_IFTYPE_WDS) return TX_CONTINUE; @@ -2013,6 +2016,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail_rcu; band = chanctx_conf->def.chan->band; break; + case NL80211_IFTYPE_OCB: + /* DA SA BSSID */ + memcpy(hdr.addr1, skb->data, ETH_ALEN); + memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + eth_broadcast_addr(hdr.addr3); + hdrlen = 24; + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto fail_rcu; + band = chanctx_conf->def.chan->band; + break; case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -2057,6 +2071,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * EAPOL frames from the local station. */ if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && + (sdata->vif.type != NL80211_IFTYPE_OCB) && !multicast && !authorized && (cpu_to_be16(ethertype) != sdata->control_port_protocol || !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 3c61060a4d2b..f9319a5dca64 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -693,6 +693,34 @@ void ieee80211_iterate_active_interfaces_rtnl( } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); +static void __iterate_stations(struct ieee80211_local *local, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data) +{ + struct sta_info *sta; + + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (!sta->uploaded) + continue; + + iterator(data, &sta->sta); + } +} + +void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw, + void (*iterator)(void *data, + struct ieee80211_sta *sta), + void *data) +{ + struct ieee80211_local *local = hw_to_local(hw); + + rcu_read_lock(); + __iterate_stations(local, iterator, data); + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic); + struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); @@ -1073,6 +1101,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, struct ieee80211_chanctx_conf *chanctx_conf; int ac; bool use_11b, enable_qos; + bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */ int aCWmin, aCWmax; if (!local->ops->conf_tx) @@ -1097,6 +1126,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, */ enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); + is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB); + /* Set defaults according to 802.11-2007 Table 7-37 */ aCWmax = 1023; if (use_11b) @@ -1118,7 +1149,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; - qparam.aifs = 7; + if (is_ocb) + qparam.aifs = 9; + else + qparam.aifs = 7; break; /* never happens but let's not leave undefined */ default: @@ -1126,21 +1160,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, qparam.cw_max = aCWmax; qparam.cw_min = aCWmin; qparam.txop = 0; - qparam.aifs = 3; + if (is_ocb) + qparam.aifs = 6; + else + qparam.aifs = 3; break; case IEEE80211_AC_VI: qparam.cw_max = aCWmin; qparam.cw_min = (aCWmin + 1) / 2 - 1; - if (use_11b) + if (is_ocb) + qparam.txop = 0; + else if (use_11b) qparam.txop = 6016/32; else qparam.txop = 3008/32; - qparam.aifs = 2; + + if (is_ocb) + qparam.aifs = 3; + else + qparam.aifs = 2; break; case IEEE80211_AC_VO: qparam.cw_max = (aCWmin + 1) / 2 - 1; qparam.cw_min = (aCWmin + 1) / 4 - 1; - if (use_11b) + if (is_ocb) + qparam.txop = 0; + else if (use_11b) qparam.txop = 3264/32; else qparam.txop = 1504/32; @@ -1813,6 +1858,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) ieee80211_bss_info_change_notify(sdata, changed); sdata_unlock(sdata); break; + case NL80211_IFTYPE_OCB: + changed |= BSS_CHANGED_OCB; + ieee80211_bss_info_change_notify(sdata, changed); + break; case NL80211_IFTYPE_ADHOC: changed |= BSS_CHANGED_IBSS; /* fall through */ @@ -1949,7 +1998,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) * We may want to change that later, however. */ if (!local->suspended || reconfig_due_to_wowlan) - drv_restart_complete(local); + drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART); if (!local->suspended) return 0; @@ -1960,6 +2009,9 @@ int ieee80211_reconfig(struct ieee80211_local *local) mb(); local->resuming = false; + if (!reconfig_due_to_wowlan) + drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND); + list_for_each_entry(sdata, &local->interfaces, list) { if (!ieee80211_sdata_running(sdata)) continue; @@ -2052,42 +2104,36 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) return false; } -/** - * ieee80211_ie_split - split an IE buffer according to ordering - * - * @ies: the IE buffer - * @ielen: the length of the IE buffer - * @ids: an array with element IDs that are allowed before - * the split - * @n_ids: the size of the element ID array - * @offset: offset where to start splitting in the buffer - * - * This function splits an IE buffer by updating the @offset - * variable to point to the location where the buffer should be - * split. - * - * It assumes that the given IE buffer is well-formed, this - * has to be guaranteed by the caller! - * - * It also assumes that the IEs in the buffer are ordered - * correctly, if not the result of using this function will not - * be ordered correctly either, i.e. it does no reordering. - * - * The function returns the offset where the next part of the - * buffer starts, which may be @ielen if the entire (remainder) - * of the buffer should be used. - */ -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset) +size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, + const u8 *after_ric, int n_after_ric, + size_t offset) { size_t pos = offset; - while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) - pos += 2 + ies[pos + 1]; + while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { + if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { + pos += 2 + ies[pos + 1]; + + while (pos < ielen && + !ieee80211_id_in_list(after_ric, n_after_ric, + ies[pos])) + pos += 2 + ies[pos + 1]; + } else { + pos += 2 + ies[pos + 1]; + } + } return pos; } +size_t ieee80211_ie_split(const u8 *ies, size_t ielen, + const u8 *ids, int n_ids, size_t offset) +{ + return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); +} +EXPORT_SYMBOL(ieee80211_ie_split); + size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) { size_t pos = offset; @@ -2526,11 +2572,23 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work) struct ieee80211_local *local = container_of(work, struct ieee80211_local, radar_detected_work); struct cfg80211_chan_def chandef = local->hw.conf.chandef; + struct ieee80211_chanctx *ctx; + int num_chanctx = 0; + + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) { + if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) + continue; + + num_chanctx++; + chandef = ctx->conf.def; + } + mutex_unlock(&local->chanctx_mtx); ieee80211_dfs_cac_cancel(local); - if (local->use_chanctx) - /* currently not handled */ + if (num_chanctx > 1) + /* XXX: multi-channel is not supported yet */ WARN_ON(1); else cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL); diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c index 9181fb6d6437..a4220e92f0cc 100644 --- a/net/mac80211/wep.c +++ b/net/mac80211/wep.c @@ -111,8 +111,6 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local, (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) return newhdr + hdrlen; - skb_set_network_header(skb, skb_network_offset(skb) + - IEEE80211_WEP_IV_LEN); ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen); return newhdr + hdrlen; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index 3b873989992c..fdf52db95b33 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -54,10 +54,18 @@ static int wme_downgrade_ac(struct sk_buff *skb) } static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb) + struct sta_info *sta, struct sk_buff *skb) { + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + /* in case we are a client verify acm is not set for this ac */ - while (unlikely(sdata->wmm_acm & BIT(skb->priority))) { + while (sdata->wmm_acm & BIT(skb->priority)) { + int ac = ieee802_1d_to_ac[skb->priority]; + + if (ifmgd->tx_tspec[ac].admitted_time && + skb->priority == ifmgd->tx_tspec[ac].up) + return ac; + if (wme_downgrade_ac(skb)) { /* * This should not really happen. The AP has marked all @@ -96,7 +104,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, p = ieee80211_get_qos_ctl(hdr); skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK; - return ieee80211_downgrade_queue(sdata, skb); + return ieee80211_downgrade_queue(sdata, NULL, skb); } /* Indicate which queue to use. */ @@ -108,6 +116,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, const u8 *ra = NULL; bool qos = false; struct mac80211_qos_map *qos_map; + u16 ret; if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) { skb->priority = 0; /* required for correct WPA/11i MIC */ @@ -139,6 +148,10 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_ADHOC: ra = skb->data; break; + case NL80211_IFTYPE_OCB: + /* all stations are required to support WME */ + qos = true; + break; default: break; } @@ -148,27 +161,29 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, if (sta) qos = sta->sta.wme; } - rcu_read_unlock(); if (!qos) { skb->priority = 0; /* required for correct WPA/11i MIC */ - return IEEE80211_AC_BE; + ret = IEEE80211_AC_BE; + goto out; } if (skb->protocol == sdata->control_port_protocol) { skb->priority = 7; - return ieee80211_downgrade_queue(sdata, skb); + goto downgrade; } /* use the data classifier to determine what 802.1d tag the * data frame has */ - rcu_read_lock(); qos_map = rcu_dereference(sdata->qos_map); skb->priority = cfg80211_classify8021d(skb, qos_map ? &qos_map->qos_map : NULL); - rcu_read_unlock(); - return ieee80211_downgrade_queue(sdata, skb); + downgrade: + ret = ieee80211_downgrade_queue(sdata, sta, skb); + out: + rcu_read_unlock(); + return ret; } /** diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index 7fea4bb8acbc..80151edc5195 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -13,8 +13,6 @@ #include <linux/netdevice.h> #include "ieee80211_i.h" -extern const int ieee802_1d_to_ac[8]; - u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, struct ieee80211_hdr *hdr); diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 983527a4c1ab..12398fde02e8 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -209,8 +209,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos = skb_push(skb, IEEE80211_TKIP_IV_LEN); memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + - IEEE80211_TKIP_IV_LEN); pos += hdrlen; /* the HW only needs room for the IV, but not the actual IV */ @@ -434,8 +432,6 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN); memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + - IEEE80211_CCMP_HDR_LEN); /* the HW only needs room for the IV, but not the actual IV */ if (info->control.hw_key && @@ -575,7 +571,6 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, pos = skb_push(skb, cs->hdr_len); memmove(pos, pos + cs->hdr_len, hdrlen); - skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len); return TX_CONTINUE; } diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig index 1818a99b3081..aa462b480a39 100644 --- a/net/mac802154/Kconfig +++ b/net/mac802154/Kconfig @@ -16,5 +16,5 @@ config MAC802154 been tested yet! If you plan to use HardMAC IEEE 802.15.4 devices, you can - say N here. Alternatievly you can say M to compile it as + say N here. Alternatively you can say M to compile it as module. diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile index 9723d6f3f3e5..702d8b466317 100644 --- a/net/mac802154/Makefile +++ b/net/mac802154/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_MAC802154) += mac802154.o -mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \ - monitor.o wpan.o llsec.o +mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \ + iface.o llsec.o util.o cfg.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c new file mode 100644 index 000000000000..c035708ada16 --- /dev/null +++ b/net/mac802154/cfg.c @@ -0,0 +1,210 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Authors: + * Alexander Aring <aar@pengutronix.de> + * + * Based on: net/mac80211/cfg.c + */ + +#include <net/rtnetlink.h> +#include <net/cfg802154.h> + +#include "ieee802154_i.h" +#include "driver-ops.h" +#include "cfg.h" + +static struct net_device * +ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy, + const char *name, int type) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + struct net_device *dev; + + rtnl_lock(); + dev = ieee802154_if_add(local, name, type, + cpu_to_le64(0x0000000000000000ULL)); + rtnl_unlock(); + + return dev; +} + +static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy, + struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + ieee802154_if_remove(sdata); +} + +static int +ieee802154_add_iface(struct wpan_phy *phy, const char *name, + enum nl802154_iftype type, __le64 extended_addr) +{ + struct ieee802154_local *local = wpan_phy_priv(phy); + struct net_device *err; + + err = ieee802154_if_add(local, name, type, extended_addr); + if (IS_ERR(err)) + return PTR_ERR(err); + + return 0; +} + +static int +ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev) +{ + ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev)); + + return 0; +} + +static int +ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + int ret; + + ASSERT_RTNL(); + + /* check if phy support this setting */ + if (!(wpan_phy->channels_supported[page] & BIT(channel))) + return -EINVAL; + + ret = drv_set_channel(local, page, channel); + if (!ret) { + wpan_phy->current_page = page; + wpan_phy->current_channel = channel; + } + + return ret; +} + +static int +ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le16 pan_id) +{ + ASSERT_RTNL(); + + /* TODO + * I am not sure about to check here on broadcast pan_id. + * Broadcast is a valid setting, comment from 802.15.4: + * If this value is 0xffff, the device is not associated. + * + * This could useful to simple deassociate an device. + */ + if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST)) + return -EINVAL; + + wpan_dev->pan_id = pan_id; + return 0; +} + +static int +ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + u8 min_be, u8 max_be) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS)) + return -EOPNOTSUPP; + + wpan_dev->min_be = min_be; + wpan_dev->max_be = max_be; + return 0; +} + +static int +ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + __le16 short_addr) +{ + ASSERT_RTNL(); + + /* TODO + * I am not sure about to check here on broadcast short_addr. + * Broadcast is a valid setting, comment from 802.15.4: + * A value of 0xfffe indicates that the device has + * associated but has not been allocated an address. A + * value of 0xffff indicates that the device does not + * have a short address. + * + * I think we should allow to set these settings but + * don't allow to allow socket communication with it. + */ + if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) || + short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST)) + return -EINVAL; + + wpan_dev->short_addr = short_addr; + return 0; +} + +static int +ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + u8 max_csma_backoffs) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS)) + return -EOPNOTSUPP; + + wpan_dev->csma_retries = max_csma_backoffs; + return 0; +} + +static int +ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy, + struct wpan_dev *wpan_dev, + s8 max_frame_retries) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES)) + return -EOPNOTSUPP; + + wpan_dev->frame_retries = max_frame_retries; + return 0; +} + +static int +ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, + bool mode) +{ + struct ieee802154_local *local = wpan_phy_priv(wpan_phy); + + ASSERT_RTNL(); + + if (!(local->hw.flags & IEEE802154_HW_LBT)) + return -EOPNOTSUPP; + + wpan_dev->lbt = mode; + return 0; +} + +const struct cfg802154_ops mac802154_config_ops = { + .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated, + .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated, + .add_virtual_intf = ieee802154_add_iface, + .del_virtual_intf = ieee802154_del_iface, + .set_channel = ieee802154_set_channel, + .set_pan_id = ieee802154_set_pan_id, + .set_short_addr = ieee802154_set_short_addr, + .set_backoff_exponent = ieee802154_set_backoff_exponent, + .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs, + .set_max_frame_retries = ieee802154_set_max_frame_retries, + .set_lbt_mode = ieee802154_set_lbt_mode, +}; diff --git a/net/mac802154/cfg.h b/net/mac802154/cfg.h new file mode 100644 index 000000000000..e2718f981e82 --- /dev/null +++ b/net/mac802154/cfg.h @@ -0,0 +1,9 @@ +/* mac802154 configuration hooks for cfg802154 + */ + +#ifndef __CFG_H +#define __CFG_H + +extern const struct cfg802154_ops mac802154_config_ops; + +#endif /* __CFG_H */ diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h new file mode 100644 index 000000000000..f21e864613d0 --- /dev/null +++ b/net/mac802154/driver-ops.h @@ -0,0 +1,222 @@ +#ifndef __MAC802154_DRVIER_OPS +#define __MAC802154_DRIVER_OPS + +#include <linux/types.h> +#include <linux/rtnetlink.h> + +#include <net/mac802154.h> + +#include "ieee802154_i.h" + +static inline int +drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb) +{ + return local->ops->xmit_async(&local->hw, skb); +} + +static inline int +drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb) +{ + /* don't allow other operations while sync xmit */ + ASSERT_RTNL(); + + might_sleep(); + + return local->ops->xmit_sync(&local->hw, skb); +} + +static inline int drv_start(struct ieee802154_local *local) +{ + might_sleep(); + + local->started = true; + smp_mb(); + + return local->ops->start(&local->hw); +} + +static inline void drv_stop(struct ieee802154_local *local) +{ + might_sleep(); + + local->ops->stop(&local->hw); + + /* sync away all work on the tasklet before clearing started */ + tasklet_disable(&local->tasklet); + tasklet_enable(&local->tasklet); + + barrier(); + + local->started = false; +} + +static inline int +drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel) +{ + might_sleep(); + + return local->ops->set_channel(&local->hw, page, channel); +} + +static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm) +{ + might_sleep(); + + if (!local->ops->set_txpower) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_txpower(&local->hw, dbm); +} + +static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode) +{ + might_sleep(); + + if (!local->ops->set_cca_mode) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_cca_mode(&local->hw, cca_mode); +} + +static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode) +{ + might_sleep(); + + if (!local->ops->set_lbt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_lbt(&local->hw, mode); +} + +static inline int +drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level) +{ + might_sleep(); + + if (!local->ops->set_cca_ed_level) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_cca_ed_level(&local->hw, ed_level); +} + +static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.pan_id = pan_id; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_PANID_CHANGED); +} + +static inline int +drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.ieee_addr = extended_addr; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_IEEEADDR_CHANGED); +} + +static inline int +drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.short_addr = short_addr; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_SADDR_CHANGED); +} + +static inline int +drv_set_pan_coord(struct ieee802154_local *local, bool is_coord) +{ + struct ieee802154_hw_addr_filt filt; + + might_sleep(); + + if (!local->ops->set_hw_addr_filt) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + filt.pan_coord = is_coord; + + return local->ops->set_hw_addr_filt(&local->hw, &filt, + IEEE802154_AFILT_PANC_CHANGED); +} + +static inline int +drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be, + u8 max_csma_backoffs) +{ + might_sleep(); + + if (!local->ops->set_csma_params) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_csma_params(&local->hw, min_be, max_be, + max_csma_backoffs); +} + +static inline int +drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries) +{ + might_sleep(); + + if (!local->ops->set_frame_retries) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_frame_retries(&local->hw, max_frame_retries); +} + +static inline int +drv_set_promiscuous_mode(struct ieee802154_local *local, bool on) +{ + might_sleep(); + + if (!local->ops->set_promiscuous_mode) { + WARN_ON(1); + return -EOPNOTSUPP; + } + + return local->ops->set_promiscuous_mode(&local->hw, on); +} + +#endif /* __MAC802154_DRVIER_OPS */ diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c deleted file mode 100644 index b36b2b996578..000000000000 --- a/net/mac802154/ieee802154_dev.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * Copyright (C) 2007-2012 Siemens AG - * - * Written by: - * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - * - * Based on the code from 'linux-zigbee.sourceforge.net' project. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/netdevice.h> - -#include <net/netlink.h> -#include <linux/nl802154.h> -#include <net/mac802154.h> -#include <net/ieee802154_netdev.h> -#include <net/route.h> -#include <net/wpan-phy.h> - -#include "mac802154.h" - -int mac802154_slave_open(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct mac802154_sub_if_data *subif; - struct mac802154_priv *ipriv = priv->hw; - int res = 0; - - ASSERT_RTNL(); - - if (priv->type == IEEE802154_DEV_WPAN) { - mutex_lock(&priv->hw->slaves_mtx); - list_for_each_entry(subif, &priv->hw->slaves, list) { - if (subif != priv && subif->type == priv->type && - subif->running) { - mutex_unlock(&priv->hw->slaves_mtx); - return -EBUSY; - } - } - mutex_unlock(&priv->hw->slaves_mtx); - } - - mutex_lock(&priv->hw->slaves_mtx); - priv->running = true; - mutex_unlock(&priv->hw->slaves_mtx); - - if (ipriv->open_count++ == 0) { - res = ipriv->ops->start(&ipriv->hw); - WARN_ON(res); - if (res) - goto err; - } - - if (ipriv->ops->ieee_addr) { - __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr); - - res = ipriv->ops->ieee_addr(&ipriv->hw, addr); - WARN_ON(res); - if (res) - goto err; - mac802154_dev_set_ieee_addr(dev); - } - - netif_start_queue(dev); - return 0; -err: - priv->hw->open_count--; - - return res; -} - -int mac802154_slave_close(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct mac802154_priv *ipriv = priv->hw; - - ASSERT_RTNL(); - - netif_stop_queue(dev); - - mutex_lock(&priv->hw->slaves_mtx); - priv->running = false; - mutex_unlock(&priv->hw->slaves_mtx); - - if (!--ipriv->open_count) - ipriv->ops->stop(&ipriv->hw); - - return 0; -} - -static int -mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - struct mac802154_priv *ipriv; - int err; - - ipriv = wpan_phy_priv(phy); - - priv = netdev_priv(dev); - priv->dev = dev; - priv->hw = ipriv; - - dev->needed_headroom = ipriv->hw.extra_tx_headroom; - - SET_NETDEV_DEV(dev, &ipriv->phy->dev); - - mutex_lock(&ipriv->slaves_mtx); - if (!ipriv->running) { - mutex_unlock(&ipriv->slaves_mtx); - return -ENODEV; - } - mutex_unlock(&ipriv->slaves_mtx); - - err = register_netdev(dev); - if (err < 0) - return err; - - rtnl_lock(); - mutex_lock(&ipriv->slaves_mtx); - list_add_tail_rcu(&priv->list, &ipriv->slaves); - mutex_unlock(&ipriv->slaves_mtx); - rtnl_unlock(); - - return 0; -} - -static void -mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev) -{ - struct mac802154_sub_if_data *sdata; - - ASSERT_RTNL(); - - sdata = netdev_priv(dev); - - BUG_ON(sdata->hw->phy != phy); - - mutex_lock(&sdata->hw->slaves_mtx); - list_del_rcu(&sdata->list); - mutex_unlock(&sdata->hw->slaves_mtx); - - synchronize_rcu(); - unregister_netdevice(sdata->dev); -} - -static struct net_device * -mac802154_add_iface(struct wpan_phy *phy, const char *name, int type) -{ - struct net_device *dev; - int err = -ENOMEM; - - switch (type) { - case IEEE802154_DEV_MONITOR: - dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), - name, NET_NAME_UNKNOWN, - mac802154_monitor_setup); - break; - case IEEE802154_DEV_WPAN: - dev = alloc_netdev(sizeof(struct mac802154_sub_if_data), - name, NET_NAME_UNKNOWN, - mac802154_wpan_setup); - break; - default: - dev = NULL; - err = -EINVAL; - break; - } - if (!dev) - goto err; - - err = mac802154_netdev_register(phy, dev); - if (err) - goto err_free; - - dev_hold(dev); /* we return an incremented device refcount */ - return dev; - -err_free: - free_netdev(dev); -err: - return ERR_PTR(err); -} - -static int mac802154_set_txpower(struct wpan_phy *phy, int db) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_txpower(&priv->hw, db); -} - -static int mac802154_set_lbt(struct wpan_phy *phy, bool on) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_lbt(&priv->hw, on); -} - -static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_cca_mode(&priv->hw, mode); -} - -static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_cca_ed_level(&priv->hw, level); -} - -static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be, - u8 max_be, u8 retries) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries); -} - -static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries) -{ - struct mac802154_priv *priv = wpan_phy_priv(phy); - - return priv->ops->set_frame_retries(&priv->hw, retries); -} - -struct ieee802154_dev * -ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) -{ - struct wpan_phy *phy; - struct mac802154_priv *priv; - size_t priv_size; - - if (!ops || !ops->xmit || !ops->ed || !ops->start || - !ops->stop || !ops->set_channel) { - pr_err("undefined IEEE802.15.4 device operations\n"); - return NULL; - } - - /* Ensure 32-byte alignment of our private data and hw private data. - * We use the wpan_phy priv data for both our mac802154_priv and for - * the driver's private data - * - * in memory it'll be like this: - * - * +-----------------------+ - * | struct wpan_phy | - * +-----------------------+ - * | struct mac802154_priv | - * +-----------------------+ - * | driver's private data | - * +-----------------------+ - * - * Due to ieee802154 layer isn't aware of driver and MAC structures, - * so lets allign them here. - */ - - priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len; - - phy = wpan_phy_alloc(priv_size); - if (!phy) { - pr_err("failure to allocate master IEEE802.15.4 device\n"); - return NULL; - } - - priv = wpan_phy_priv(phy); - priv->phy = phy; - priv->hw.phy = priv->phy; - priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN); - priv->ops = ops; - - INIT_LIST_HEAD(&priv->slaves); - mutex_init(&priv->slaves_mtx); - - return &priv->hw; -} -EXPORT_SYMBOL(ieee802154_alloc_device); - -void ieee802154_free_device(struct ieee802154_dev *hw) -{ - struct mac802154_priv *priv = mac802154_to_priv(hw); - - BUG_ON(!list_empty(&priv->slaves)); - - mutex_destroy(&priv->slaves_mtx); - - wpan_phy_free(priv->phy); -} -EXPORT_SYMBOL(ieee802154_free_device); - -int ieee802154_register_device(struct ieee802154_dev *dev) -{ - struct mac802154_priv *priv = mac802154_to_priv(dev); - int rc = -ENOSYS; - - if (dev->flags & IEEE802154_HW_TXPOWER) { - if (!priv->ops->set_txpower) - goto out; - - priv->phy->set_txpower = mac802154_set_txpower; - } - - if (dev->flags & IEEE802154_HW_LBT) { - if (!priv->ops->set_lbt) - goto out; - - priv->phy->set_lbt = mac802154_set_lbt; - } - - if (dev->flags & IEEE802154_HW_CCA_MODE) { - if (!priv->ops->set_cca_mode) - goto out; - - priv->phy->set_cca_mode = mac802154_set_cca_mode; - } - - if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) { - if (!priv->ops->set_cca_ed_level) - goto out; - - priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level; - } - - if (dev->flags & IEEE802154_HW_CSMA_PARAMS) { - if (!priv->ops->set_csma_params) - goto out; - - priv->phy->set_csma_params = mac802154_set_csma_params; - } - - if (dev->flags & IEEE802154_HW_FRAME_RETRIES) { - if (!priv->ops->set_frame_retries) - goto out; - - priv->phy->set_frame_retries = mac802154_set_frame_retries; - } - - priv->dev_workqueue = - create_singlethread_workqueue(wpan_phy_name(priv->phy)); - if (!priv->dev_workqueue) { - rc = -ENOMEM; - goto out; - } - - wpan_phy_set_dev(priv->phy, priv->hw.parent); - - priv->phy->add_iface = mac802154_add_iface; - priv->phy->del_iface = mac802154_del_iface; - - rc = wpan_phy_register(priv->phy); - if (rc < 0) - goto out_wq; - - rtnl_lock(); - - mutex_lock(&priv->slaves_mtx); - priv->running = MAC802154_DEVICE_RUN; - mutex_unlock(&priv->slaves_mtx); - - rtnl_unlock(); - - return 0; - -out_wq: - destroy_workqueue(priv->dev_workqueue); -out: - return rc; -} -EXPORT_SYMBOL(ieee802154_register_device); - -void ieee802154_unregister_device(struct ieee802154_dev *dev) -{ - struct mac802154_priv *priv = mac802154_to_priv(dev); - struct mac802154_sub_if_data *sdata, *next; - - flush_workqueue(priv->dev_workqueue); - destroy_workqueue(priv->dev_workqueue); - - rtnl_lock(); - - mutex_lock(&priv->slaves_mtx); - priv->running = MAC802154_DEVICE_STOPPED; - mutex_unlock(&priv->slaves_mtx); - - list_for_each_entry_safe(sdata, next, &priv->slaves, list) { - mutex_lock(&sdata->hw->slaves_mtx); - list_del(&sdata->list); - mutex_unlock(&sdata->hw->slaves_mtx); - - unregister_netdevice(sdata->dev); - } - - rtnl_unlock(); - - wpan_phy_unregister(priv->phy); -} -EXPORT_SYMBOL(ieee802154_unregister_device); - -MODULE_DESCRIPTION("IEEE 802.15.4 implementation"); -MODULE_LICENSE("GPL v2"); diff --git a/net/mac802154/mac802154.h b/net/mac802154/ieee802154_i.h index 762a6f849c6b..bebd70ffc7a3 100644 --- a/net/mac802154/mac802154.h +++ b/net/mac802154/ieee802154_i.h @@ -10,29 +10,28 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> */ -#ifndef MAC802154_H -#define MAC802154_H +#ifndef __IEEE802154_I_H +#define __IEEE802154_I_H #include <linux/mutex.h> +#include <linux/hrtimer.h> +#include <net/cfg802154.h> #include <net/mac802154.h> +#include <net/nl802154.h> #include <net/ieee802154_netdev.h> #include "llsec.h" /* mac802154 device private data */ -struct mac802154_priv { - struct ieee802154_dev hw; - struct ieee802154_ops *ops; +struct ieee802154_local { + struct ieee802154_hw hw; + const struct ieee802154_ops *ops; /* ieee802154 phy */ struct wpan_phy *phy; @@ -46,23 +45,29 @@ struct mac802154_priv { * * So atomic readers can use any of this protection methods. */ - struct list_head slaves; - struct mutex slaves_mtx; + struct list_head interfaces; + struct mutex iflist_mtx; /* This one is used for scanning and other jobs not to be interfered * with serial driver. */ - struct workqueue_struct *dev_workqueue; + struct workqueue_struct *workqueue; - /* SoftMAC device is registered and running. One can add subinterfaces. - * This flag should be modified under slaves_mtx and RTNL, so you can - * read them using any of protection methods. - */ - bool running; + struct hrtimer ifs_timer; + + bool started; + + struct tasklet_struct tasklet; + struct sk_buff_head skb_queue; +}; + +enum { + IEEE802154_RX_MSG = 1, }; -#define MAC802154_DEVICE_STOPPED 0x00 -#define MAC802154_DEVICE_RUN 0x01 +enum ieee802154_sdata_state_bits { + SDATA_STATE_RUNNING, +}; /* Slave interface definition. * @@ -70,72 +75,74 @@ struct mac802154_priv { * Each ieee802154 device/transceiver may have several slaves and able * to be associated with several networks at the same time. */ -struct mac802154_sub_if_data { +struct ieee802154_sub_if_data { struct list_head list; /* the ieee802154_priv->slaves list */ - struct mac802154_priv *hw; + struct wpan_dev wpan_dev; + + struct ieee802154_local *local; struct net_device *dev; - int type; - bool running; + unsigned long state; + char name[IFNAMSIZ]; spinlock_t mib_lock; - __le16 pan_id; - __le16 short_addr; - __le64 extended_addr; - - u8 chan; - u8 page; - - struct ieee802154_mac_params mac_params; - - /* MAC BSN field */ - u8 bsn; - /* MAC DSN field */ - u8 dsn; - /* protects sec from concurrent access by netlink. access by * encrypt/decrypt/header_create safe without additional protection. */ struct mutex sec_mtx; struct mac802154_llsec sec; + /* must be last, dynamically sized area in this! */ + struct ieee802154_vif vif; }; -#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw) - #define MAC802154_CHAN_NONE 0xff /* No channel is assigned */ -extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced; -extern struct ieee802154_mlme_ops mac802154_mlme_wpan; - -int mac802154_slave_open(struct net_device *dev); -int mac802154_slave_close(struct net_device *dev); +/* utility functions/constants */ +extern const void *const mac802154_wpan_phy_privid; /* for wpan_phy privid */ + +static inline struct ieee802154_local * +hw_to_local(struct ieee802154_hw *hw) +{ + return container_of(hw, struct ieee802154_local, hw); +} + +static inline struct ieee802154_sub_if_data * +IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev) +{ + return netdev_priv(dev); +} + +static inline struct ieee802154_sub_if_data * +IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev) +{ + return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev); +} + +static inline bool +ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata) +{ + return test_bit(SDATA_STATE_RUNNING, &sdata->state); +} -void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb); -void mac802154_monitor_setup(struct net_device *dev); - -void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb); -void mac802154_wpan_setup(struct net_device *dev); +extern struct ieee802154_mlme_ops mac802154_mlme_wpan; -netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, - u8 page, u8 chan); +netdev_tx_t +ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t +ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); +enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer); /* MIB callbacks */ void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val); __le16 mac802154_dev_get_short_addr(const struct net_device *dev); -void mac802154_dev_set_ieee_addr(struct net_device *dev); __le16 mac802154_dev_get_pan_id(const struct net_device *dev); void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val); void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan); u8 mac802154_dev_get_dsn(const struct net_device *dev); -int mac802154_set_mac_params(struct net_device *dev, - const struct ieee802154_mac_params *params); -void mac802154_get_mac_params(struct net_device *dev, - struct ieee802154_mac_params *params); - int mac802154_get_params(struct net_device *dev, struct ieee802154_llsec_params *params); int mac802154_set_params(struct net_device *dev, @@ -169,4 +176,13 @@ void mac802154_get_table(struct net_device *dev, struct ieee802154_llsec_table **t); void mac802154_unlock_table(struct net_device *dev); -#endif /* MAC802154_H */ +/* interface handling */ +int ieee802154_iface_init(void); +void ieee802154_iface_exit(void); +void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata); +struct net_device * +ieee802154_if_add(struct ieee802154_local *local, const char *name, + enum nl802154_iftype type, __le64 extended_addr); +void ieee802154_remove_interfaces(struct ieee802154_local *local); + +#endif /* __IEEE802154_I_H */ diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c new file mode 100644 index 000000000000..38dfc72d24b6 --- /dev/null +++ b/net/mac802154/iface.c @@ -0,0 +1,588 @@ +/* + * Copyright 2007-2012 Siemens AG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Written by: + * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> + * Sergey Lapin <slapin@ossfans.org> + * Maxim Gorbachyov <maxim.gorbachev@siemens.com> + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + */ + +#include <linux/netdevice.h> +#include <linux/module.h> +#include <linux/if_arp.h> +#include <linux/ieee802154.h> + +#include <net/nl802154.h> +#include <net/mac802154.h> +#include <net/ieee802154_netdev.h> +#include <net/cfg802154.h> + +#include "ieee802154_i.h" +#include "driver-ops.h" + +static int mac802154_wpan_update_llsec(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + int rc = 0; + + if (ops->llsec) { + struct ieee802154_llsec_params params; + int changed = 0; + + params.pan_id = wpan_dev->pan_id; + changed |= IEEE802154_LLSEC_PARAM_PAN_ID; + + params.hwaddr = wpan_dev->extended_addr; + changed |= IEEE802154_LLSEC_PARAM_HWADDR; + + rc = ops->llsec->set_params(dev, ¶ms, changed); + } + + return rc; +} + +static int +mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct sockaddr_ieee802154 *sa = + (struct sockaddr_ieee802154 *)&ifr->ifr_addr; + int err = -ENOIOCTLCMD; + + ASSERT_RTNL(); + + spin_lock_bh(&sdata->mib_lock); + + switch (cmd) { + case SIOCGIFADDR: + { + u16 pan_id, short_addr; + + pan_id = le16_to_cpu(wpan_dev->pan_id); + short_addr = le16_to_cpu(wpan_dev->short_addr); + if (pan_id == IEEE802154_PANID_BROADCAST || + short_addr == IEEE802154_ADDR_BROADCAST) { + err = -EADDRNOTAVAIL; + break; + } + + sa->family = AF_IEEE802154; + sa->addr.addr_type = IEEE802154_ADDR_SHORT; + sa->addr.pan_id = pan_id; + sa->addr.short_addr = short_addr; + + err = 0; + break; + } + case SIOCSIFADDR: + if (netif_running(dev)) { + spin_unlock_bh(&sdata->mib_lock); + return -EBUSY; + } + + dev_warn(&dev->dev, + "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n"); + if (sa->family != AF_IEEE802154 || + sa->addr.addr_type != IEEE802154_ADDR_SHORT || + sa->addr.pan_id == IEEE802154_PANID_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || + sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { + err = -EINVAL; + break; + } + + wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id); + wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr); + + err = mac802154_wpan_update_llsec(dev); + break; + } + + spin_unlock_bh(&sdata->mib_lock); + return err; +} + +static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct sockaddr *addr = p; + __le64 extended_addr; + + if (netif_running(dev)) + return -EBUSY; + + ieee802154_be64_to_le64(&extended_addr, addr->sa_data); + if (!ieee802154_is_valid_extended_addr(extended_addr)) + return -EINVAL; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + sdata->wpan_dev.extended_addr = extended_addr; + + return mac802154_wpan_update_llsec(dev); +} + +static int mac802154_slave_open(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_sub_if_data *subif; + struct ieee802154_local *local = sdata->local; + int res = 0; + + ASSERT_RTNL(); + + if (sdata->vif.type == NL802154_IFTYPE_NODE) { + mutex_lock(&sdata->local->iflist_mtx); + list_for_each_entry(subif, &sdata->local->interfaces, list) { + if (subif != sdata && + subif->vif.type == sdata->vif.type && + ieee802154_sdata_running(subif)) { + mutex_unlock(&sdata->local->iflist_mtx); + return -EBUSY; + } + } + mutex_unlock(&sdata->local->iflist_mtx); + } + + set_bit(SDATA_STATE_RUNNING, &sdata->state); + + if (!local->open_count) { + res = drv_start(local); + WARN_ON(res); + if (res) + goto err; + } + + local->open_count++; + netif_start_queue(dev); + return 0; +err: + /* might already be clear but that doesn't matter */ + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + + return res; +} + +static int mac802154_wpan_open(struct net_device *dev) +{ + int rc; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct wpan_phy *phy = sdata->local->phy; + + rc = mac802154_slave_open(dev); + if (rc < 0) + return rc; + + mutex_lock(&phy->pib_lock); + + if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) { + rc = drv_set_promiscuous_mode(local, + wpan_dev->promiscuous_mode); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_AFILT) { + rc = drv_set_pan_id(local, wpan_dev->pan_id); + if (rc < 0) + goto out; + + rc = drv_set_extended_addr(local, wpan_dev->extended_addr); + if (rc < 0) + goto out; + + rc = drv_set_short_addr(local, wpan_dev->short_addr); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_LBT) { + rc = drv_set_lbt_mode(local, wpan_dev->lbt); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) { + rc = drv_set_csma_params(local, wpan_dev->min_be, + wpan_dev->max_be, + wpan_dev->csma_retries); + if (rc < 0) + goto out; + } + + if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) { + rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries); + if (rc < 0) + goto out; + } + + mutex_unlock(&phy->pib_lock); + return 0; + +out: + mutex_unlock(&phy->pib_lock); + return rc; +} + +static int mac802154_slave_close(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + + ASSERT_RTNL(); + + hrtimer_cancel(&local->ifs_timer); + + netif_stop_queue(dev); + local->open_count--; + + clear_bit(SDATA_STATE_RUNNING, &sdata->state); + + if (!local->open_count) + drv_stop(local); + + return 0; +} + +static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata, + struct ieee802154_hdr *hdr, + const struct ieee802154_mac_cb *cb) +{ + struct ieee802154_llsec_params params; + u8 level; + + mac802154_llsec_get_params(&sdata->sec, ¶ms); + + if (!params.enabled && cb->secen_override && cb->secen) + return -EINVAL; + if (!params.enabled || + (cb->secen_override && !cb->secen) || + !params.out_level) + return 0; + if (cb->seclevel_override && !cb->seclevel) + return -EINVAL; + + level = cb->seclevel_override ? cb->seclevel : params.out_level; + + hdr->fc.security_enabled = 1; + hdr->sec.level = level; + hdr->sec.key_id_mode = params.out_key.mode; + if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX) + hdr->sec.short_src = params.out_key.short_source; + else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX) + hdr->sec.extended_src = params.out_key.extended_source; + hdr->sec.key_id = params.out_key.id; + + return 0; +} + +static int mac802154_header_create(struct sk_buff *skb, + struct net_device *dev, + unsigned short type, + const void *daddr, + const void *saddr, + unsigned len) +{ + struct ieee802154_hdr hdr; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + struct ieee802154_mac_cb *cb = mac_cb(skb); + int hlen; + + if (!daddr) + return -EINVAL; + + memset(&hdr.fc, 0, sizeof(hdr.fc)); + hdr.fc.type = cb->type; + hdr.fc.security_enabled = cb->secen; + hdr.fc.ack_request = cb->ackreq; + hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); + + if (mac802154_set_header_security(sdata, &hdr, cb) < 0) + return -EINVAL; + + if (!saddr) { + spin_lock_bh(&sdata->mib_lock); + + if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) || + wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || + wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { + hdr.source.mode = IEEE802154_ADDR_LONG; + hdr.source.extended_addr = wpan_dev->extended_addr; + } else { + hdr.source.mode = IEEE802154_ADDR_SHORT; + hdr.source.short_addr = wpan_dev->short_addr; + } + + hdr.source.pan_id = wpan_dev->pan_id; + + spin_unlock_bh(&sdata->mib_lock); + } else { + hdr.source = *(const struct ieee802154_addr *)saddr; + } + + hdr.dest = *(const struct ieee802154_addr *)daddr; + + hlen = ieee802154_hdr_push(skb, &hdr); + if (hlen < 0) + return -EINVAL; + + skb_reset_mac_header(skb); + skb->mac_len = hlen; + + if (len > ieee802154_max_payload(&hdr)) + return -EMSGSIZE; + + return hlen; +} + +static int +mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + struct ieee802154_hdr hdr; + struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; + + if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) { + pr_debug("malformed packet\n"); + return 0; + } + + *addr = hdr.source; + return sizeof(*addr); +} + +static struct header_ops mac802154_header_ops = { + .create = mac802154_header_create, + .parse = mac802154_header_parse, +}; + +static const struct net_device_ops mac802154_wpan_ops = { + .ndo_open = mac802154_wpan_open, + .ndo_stop = mac802154_slave_close, + .ndo_start_xmit = ieee802154_subif_start_xmit, + .ndo_do_ioctl = mac802154_wpan_ioctl, + .ndo_set_mac_address = mac802154_wpan_mac_addr, +}; + +static const struct net_device_ops mac802154_monitor_ops = { + .ndo_open = mac802154_wpan_open, + .ndo_stop = mac802154_slave_close, + .ndo_start_xmit = ieee802154_monitor_start_xmit, +}; + +static void mac802154_wpan_free(struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + mac802154_llsec_destroy(&sdata->sec); + + free_netdev(dev); +} + +static void ieee802154_if_setup(struct net_device *dev) +{ + dev->addr_len = IEEE802154_EXTENDED_ADDR_LEN; + memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN); + + dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; + dev->needed_tailroom = 2 + 16; /* FCS + MIC */ + dev->mtu = IEEE802154_MTU; + dev->tx_queue_len = 300; + dev->flags = IFF_NOARP | IFF_BROADCAST; +} + +static int +ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, + enum nl802154_iftype type) +{ + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + + /* set some type-dependent values */ + sdata->vif.type = type; + sdata->wpan_dev.iftype = type; + + get_random_bytes(&wpan_dev->bsn, 1); + get_random_bytes(&wpan_dev->dsn, 1); + + /* defaults per 802.15.4-2011 */ + wpan_dev->min_be = 3; + wpan_dev->max_be = 5; + wpan_dev->csma_retries = 4; + /* for compatibility, actual default is 3 */ + wpan_dev->frame_retries = -1; + + wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); + wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); + + switch (type) { + case NL802154_IFTYPE_NODE: + ieee802154_be64_to_le64(&wpan_dev->extended_addr, + sdata->dev->dev_addr); + + sdata->dev->header_ops = &mac802154_header_ops; + sdata->dev->destructor = mac802154_wpan_free; + sdata->dev->netdev_ops = &mac802154_wpan_ops; + sdata->dev->ml_priv = &mac802154_mlme_wpan; + wpan_dev->promiscuous_mode = false; + + spin_lock_init(&sdata->mib_lock); + mutex_init(&sdata->sec_mtx); + + mac802154_llsec_init(&sdata->sec); + break; + case NL802154_IFTYPE_MONITOR: + sdata->dev->destructor = free_netdev; + sdata->dev->netdev_ops = &mac802154_monitor_ops; + wpan_dev->promiscuous_mode = true; + break; + default: + BUG(); + } + + return 0; +} + +struct net_device * +ieee802154_if_add(struct ieee802154_local *local, const char *name, + enum nl802154_iftype type, __le64 extended_addr) +{ + struct net_device *ndev = NULL; + struct ieee802154_sub_if_data *sdata = NULL; + int ret = -ENOMEM; + + ASSERT_RTNL(); + + ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name, + NET_NAME_UNKNOWN, ieee802154_if_setup); + if (!ndev) + return ERR_PTR(-ENOMEM); + + ndev->needed_headroom = local->hw.extra_tx_headroom; + + ret = dev_alloc_name(ndev, ndev->name); + if (ret < 0) + goto err; + + ieee802154_le64_to_be64(ndev->perm_addr, + &local->hw.phy->perm_extended_addr); + switch (type) { + case NL802154_IFTYPE_NODE: + ndev->type = ARPHRD_IEEE802154; + if (ieee802154_is_valid_extended_addr(extended_addr)) + ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr); + else + memcpy(ndev->dev_addr, ndev->perm_addr, + IEEE802154_EXTENDED_ADDR_LEN); + break; + case NL802154_IFTYPE_MONITOR: + ndev->type = ARPHRD_IEEE802154_MONITOR; + break; + default: + ret = -EINVAL; + goto err; + } + + /* TODO check this */ + SET_NETDEV_DEV(ndev, &local->phy->dev); + sdata = netdev_priv(ndev); + ndev->ieee802154_ptr = &sdata->wpan_dev; + memcpy(sdata->name, ndev->name, IFNAMSIZ); + sdata->dev = ndev; + sdata->wpan_dev.wpan_phy = local->hw.phy; + sdata->local = local; + + /* setup type-dependent data */ + ret = ieee802154_setup_sdata(sdata, type); + if (ret) + goto err; + + if (ndev) { + ret = register_netdevice(ndev); + if (ret < 0) + goto err; + } + + mutex_lock(&local->iflist_mtx); + list_add_tail_rcu(&sdata->list, &local->interfaces); + mutex_unlock(&local->iflist_mtx); + + return ndev; + +err: + free_netdev(ndev); + return ERR_PTR(ret); +} + +void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata) +{ + ASSERT_RTNL(); + + mutex_lock(&sdata->local->iflist_mtx); + list_del_rcu(&sdata->list); + mutex_unlock(&sdata->local->iflist_mtx); + + synchronize_rcu(); + unregister_netdevice(sdata->dev); +} + +void ieee802154_remove_interfaces(struct ieee802154_local *local) +{ + struct ieee802154_sub_if_data *sdata, *tmp; + + mutex_lock(&local->iflist_mtx); + list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) { + list_del(&sdata->list); + + unregister_netdevice(sdata->dev); + } + mutex_unlock(&local->iflist_mtx); +} + +static int netdev_notify(struct notifier_block *nb, + unsigned long state, void *ptr) +{ + struct net_device *dev = netdev_notifier_info_to_dev(ptr); + struct ieee802154_sub_if_data *sdata; + + if (state != NETDEV_CHANGENAME) + return NOTIFY_DONE; + + if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy) + return NOTIFY_DONE; + + if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid) + return NOTIFY_DONE; + + sdata = IEEE802154_DEV_TO_SUB_IF(dev); + memcpy(sdata->name, dev->name, IFNAMSIZ); + + return NOTIFY_OK; +} + +static struct notifier_block mac802154_netdev_notifier = { + .notifier_call = netdev_notify, +}; + +int ieee802154_iface_init(void) +{ + return register_netdevice_notifier(&mac802154_netdev_notifier); +} + +void ieee802154_iface_exit(void) +{ + unregister_netdevice_notifier(&mac802154_netdev_notifier); +} diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c index 457058142098..fa0d5237c2e0 100644 --- a/net/mac802154/llsec.c +++ b/net/mac802154/llsec.c @@ -17,10 +17,10 @@ #include <linux/err.h> #include <linux/bug.h> #include <linux/completion.h> -#include <net/ieee802154.h> +#include <linux/ieee802154.h> #include <crypto/algapi.h> -#include "mac802154.h" +#include "ieee802154_i.h" #include "llsec.h" static void llsec_key_put(struct mac802154_llsec_key *key); diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c index bf809131eef7..6aacb1816889 100644 --- a/net/mac802154/mac_cmd.c +++ b/net/mac802154/mac_cmd.c @@ -12,10 +12,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Sergey Lapin <slapin@ossfans.org> * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> @@ -24,14 +20,14 @@ #include <linux/skbuff.h> #include <linux/if_arp.h> +#include <linux/ieee802154.h> -#include <net/ieee802154.h> #include <net/ieee802154_netdev.h> -#include <net/wpan-phy.h> +#include <net/cfg802154.h> #include <net/mac802154.h> -#include <net/nl802154.h> -#include "mac802154.h" +#include "ieee802154_i.h" +#include "driver-ops.h" static int mac802154_mlme_start_req(struct net_device *dev, struct ieee802154_addr *addr, @@ -43,11 +39,12 @@ static int mac802154_mlme_start_req(struct net_device *dev, struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); int rc = 0; + ASSERT_RTNL(); + BUG_ON(addr->mode != IEEE802154_ADDR_SHORT); mac802154_dev_set_pan_id(dev, addr->pan_id); mac802154_dev_set_short_addr(dev, addr->short_addr); - mac802154_dev_set_ieee_addr(dev); mac802154_dev_set_page_channel(dev, page, channel); if (ops->llsec) { @@ -69,21 +66,71 @@ static int mac802154_mlme_start_req(struct net_device *dev, rc = ops->llsec->set_params(dev, ¶ms, changed); } - /* FIXME: add validation for unused parameters to be sane - * for SoftMAC - */ - ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS); - return rc; } -static struct wpan_phy *mac802154_get_phy(const struct net_device *dev) +static int mac802154_set_mac_params(struct net_device *dev, + const struct ieee802154_mac_params *params) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + int ret; + + ASSERT_RTNL(); + + /* PHY */ + wpan_dev->wpan_phy->transmit_power = params->transmit_power; + wpan_dev->wpan_phy->cca_mode = params->cca_mode; + wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level; + + /* MAC */ + wpan_dev->min_be = params->min_be; + wpan_dev->max_be = params->max_be; + wpan_dev->csma_retries = params->csma_retries; + wpan_dev->frame_retries = params->frame_retries; + wpan_dev->lbt = params->lbt; + + if (local->hw.flags & IEEE802154_HW_TXPOWER) { + ret = drv_set_tx_power(local, params->transmit_power); + if (ret < 0) + return ret; + } + + if (local->hw.flags & IEEE802154_HW_CCA_MODE) { + ret = drv_set_cca_mode(local, params->cca_mode); + if (ret < 0) + return ret; + } - BUG_ON(dev->type != ARPHRD_IEEE802154); + if (local->hw.flags & IEEE802154_HW_CCA_ED_LEVEL) { + ret = drv_set_cca_ed_level(local, params->cca_ed_level); + if (ret < 0) + return ret; + } + + return 0; +} - return to_phy(get_device(&priv->hw->phy->dev)); +static void mac802154_get_mac_params(struct net_device *dev, + struct ieee802154_mac_params *params) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + + ASSERT_RTNL(); + + /* PHY */ + params->transmit_power = wpan_dev->wpan_phy->transmit_power; + params->cca_mode = wpan_dev->wpan_phy->cca_mode; + params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level; + + /* MAC */ + params->min_be = wpan_dev->min_be; + params->max_be = wpan_dev->max_be; + params->csma_retries = wpan_dev->csma_retries; + params->frame_retries = wpan_dev->frame_retries; + params->lbt = wpan_dev->lbt; } static struct ieee802154_llsec_ops mac802154_llsec_ops = { @@ -102,12 +149,7 @@ static struct ieee802154_llsec_ops mac802154_llsec_ops = { .unlock_table = mac802154_unlock_table, }; -struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = { - .get_phy = mac802154_get_phy, -}; - struct ieee802154_mlme_ops mac802154_mlme_wpan = { - .get_phy = mac802154_get_phy, .start_req = mac802154_mlme_start_req, .get_pan_id = mac802154_dev_get_pan_id, .get_short_addr = mac802154_dev_get_short_addr, diff --git a/net/mac802154/main.c b/net/mac802154/main.c new file mode 100644 index 000000000000..8500378c8318 --- /dev/null +++ b/net/mac802154/main.c @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2007-2012 Siemens AG + * + * Written by: + * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> + +#include <net/netlink.h> +#include <net/nl802154.h> +#include <net/mac802154.h> +#include <net/ieee802154_netdev.h> +#include <net/route.h> +#include <net/cfg802154.h> + +#include "ieee802154_i.h" +#include "cfg.h" + +static void ieee802154_tasklet_handler(unsigned long data) +{ + struct ieee802154_local *local = (struct ieee802154_local *)data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->skb_queue))) { + switch (skb->pkt_type) { + case IEEE802154_RX_MSG: + /* Clear skb->pkt_type in order to not confuse kernel + * netstack. + */ + skb->pkt_type = 0; + ieee802154_rx(&local->hw, skb); + break; + default: + WARN(1, "mac802154: Packet is of unknown type %d\n", + skb->pkt_type); + kfree_skb(skb); + break; + } + } +} + +struct ieee802154_hw * +ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops) +{ + struct wpan_phy *phy; + struct ieee802154_local *local; + size_t priv_size; + + if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed || + !ops->start || !ops->stop || !ops->set_channel) { + pr_err("undefined IEEE802.15.4 device operations\n"); + return NULL; + } + + /* Ensure 32-byte alignment of our private data and hw private data. + * We use the wpan_phy priv data for both our ieee802154_local and for + * the driver's private data + * + * in memory it'll be like this: + * + * +-------------------------+ + * | struct wpan_phy | + * +-------------------------+ + * | struct ieee802154_local | + * +-------------------------+ + * | driver's private data | + * +-------------------------+ + * + * Due to ieee802154 layer isn't aware of driver and MAC structures, + * so lets align them here. + */ + + priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len; + + phy = wpan_phy_new(&mac802154_config_ops, priv_size); + if (!phy) { + pr_err("failure to allocate master IEEE802.15.4 device\n"); + return NULL; + } + + phy->privid = mac802154_wpan_phy_privid; + + local = wpan_phy_priv(phy); + local->phy = phy; + local->hw.phy = local->phy; + local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); + local->ops = ops; + + INIT_LIST_HEAD(&local->interfaces); + mutex_init(&local->iflist_mtx); + + tasklet_init(&local->tasklet, + ieee802154_tasklet_handler, + (unsigned long)local); + + skb_queue_head_init(&local->skb_queue); + + return &local->hw; +} +EXPORT_SYMBOL(ieee802154_alloc_hw); + +void ieee802154_free_hw(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + + BUG_ON(!list_empty(&local->interfaces)); + + mutex_destroy(&local->iflist_mtx); + + wpan_phy_free(local->phy); +} +EXPORT_SYMBOL(ieee802154_free_hw); + +static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy) +{ + /* TODO warn on empty symbol_duration + * Should be done when all drivers sets this value. + */ + + wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD * + wpan_phy->symbol_duration; + wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD * + wpan_phy->symbol_duration; +} + +int ieee802154_register_hw(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + struct net_device *dev; + int rc = -ENOSYS; + + local->workqueue = + create_singlethread_workqueue(wpan_phy_name(local->phy)); + if (!local->workqueue) { + rc = -ENOMEM; + goto out; + } + + hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + local->ifs_timer.function = ieee802154_xmit_ifs_timer; + + wpan_phy_set_dev(local->phy, local->hw.parent); + + ieee802154_setup_wpan_phy_pib(local->phy); + + rc = wpan_phy_register(local->phy); + if (rc < 0) + goto out_wq; + + rtnl_lock(); + + dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE, + cpu_to_le64(0x0000000000000000ULL)); + if (IS_ERR(dev)) { + rtnl_unlock(); + rc = PTR_ERR(dev); + goto out_wq; + } + + rtnl_unlock(); + + return 0; + +out_wq: + destroy_workqueue(local->workqueue); +out: + return rc; +} +EXPORT_SYMBOL(ieee802154_register_hw); + +void ieee802154_unregister_hw(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + + tasklet_kill(&local->tasklet); + flush_workqueue(local->workqueue); + destroy_workqueue(local->workqueue); + + rtnl_lock(); + + ieee802154_remove_interfaces(local); + + rtnl_unlock(); + + wpan_phy_unregister(local->phy); +} +EXPORT_SYMBOL(ieee802154_unregister_hw); + +static int __init ieee802154_init(void) +{ + return ieee802154_iface_init(); +} + +static void __exit ieee802154_exit(void) +{ + ieee802154_iface_exit(); + + rcu_barrier(); +} + +subsys_initcall(ieee802154_init); +module_exit(ieee802154_exit); + +MODULE_DESCRIPTION("IEEE 802.15.4 subsystem"); +MODULE_LICENSE("GPL v2"); diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c index 868a040fd422..3596b29ead6b 100644 --- a/net/mac802154/mib.c +++ b/net/mac802154/mib.c @@ -10,10 +10,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Sergey Lapin <slapin@ossfans.org> @@ -25,193 +21,86 @@ #include <net/mac802154.h> #include <net/ieee802154_netdev.h> -#include <net/wpan-phy.h> - -#include "mac802154.h" - -struct phy_chan_notify_work { - struct work_struct work; - struct net_device *dev; -}; - -struct hw_addr_filt_notify_work { - struct work_struct work; - struct net_device *dev; - unsigned long changed; -}; - -static struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); +#include <net/cfg802154.h> - BUG_ON(dev->type != ARPHRD_IEEE802154); - - return priv->hw; -} - -static void hw_addr_notify(struct work_struct *work) -{ - struct hw_addr_filt_notify_work *nw = container_of(work, - struct hw_addr_filt_notify_work, work); - struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); - int res; - - res = hw->ops->set_hw_addr_filt(&hw->hw, - &hw->hw.hw_filt, - nw->changed); - if (res) - pr_debug("failed changed mask %lx\n", nw->changed); - - kfree(nw); -} - -static void set_hw_addr_filt(struct net_device *dev, unsigned long changed) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct hw_addr_filt_notify_work *work; - - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) - return; - - INIT_WORK(&work->work, hw_addr_notify); - work->dev = dev; - work->changed = changed; - queue_work(priv->hw->dev_workqueue, &work->work); -} +#include "ieee802154_i.h" +#include "driver-ops.h" void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - priv->short_addr = val; - spin_unlock_bh(&priv->mib_lock); - - if ((priv->hw->ops->set_hw_addr_filt) && - (priv->hw->hw.hw_filt.short_addr != priv->short_addr)) { - priv->hw->hw.hw_filt.short_addr = priv->short_addr; - set_hw_addr_filt(dev, IEEE802515_AFILT_SADDR_CHANGED); - } + spin_lock_bh(&sdata->mib_lock); + sdata->wpan_dev.short_addr = val; + spin_unlock_bh(&sdata->mib_lock); } __le16 mac802154_dev_get_short_addr(const struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); __le16 ret; BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - ret = priv->short_addr; - spin_unlock_bh(&priv->mib_lock); + spin_lock_bh(&sdata->mib_lock); + ret = sdata->wpan_dev.short_addr; + spin_unlock_bh(&sdata->mib_lock); return ret; } -void mac802154_dev_set_ieee_addr(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct mac802154_priv *mac = priv->hw; - - priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr); - - if (mac->ops->set_hw_addr_filt && - mac->hw.hw_filt.ieee_addr != priv->extended_addr) { - mac->hw.hw_filt.ieee_addr = priv->extended_addr; - set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED); - } -} - __le16 mac802154_dev_get_pan_id(const struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); __le16 ret; BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - ret = priv->pan_id; - spin_unlock_bh(&priv->mib_lock); + spin_lock_bh(&sdata->mib_lock); + ret = sdata->wpan_dev.pan_id; + spin_unlock_bh(&sdata->mib_lock); return ret; } void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - priv->pan_id = val; - spin_unlock_bh(&priv->mib_lock); - - if ((priv->hw->ops->set_hw_addr_filt) && - (priv->hw->hw.hw_filt.pan_id != priv->pan_id)) { - priv->hw->hw.hw_filt.pan_id = priv->pan_id; - set_hw_addr_filt(dev, IEEE802515_AFILT_PANID_CHANGED); - } + spin_lock_bh(&sdata->mib_lock); + sdata->wpan_dev.pan_id = val; + spin_unlock_bh(&sdata->mib_lock); } u8 mac802154_dev_get_dsn(const struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - return priv->dsn++; -} - -static void phy_chan_notify(struct work_struct *work) -{ - struct phy_chan_notify_work *nw = container_of(work, - struct phy_chan_notify_work, work); - struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev); - struct mac802154_sub_if_data *priv = netdev_priv(nw->dev); - int res; - - mutex_lock(&priv->hw->phy->pib_lock); - res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan); - if (res) { - pr_debug("set_channel failed\n"); - } else { - priv->hw->phy->current_channel = priv->chan; - priv->hw->phy->current_page = priv->page; - } - mutex_unlock(&priv->hw->phy->pib_lock); - - kfree(nw); + return sdata->wpan_dev.dsn++; } void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct phy_chan_notify_work *work; + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + struct ieee802154_local *local = sdata->local; + int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - spin_lock_bh(&priv->mib_lock); - priv->page = page; - priv->chan = chan; - spin_unlock_bh(&priv->mib_lock); - - mutex_lock(&priv->hw->phy->pib_lock); - if (priv->hw->phy->current_channel != priv->chan || - priv->hw->phy->current_page != priv->page) { - mutex_unlock(&priv->hw->phy->pib_lock); - - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) - return; - - INIT_WORK(&work->work, phy_chan_notify); - work->dev = dev; - queue_work(priv->hw->dev_workqueue, &work->work); + res = drv_set_channel(local, page, chan); + if (res) { + pr_debug("set_channel failed\n"); } else { - mutex_unlock(&priv->hw->phy->pib_lock); + mutex_lock(&local->phy->pib_lock); + local->phy->current_channel = chan; + local->phy->current_page = page; + mutex_unlock(&local->phy->pib_lock); } } @@ -219,14 +108,14 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan) int mac802154_get_params(struct net_device *dev, struct ieee802154_llsec_params *params) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_get_params(&priv->sec, params); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_get_params(&sdata->sec, params); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -235,14 +124,14 @@ int mac802154_set_params(struct net_device *dev, const struct ieee802154_llsec_params *params, int changed) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_set_params(&priv->sec, params, changed); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_set_params(&sdata->sec, params, changed); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -252,14 +141,14 @@ int mac802154_add_key(struct net_device *dev, const struct ieee802154_llsec_key_id *id, const struct ieee802154_llsec_key *key) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_key_add(&priv->sec, id, key); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_key_add(&sdata->sec, id, key); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -267,14 +156,14 @@ int mac802154_add_key(struct net_device *dev, int mac802154_del_key(struct net_device *dev, const struct ieee802154_llsec_key_id *id) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_key_del(&priv->sec, id); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_key_del(&sdata->sec, id); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -283,28 +172,28 @@ int mac802154_del_key(struct net_device *dev, int mac802154_add_dev(struct net_device *dev, const struct ieee802154_llsec_device *llsec_dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_dev_add(&priv->sec, llsec_dev); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_dev_add(&sdata->sec, llsec_dev); + mutex_unlock(&sdata->sec_mtx); return res; } int mac802154_del_dev(struct net_device *dev, __le64 dev_addr) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_dev_del(&priv->sec, dev_addr); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_dev_del(&sdata->sec, dev_addr); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -314,14 +203,14 @@ int mac802154_add_devkey(struct net_device *dev, __le64 device_addr, const struct ieee802154_llsec_device_key *key) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_devkey_add(&sdata->sec, device_addr, key); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -330,14 +219,14 @@ int mac802154_del_devkey(struct net_device *dev, __le64 device_addr, const struct ieee802154_llsec_device_key *key) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_devkey_del(&sdata->sec, device_addr, key); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -346,14 +235,14 @@ int mac802154_del_devkey(struct net_device *dev, int mac802154_add_seclevel(struct net_device *dev, const struct ieee802154_llsec_seclevel *sl) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_seclevel_add(&priv->sec, sl); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_seclevel_add(&sdata->sec, sl); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -361,14 +250,14 @@ int mac802154_add_seclevel(struct net_device *dev, int mac802154_del_seclevel(struct net_device *dev, const struct ieee802154_llsec_seclevel *sl) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); int res; BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); - res = mac802154_llsec_seclevel_del(&priv->sec, sl); - mutex_unlock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); + res = mac802154_llsec_seclevel_del(&sdata->sec, sl); + mutex_unlock(&sdata->sec_mtx); return res; } @@ -376,28 +265,28 @@ int mac802154_del_seclevel(struct net_device *dev, void mac802154_lock_table(struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_lock(&priv->sec_mtx); + mutex_lock(&sdata->sec_mtx); } void mac802154_get_table(struct net_device *dev, struct ieee802154_llsec_table **t) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - *t = &priv->sec.table; + *t = &sdata->sec.table; } void mac802154_unlock_table(struct net_device *dev) { - struct mac802154_sub_if_data *priv = netdev_priv(dev); + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); BUG_ON(dev->type != ARPHRD_IEEE802154); - mutex_unlock(&priv->sec_mtx); + mutex_unlock(&sdata->sec_mtx); } diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c deleted file mode 100644 index a68230e2b25f..000000000000 --- a/net/mac802154/monitor.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007, 2008, 2009 Siemens AG - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Written by: - * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> - * Sergey Lapin <slapin@ossfans.org> - * Maxim Gorbachyov <maxim.gorbachev@siemens.com> - * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - */ - -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> -#include <linux/crc-ccitt.h> - -#include <net/ieee802154.h> -#include <net/mac802154.h> -#include <net/netlink.h> -#include <net/wpan-phy.h> -#include <linux/nl802154.h> - -#include "mac802154.h" - -static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - u8 chan, page; - - priv = netdev_priv(dev); - - /* FIXME: locking */ - chan = priv->hw->phy->current_channel; - page = priv->hw->phy->current_page; - - if (chan == MAC802154_CHAN_NONE) /* not initialized */ - return NETDEV_TX_OK; - - if (WARN_ON(page >= WPAN_NUM_PAGES) || - WARN_ON(chan >= WPAN_NUM_CHANNELS)) - return NETDEV_TX_OK; - - skb->skb_iif = dev->ifindex; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - return mac802154_tx(priv->hw, skb, page, chan); -} - - -void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb) -{ - struct sk_buff *skb2; - struct mac802154_sub_if_data *sdata; - u16 crc = crc_ccitt(0, skb->data, skb->len); - u8 *data; - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &priv->slaves, list) { - if (sdata->type != IEEE802154_DEV_MONITOR || - !netif_running(sdata->dev)) - continue; - - skb2 = skb_clone(skb, GFP_ATOMIC); - skb2->dev = sdata->dev; - skb2->pkt_type = PACKET_HOST; - data = skb_put(skb2, 2); - data[0] = crc & 0xff; - data[1] = crc >> 8; - - netif_rx_ni(skb2); - } - rcu_read_unlock(); -} - -static const struct net_device_ops mac802154_monitor_ops = { - .ndo_open = mac802154_slave_open, - .ndo_stop = mac802154_slave_close, - .ndo_start_xmit = mac802154_monitor_xmit, -}; - -void mac802154_monitor_setup(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - - dev->addr_len = 0; - dev->hard_header_len = 0; - dev->needed_tailroom = 2; /* room for FCS */ - dev->mtu = IEEE802154_MTU; - dev->tx_queue_len = 10; - dev->type = ARPHRD_IEEE802154_MONITOR; - dev->flags = IFF_NOARP | IFF_BROADCAST; - dev->watchdog_timeo = 0; - - dev->destructor = free_netdev; - dev->netdev_ops = &mac802154_monitor_ops; - dev->ml_priv = &mac802154_mlme_reduced; - - priv = netdev_priv(dev); - priv->type = IEEE802154_DEV_MONITOR; - - priv->chan = MAC802154_CHAN_NONE; /* not initialized */ - priv->page = 0; -} diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c index a14cf9ede171..041dbd5958d4 100644 --- a/net/mac802154/rx.c +++ b/net/mac802154/rx.c @@ -10,10 +10,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Pavel Smolenskiy <pavel.smolenskiy@gmail.com> * Maxim Gorbachyov <maxim.gorbachev@siemens.com> @@ -23,92 +19,285 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/workqueue.h> #include <linux/netdevice.h> #include <linux/crc-ccitt.h> +#include <asm/unaligned.h> #include <net/mac802154.h> #include <net/ieee802154_netdev.h> +#include <net/nl802154.h> -#include "mac802154.h" +#include "ieee802154_i.h" -/* The IEEE 802.15.4 standard defines 4 MAC packet types: - * - beacon frame - * - MAC command frame - * - acknowledgement frame - * - data frame - * - * and only the data frame should be pushed to the upper layers, other types - * are just internal MAC layer management information. So only data packets - * are going to be sent to the networking queue, all other will be processed - * right here by using the device workqueue. - */ -struct rx_work { - struct sk_buff *skb; - struct work_struct work; - struct ieee802154_dev *dev; - u8 lqi; -}; +static int ieee802154_deliver_skb(struct sk_buff *skb) +{ + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->protocol = htons(ETH_P_IEEE802154); -static void -mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi) + return netif_receive_skb(skb); +} + +static int +ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata, + struct sk_buff *skb, const struct ieee802154_hdr *hdr) { - struct mac802154_priv *priv = mac802154_to_priv(hw); + struct wpan_dev *wpan_dev = &sdata->wpan_dev; + __le16 span, sshort; + int rc; - mac_cb(skb)->lqi = lqi; - skb->protocol = htons(ETH_P_IEEE802154); - skb_reset_mac_header(skb); + pr_debug("getting packet via slave interface %s\n", sdata->dev->name); - if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { - u16 crc; + spin_lock_bh(&sdata->mib_lock); - if (skb->len < 2) { - pr_debug("got invalid frame\n"); - goto fail; - } - crc = crc_ccitt(0, skb->data, skb->len); - if (crc) { - pr_debug("CRC mismatch\n"); - goto fail; - } - skb_trim(skb, skb->len - 2); /* CRC */ + span = wpan_dev->pan_id; + sshort = wpan_dev->short_addr; + + switch (mac_cb(skb)->dest.mode) { + case IEEE802154_ADDR_NONE: + if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE) + /* FIXME: check if we are PAN coordinator */ + skb->pkt_type = PACKET_OTHERHOST; + else + /* ACK comes with both addresses empty */ + skb->pkt_type = PACKET_HOST; + break; + case IEEE802154_ADDR_LONG: + if (mac_cb(skb)->dest.pan_id != span && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) + skb->pkt_type = PACKET_OTHERHOST; + else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr) + skb->pkt_type = PACKET_HOST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + case IEEE802154_ADDR_SHORT: + if (mac_cb(skb)->dest.pan_id != span && + mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) + skb->pkt_type = PACKET_OTHERHOST; + else if (mac_cb(skb)->dest.short_addr == sshort) + skb->pkt_type = PACKET_HOST; + else if (mac_cb(skb)->dest.short_addr == + cpu_to_le16(IEEE802154_ADDR_BROADCAST)) + skb->pkt_type = PACKET_BROADCAST; + else + skb->pkt_type = PACKET_OTHERHOST; + break; + default: + spin_unlock_bh(&sdata->mib_lock); + pr_debug("invalid dest mode\n"); + kfree_skb(skb); + return NET_RX_DROP; } - mac802154_monitors_rx(priv, skb); - mac802154_wpans_rx(priv, skb); + spin_unlock_bh(&sdata->mib_lock); - return; + skb->dev = sdata->dev; + + rc = mac802154_llsec_decrypt(&sdata->sec, skb); + if (rc) { + pr_debug("decryption failed: %i\n", rc); + goto fail; + } + + sdata->dev->stats.rx_packets++; + sdata->dev->stats.rx_bytes += skb->len; + + switch (mac_cb(skb)->type) { + case IEEE802154_FC_TYPE_DATA: + return ieee802154_deliver_skb(skb); + default: + pr_warn("ieee802154: bad frame received (type = %d)\n", + mac_cb(skb)->type); + goto fail; + } fail: kfree_skb(skb); + return NET_RX_DROP; } -static void mac802154_rx_worker(struct work_struct *work) +static void +ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr) { - struct rx_work *rw = container_of(work, struct rx_work, work); + if (addr->mode == IEEE802154_ADDR_NONE) + pr_debug("%s not present\n", name); + + pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id)); + if (addr->mode == IEEE802154_ADDR_SHORT) { + pr_debug("%s is short: %04x\n", name, + le16_to_cpu(addr->short_addr)); + } else { + u64 hw = swab64((__force u64)addr->extended_addr); - mac802154_subif_rx(rw->dev, rw->skb, rw->lqi); - kfree(rw); + pr_debug("%s is hardware: %8phC\n", name, &hw); + } } -void -ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi) +static int +ieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr) { - struct mac802154_priv *priv = mac802154_to_priv(dev); - struct rx_work *work; + int hlen; + struct ieee802154_mac_cb *cb = mac_cb_init(skb); - if (!skb) - return; + skb_reset_mac_header(skb); + + hlen = ieee802154_hdr_pull(skb, hdr); + if (hlen < 0) + return -EINVAL; + + skb->mac_len = hlen; + + pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc), + hdr->seq); + + cb->type = hdr->fc.type; + cb->ackreq = hdr->fc.ack_request; + cb->secen = hdr->fc.security_enabled; + + ieee802154_print_addr("destination", &hdr->dest); + ieee802154_print_addr("source", &hdr->source); + + cb->source = hdr->source; + cb->dest = hdr->dest; + + if (hdr->fc.security_enabled) { + u64 key; + + pr_debug("seclevel %i\n", hdr->sec.level); + + switch (hdr->sec.key_id_mode) { + case IEEE802154_SCF_KEY_IMPLICIT: + pr_debug("implicit key\n"); + break; + + case IEEE802154_SCF_KEY_INDEX: + pr_debug("key %02x\n", hdr->sec.key_id); + break; + + case IEEE802154_SCF_KEY_SHORT_INDEX: + pr_debug("key %04x:%04x %02x\n", + le32_to_cpu(hdr->sec.short_src) >> 16, + le32_to_cpu(hdr->sec.short_src) & 0xffff, + hdr->sec.key_id); + break; + + case IEEE802154_SCF_KEY_HW_INDEX: + key = swab64((__force u64)hdr->sec.extended_src); + pr_debug("key source %8phC %02x\n", &key, + hdr->sec.key_id); + break; + } + } + + return 0; +} + +static void +__ieee802154_rx_handle_packet(struct ieee802154_local *local, + struct sk_buff *skb) +{ + int ret; + struct ieee802154_sub_if_data *sdata; + struct ieee802154_hdr hdr; - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) + ret = ieee802154_parse_frame_start(skb, &hdr); + if (ret) { + pr_debug("got invalid frame\n"); + kfree_skb(skb); return; + } + + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL802154_IFTYPE_NODE || + !netif_running(sdata->dev)) + continue; + + ieee802154_subif_frame(sdata, skb, &hdr); + skb = NULL; + break; + } + + if (skb) + kfree_skb(skb); +} + +static void +ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb) +{ + struct sk_buff *skb2; + struct ieee802154_sub_if_data *sdata; + + skb_reset_mac_header(skb); + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_IEEE802154); + + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (sdata->vif.type != NL802154_IFTYPE_MONITOR) + continue; + + if (!ieee802154_sdata_running(sdata)) + continue; + + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) { + skb2->dev = sdata->dev; + ieee802154_deliver_skb(skb2); + + sdata->dev->stats.rx_packets++; + sdata->dev->stats.rx_bytes += skb->len; + } + } +} + +void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb) +{ + struct ieee802154_local *local = hw_to_local(hw); + u16 crc; - INIT_WORK(&work->work, mac802154_rx_worker); - work->skb = skb; - work->dev = dev; - work->lqi = lqi; + WARN_ON_ONCE(softirq_count() == 0); - queue_work(priv->dev_workqueue, &work->work); + /* TODO: When a transceiver omits the checksum here, we + * add an own calculated one. This is currently an ugly + * solution because the monitor needs a crc here. + */ + if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) { + crc = crc_ccitt(0, skb->data, skb->len); + put_unaligned_le16(crc, skb_put(skb, 2)); + } + + rcu_read_lock(); + + ieee802154_monitors_rx(local, skb); + + /* Check if transceiver doesn't validate the checksum. + * If not we validate the checksum here. + */ + if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) { + crc = crc_ccitt(0, skb->data, skb->len); + if (crc) { + rcu_read_unlock(); + kfree_skb(skb); + return; + } + } + /* remove crc */ + skb_trim(skb, skb->len - 2); + + __ieee802154_rx_handle_packet(local, skb); + + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee802154_rx); + +void +ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi) +{ + struct ieee802154_local *local = hw_to_local(hw); + + mac_cb(skb)->lqi = lqi; + skb->pkt_type = IEEE802154_RX_MSG; + skb_queue_tail(&local->skb_queue, skb); + tasklet_schedule(&local->tasklet); } EXPORT_SYMBOL(ieee802154_rx_irqsafe); diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index fdf4c0e67259..c62e95695c78 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -10,10 +10,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Written by: * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> * Sergey Lapin <slapin@ossfans.org> @@ -24,106 +20,98 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/crc-ccitt.h> +#include <asm/unaligned.h> +#include <net/rtnetlink.h> #include <net/ieee802154_netdev.h> #include <net/mac802154.h> -#include <net/wpan-phy.h> +#include <net/cfg802154.h> -#include "mac802154.h" +#include "ieee802154_i.h" +#include "driver-ops.h" /* IEEE 802.15.4 transceivers can sleep during the xmit session, so process * packets through the workqueue. */ -struct xmit_work { +struct ieee802154_xmit_cb { struct sk_buff *skb; struct work_struct work; - struct mac802154_priv *priv; - u8 chan; - u8 page; + struct ieee802154_local *local; }; -static void mac802154_xmit_worker(struct work_struct *work) +static struct ieee802154_xmit_cb ieee802154_xmit_cb; + +static void ieee802154_xmit_worker(struct work_struct *work) { - struct xmit_work *xw = container_of(work, struct xmit_work, work); - struct mac802154_sub_if_data *sdata; + struct ieee802154_xmit_cb *cb = + container_of(work, struct ieee802154_xmit_cb, work); + struct ieee802154_local *local = cb->local; + struct sk_buff *skb = cb->skb; + struct net_device *dev = skb->dev; int res; - mutex_lock(&xw->priv->phy->pib_lock); - if (xw->priv->phy->current_channel != xw->chan || - xw->priv->phy->current_page != xw->page) { - res = xw->priv->ops->set_channel(&xw->priv->hw, - xw->page, - xw->chan); - if (res) { - pr_debug("set_channel failed\n"); - goto out; - } + rtnl_lock(); - xw->priv->phy->current_channel = xw->chan; - xw->priv->phy->current_page = xw->page; - } + /* check if ifdown occurred while schedule */ + if (!netif_running(dev)) + goto err_tx; - res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb); + res = drv_xmit_sync(local, skb); if (res) - pr_debug("transmission failed\n"); + goto err_tx; -out: - mutex_unlock(&xw->priv->phy->pib_lock); + ieee802154_xmit_complete(&local->hw, skb, false); - /* Restart the netif queue on each sub_if_data object. */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &xw->priv->slaves, list) - netif_wake_queue(sdata->dev); - rcu_read_unlock(); + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; - dev_kfree_skb(xw->skb); + rtnl_unlock(); - kfree(xw); + return; + +err_tx: + /* Restart the netif queue on each sub_if_data object. */ + ieee802154_wake_queue(&local->hw); + rtnl_unlock(); + kfree_skb(skb); + netdev_dbg(dev, "transmission failed\n"); } -netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, - u8 page, u8 chan) +static netdev_tx_t +ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb) { - struct xmit_work *work; - struct mac802154_sub_if_data *sdata; - - if (!(priv->phy->channels_supported[page] & (1 << chan))) { - WARN_ON(1); - goto err_tx; - } - - mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb); + struct net_device *dev = skb->dev; + int ret; - if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) { + if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) { u16 crc = crc_ccitt(0, skb->data, skb->len); - u8 *data = skb_put(skb, 2); - data[0] = crc & 0xff; - data[1] = crc >> 8; + put_unaligned_le16(crc, skb_put(skb, 2)); } - if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) + if (skb_cow_head(skb, local->hw.extra_tx_headroom)) goto err_tx; - work = kzalloc(sizeof(*work), GFP_ATOMIC); - if (!work) { - kfree_skb(skb); - return NETDEV_TX_BUSY; - } - /* Stop the netif queue on each sub_if_data object. */ - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &priv->slaves, list) - netif_stop_queue(sdata->dev); - rcu_read_unlock(); + ieee802154_stop_queue(&local->hw); + + /* async is priority, otherwise sync is fallback */ + if (local->ops->xmit_async) { + ret = drv_xmit_async(local, skb); + if (ret) { + ieee802154_wake_queue(&local->hw); + goto err_tx; + } - INIT_WORK(&work->work, mac802154_xmit_worker); - work->skb = skb; - work->priv = priv; - work->page = page; - work->chan = chan; + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; + } else { + INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker); + ieee802154_xmit_cb.skb = skb; + ieee802154_xmit_cb.local = local; - queue_work(priv->dev_workqueue, &work->work); + queue_work(local->workqueue, &ieee802154_xmit_cb.work); + } return NETDEV_TX_OK; @@ -131,3 +119,31 @@ err_tx: kfree_skb(skb); return NETDEV_TX_OK; } + +netdev_tx_t +ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + + skb->skb_iif = dev->ifindex; + + return ieee802154_tx(sdata->local, skb); +} + +netdev_tx_t +ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev); + int rc; + + rc = mac802154_llsec_encrypt(&sdata->sec, skb); + if (rc) { + netdev_warn(dev, "encryption failed: %i\n", rc); + kfree_skb(skb); + return NETDEV_TX_OK; + } + + skb->skb_iif = dev->ifindex; + + return ieee802154_tx(sdata->local, skb); +} diff --git a/net/mac802154/util.c b/net/mac802154/util.c new file mode 100644 index 000000000000..5fc979027919 --- /dev/null +++ b/net/mac802154/util.c @@ -0,0 +1,84 @@ +/* This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Authors: + * Alexander Aring <aar@pengutronix.de> + * + * Based on: net/mac80211/util.c + */ + +#include "ieee802154_i.h" + +/* privid for wpan_phys to determine whether they belong to us or not */ +const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid; + +void ieee802154_wake_queue(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + struct ieee802154_sub_if_data *sdata; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!sdata->dev) + continue; + + netif_wake_queue(sdata->dev); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee802154_wake_queue); + +void ieee802154_stop_queue(struct ieee802154_hw *hw) +{ + struct ieee802154_local *local = hw_to_local(hw); + struct ieee802154_sub_if_data *sdata; + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!sdata->dev) + continue; + + netif_stop_queue(sdata->dev); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL(ieee802154_stop_queue); + +enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer) +{ + struct ieee802154_local *local = + container_of(timer, struct ieee802154_local, ifs_timer); + + ieee802154_wake_queue(&local->hw); + + return HRTIMER_NORESTART; +} + +void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb, + bool ifs_handling) +{ + if (ifs_handling) { + struct ieee802154_local *local = hw_to_local(hw); + + if (skb->len > 18) + hrtimer_start(&local->ifs_timer, + ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC), + HRTIMER_MODE_REL); + else + hrtimer_start(&local->ifs_timer, + ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC), + HRTIMER_MODE_REL); + + consume_skb(skb); + } else { + ieee802154_wake_queue(hw); + consume_skb(skb); + } +} +EXPORT_SYMBOL(ieee802154_xmit_complete); diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c deleted file mode 100644 index 4ab86a57dca5..000000000000 --- a/net/mac802154/wpan.c +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright 2007-2012 Siemens AG - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Written by: - * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> - * Sergey Lapin <slapin@ossfans.org> - * Maxim Gorbachyov <maxim.gorbachev@siemens.com> - * Alexander Smirnov <alex.bluesman.smirnov@gmail.com> - */ - -#include <linux/netdevice.h> -#include <linux/module.h> -#include <linux/if_arp.h> - -#include <net/rtnetlink.h> -#include <linux/nl802154.h> -#include <net/af_ieee802154.h> -#include <net/mac802154.h> -#include <net/ieee802154_netdev.h> -#include <net/ieee802154.h> -#include <net/wpan-phy.h> - -#include "mac802154.h" - -static int mac802154_wpan_update_llsec(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev); - int rc = 0; - - if (ops->llsec) { - struct ieee802154_llsec_params params; - int changed = 0; - - params.pan_id = priv->pan_id; - changed |= IEEE802154_LLSEC_PARAM_PAN_ID; - - params.hwaddr = priv->extended_addr; - changed |= IEEE802154_LLSEC_PARAM_HWADDR; - - rc = ops->llsec->set_params(dev, ¶ms, changed); - } - - return rc; -} - -static int -mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct sockaddr_ieee802154 *sa = - (struct sockaddr_ieee802154 *)&ifr->ifr_addr; - int err = -ENOIOCTLCMD; - - spin_lock_bh(&priv->mib_lock); - - switch (cmd) { - case SIOCGIFADDR: - { - u16 pan_id, short_addr; - - pan_id = le16_to_cpu(priv->pan_id); - short_addr = le16_to_cpu(priv->short_addr); - if (pan_id == IEEE802154_PANID_BROADCAST || - short_addr == IEEE802154_ADDR_BROADCAST) { - err = -EADDRNOTAVAIL; - break; - } - - sa->family = AF_IEEE802154; - sa->addr.addr_type = IEEE802154_ADDR_SHORT; - sa->addr.pan_id = pan_id; - sa->addr.short_addr = short_addr; - - err = 0; - break; - } - case SIOCSIFADDR: - dev_warn(&dev->dev, - "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n"); - if (sa->family != AF_IEEE802154 || - sa->addr.addr_type != IEEE802154_ADDR_SHORT || - sa->addr.pan_id == IEEE802154_PANID_BROADCAST || - sa->addr.short_addr == IEEE802154_ADDR_BROADCAST || - sa->addr.short_addr == IEEE802154_ADDR_UNDEF) { - err = -EINVAL; - break; - } - - priv->pan_id = cpu_to_le16(sa->addr.pan_id); - priv->short_addr = cpu_to_le16(sa->addr.short_addr); - - err = mac802154_wpan_update_llsec(dev); - break; - } - - spin_unlock_bh(&priv->mib_lock); - return err; -} - -static int mac802154_wpan_mac_addr(struct net_device *dev, void *p) -{ - struct sockaddr *addr = p; - - if (netif_running(dev)) - return -EBUSY; - - /* FIXME: validate addr */ - memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); - mac802154_dev_set_ieee_addr(dev); - return mac802154_wpan_update_llsec(dev); -} - -int mac802154_set_mac_params(struct net_device *dev, - const struct ieee802154_mac_params *params) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - mutex_lock(&priv->hw->slaves_mtx); - priv->mac_params = *params; - mutex_unlock(&priv->hw->slaves_mtx); - - return 0; -} - -void mac802154_get_mac_params(struct net_device *dev, - struct ieee802154_mac_params *params) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - mutex_lock(&priv->hw->slaves_mtx); - *params = priv->mac_params; - mutex_unlock(&priv->hw->slaves_mtx); -} - -static int mac802154_wpan_open(struct net_device *dev) -{ - int rc; - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct wpan_phy *phy = priv->hw->phy; - - rc = mac802154_slave_open(dev); - if (rc < 0) - return rc; - - mutex_lock(&phy->pib_lock); - - if (phy->set_txpower) { - rc = phy->set_txpower(phy, priv->mac_params.transmit_power); - if (rc < 0) - goto out; - } - - if (phy->set_lbt) { - rc = phy->set_lbt(phy, priv->mac_params.lbt); - if (rc < 0) - goto out; - } - - if (phy->set_cca_mode) { - rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode); - if (rc < 0) - goto out; - } - - if (phy->set_cca_ed_level) { - rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level); - if (rc < 0) - goto out; - } - - if (phy->set_csma_params) { - rc = phy->set_csma_params(phy, priv->mac_params.min_be, - priv->mac_params.max_be, - priv->mac_params.csma_retries); - if (rc < 0) - goto out; - } - - if (phy->set_frame_retries) { - rc = phy->set_frame_retries(phy, - priv->mac_params.frame_retries); - if (rc < 0) - goto out; - } - - mutex_unlock(&phy->pib_lock); - return 0; - -out: - mutex_unlock(&phy->pib_lock); - return rc; -} - -static int mac802154_set_header_security(struct mac802154_sub_if_data *priv, - struct ieee802154_hdr *hdr, - const struct ieee802154_mac_cb *cb) -{ - struct ieee802154_llsec_params params; - u8 level; - - mac802154_llsec_get_params(&priv->sec, ¶ms); - - if (!params.enabled && cb->secen_override && cb->secen) - return -EINVAL; - if (!params.enabled || - (cb->secen_override && !cb->secen) || - !params.out_level) - return 0; - if (cb->seclevel_override && !cb->seclevel) - return -EINVAL; - - level = cb->seclevel_override ? cb->seclevel : params.out_level; - - hdr->fc.security_enabled = 1; - hdr->sec.level = level; - hdr->sec.key_id_mode = params.out_key.mode; - if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX) - hdr->sec.short_src = params.out_key.short_source; - else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX) - hdr->sec.extended_src = params.out_key.extended_source; - hdr->sec.key_id = params.out_key.id; - - return 0; -} - -static int mac802154_header_create(struct sk_buff *skb, - struct net_device *dev, - unsigned short type, - const void *daddr, - const void *saddr, - unsigned len) -{ - struct ieee802154_hdr hdr; - struct mac802154_sub_if_data *priv = netdev_priv(dev); - struct ieee802154_mac_cb *cb = mac_cb(skb); - int hlen; - - if (!daddr) - return -EINVAL; - - memset(&hdr.fc, 0, sizeof(hdr.fc)); - hdr.fc.type = cb->type; - hdr.fc.security_enabled = cb->secen; - hdr.fc.ack_request = cb->ackreq; - hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev); - - if (mac802154_set_header_security(priv, &hdr, cb) < 0) - return -EINVAL; - - if (!saddr) { - spin_lock_bh(&priv->mib_lock); - - if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) || - priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) || - priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { - hdr.source.mode = IEEE802154_ADDR_LONG; - hdr.source.extended_addr = priv->extended_addr; - } else { - hdr.source.mode = IEEE802154_ADDR_SHORT; - hdr.source.short_addr = priv->short_addr; - } - - hdr.source.pan_id = priv->pan_id; - - spin_unlock_bh(&priv->mib_lock); - } else { - hdr.source = *(const struct ieee802154_addr *)saddr; - } - - hdr.dest = *(const struct ieee802154_addr *)daddr; - - hlen = ieee802154_hdr_push(skb, &hdr); - if (hlen < 0) - return -EINVAL; - - skb_reset_mac_header(skb); - skb->mac_len = hlen; - - if (len > ieee802154_max_payload(&hdr)) - return -EMSGSIZE; - - return hlen; -} - -static int -mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr) -{ - struct ieee802154_hdr hdr; - struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr; - - if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) { - pr_debug("malformed packet\n"); - return 0; - } - - *addr = hdr.source; - return sizeof(*addr); -} - -static netdev_tx_t -mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - u8 chan, page; - int rc; - - priv = netdev_priv(dev); - - spin_lock_bh(&priv->mib_lock); - chan = priv->chan; - page = priv->page; - spin_unlock_bh(&priv->mib_lock); - - if (chan == MAC802154_CHAN_NONE || - page >= WPAN_NUM_PAGES || - chan >= WPAN_NUM_CHANNELS) { - kfree_skb(skb); - return NETDEV_TX_OK; - } - - rc = mac802154_llsec_encrypt(&priv->sec, skb); - if (rc) { - pr_warn("encryption failed: %i\n", rc); - kfree_skb(skb); - return NETDEV_TX_OK; - } - - skb->skb_iif = dev->ifindex; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; - - return mac802154_tx(priv->hw, skb, page, chan); -} - -static struct header_ops mac802154_header_ops = { - .create = mac802154_header_create, - .parse = mac802154_header_parse, -}; - -static const struct net_device_ops mac802154_wpan_ops = { - .ndo_open = mac802154_wpan_open, - .ndo_stop = mac802154_slave_close, - .ndo_start_xmit = mac802154_wpan_xmit, - .ndo_do_ioctl = mac802154_wpan_ioctl, - .ndo_set_mac_address = mac802154_wpan_mac_addr, -}; - -static void mac802154_wpan_free(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv = netdev_priv(dev); - - mac802154_llsec_destroy(&priv->sec); - - free_netdev(dev); -} - -void mac802154_wpan_setup(struct net_device *dev) -{ - struct mac802154_sub_if_data *priv; - - dev->addr_len = IEEE802154_ADDR_LEN; - memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); - - dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN; - dev->header_ops = &mac802154_header_ops; - dev->needed_tailroom = 2 + 16; /* FCS + MIC */ - dev->mtu = IEEE802154_MTU; - dev->tx_queue_len = 300; - dev->type = ARPHRD_IEEE802154; - dev->flags = IFF_NOARP | IFF_BROADCAST; - dev->watchdog_timeo = 0; - - dev->destructor = mac802154_wpan_free; - dev->netdev_ops = &mac802154_wpan_ops; - dev->ml_priv = &mac802154_mlme_wpan; - - priv = netdev_priv(dev); - priv->type = IEEE802154_DEV_WPAN; - - priv->chan = MAC802154_CHAN_NONE; - priv->page = 0; - - spin_lock_init(&priv->mib_lock); - mutex_init(&priv->sec_mtx); - - get_random_bytes(&priv->bsn, 1); - get_random_bytes(&priv->dsn, 1); - - /* defaults per 802.15.4-2011 */ - priv->mac_params.min_be = 3; - priv->mac_params.max_be = 5; - priv->mac_params.csma_retries = 4; - priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */ - - priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST); - priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST); - - mac802154_llsec_init(&priv->sec); -} - -static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb) -{ - return netif_rx_ni(skb); -} - -static int -mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb, - const struct ieee802154_hdr *hdr) -{ - __le16 span, sshort; - int rc; - - pr_debug("getting packet via slave interface %s\n", sdata->dev->name); - - spin_lock_bh(&sdata->mib_lock); - - span = sdata->pan_id; - sshort = sdata->short_addr; - - switch (mac_cb(skb)->dest.mode) { - case IEEE802154_ADDR_NONE: - if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE) - /* FIXME: check if we are PAN coordinator */ - skb->pkt_type = PACKET_OTHERHOST; - else - /* ACK comes with both addresses empty */ - skb->pkt_type = PACKET_HOST; - break; - case IEEE802154_ADDR_LONG: - if (mac_cb(skb)->dest.pan_id != span && - mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) - skb->pkt_type = PACKET_OTHERHOST; - else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr) - skb->pkt_type = PACKET_HOST; - else - skb->pkt_type = PACKET_OTHERHOST; - break; - case IEEE802154_ADDR_SHORT: - if (mac_cb(skb)->dest.pan_id != span && - mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST)) - skb->pkt_type = PACKET_OTHERHOST; - else if (mac_cb(skb)->dest.short_addr == sshort) - skb->pkt_type = PACKET_HOST; - else if (mac_cb(skb)->dest.short_addr == - cpu_to_le16(IEEE802154_ADDR_BROADCAST)) - skb->pkt_type = PACKET_BROADCAST; - else - skb->pkt_type = PACKET_OTHERHOST; - break; - default: - spin_unlock_bh(&sdata->mib_lock); - pr_debug("invalid dest mode\n"); - kfree_skb(skb); - return NET_RX_DROP; - } - - spin_unlock_bh(&sdata->mib_lock); - - skb->dev = sdata->dev; - - rc = mac802154_llsec_decrypt(&sdata->sec, skb); - if (rc) { - pr_debug("decryption failed: %i\n", rc); - goto fail; - } - - sdata->dev->stats.rx_packets++; - sdata->dev->stats.rx_bytes += skb->len; - - switch (mac_cb(skb)->type) { - case IEEE802154_FC_TYPE_DATA: - return mac802154_process_data(sdata->dev, skb); - default: - pr_warn("ieee802154: bad frame received (type = %d)\n", - mac_cb(skb)->type); - goto fail; - } - -fail: - kfree_skb(skb); - return NET_RX_DROP; -} - -static void mac802154_print_addr(const char *name, - const struct ieee802154_addr *addr) -{ - if (addr->mode == IEEE802154_ADDR_NONE) - pr_debug("%s not present\n", name); - - pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id)); - if (addr->mode == IEEE802154_ADDR_SHORT) { - pr_debug("%s is short: %04x\n", name, - le16_to_cpu(addr->short_addr)); - } else { - u64 hw = swab64((__force u64) addr->extended_addr); - - pr_debug("%s is hardware: %8phC\n", name, &hw); - } -} - -static int mac802154_parse_frame_start(struct sk_buff *skb, - struct ieee802154_hdr *hdr) -{ - int hlen; - struct ieee802154_mac_cb *cb = mac_cb_init(skb); - - hlen = ieee802154_hdr_pull(skb, hdr); - if (hlen < 0) - return -EINVAL; - - skb->mac_len = hlen; - - pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc), - hdr->seq); - - cb->type = hdr->fc.type; - cb->ackreq = hdr->fc.ack_request; - cb->secen = hdr->fc.security_enabled; - - mac802154_print_addr("destination", &hdr->dest); - mac802154_print_addr("source", &hdr->source); - - cb->source = hdr->source; - cb->dest = hdr->dest; - - if (hdr->fc.security_enabled) { - u64 key; - - pr_debug("seclevel %i\n", hdr->sec.level); - - switch (hdr->sec.key_id_mode) { - case IEEE802154_SCF_KEY_IMPLICIT: - pr_debug("implicit key\n"); - break; - - case IEEE802154_SCF_KEY_INDEX: - pr_debug("key %02x\n", hdr->sec.key_id); - break; - - case IEEE802154_SCF_KEY_SHORT_INDEX: - pr_debug("key %04x:%04x %02x\n", - le32_to_cpu(hdr->sec.short_src) >> 16, - le32_to_cpu(hdr->sec.short_src) & 0xffff, - hdr->sec.key_id); - break; - - case IEEE802154_SCF_KEY_HW_INDEX: - key = swab64((__force u64) hdr->sec.extended_src); - pr_debug("key source %8phC %02x\n", &key, - hdr->sec.key_id); - break; - } - } - - return 0; -} - -void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb) -{ - int ret; - struct mac802154_sub_if_data *sdata; - struct ieee802154_hdr hdr; - - ret = mac802154_parse_frame_start(skb, &hdr); - if (ret) { - pr_debug("got invalid frame\n"); - kfree_skb(skb); - return; - } - - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &priv->slaves, list) { - if (sdata->type != IEEE802154_DEV_WPAN || - !netif_running(sdata->dev)) - continue; - - mac802154_subif_frame(sdata, skb, &hdr); - skb = NULL; - break; - } - rcu_read_unlock(); - - if (skb) - kfree_skb(skb); -} diff --git a/net/wireless/Makefile b/net/wireless/Makefile index a761670af31d..4c9e39f04ef8 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o -cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 72d81e2154d5..85506f1d0789 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -115,7 +115,7 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) EXPORT_SYMBOL(cfg80211_chandef_valid); static void chandef_primary_freqs(const struct cfg80211_chan_def *c, - int *pri40, int *pri80) + u32 *pri40, u32 *pri80) { int tmp; @@ -366,6 +366,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, break; case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: @@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, *radar_detect |= BIT(wdev->chandef.width); } return; + case NL80211_IFTYPE_OCB: + if (wdev->chandef.chan) { + *chan = wdev->chandef.chan; + *chanmode = CHAN_MODE_SHARED; + return; + } + break; case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: diff --git a/net/wireless/core.c b/net/wireless/core.c index f52a4cd7017c..a4d27927aba2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -86,11 +86,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) return &rdev->wiphy; } -int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname) +static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev, + const char *newname) { struct cfg80211_registered_device *rdev2; - int wiphy_idx, taken = -1, result, digits; + int wiphy_idx, taken = -1, digits; ASSERT_RTNL(); @@ -109,15 +109,28 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return -EINVAL; } + /* Ensure another device does not already have this name. */ + list_for_each_entry(rdev2, &cfg80211_rdev_list, list) + if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) + return -EINVAL; + + return 0; +} + +int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname) +{ + int result; + + ASSERT_RTNL(); /* Ignore nop renames */ - if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0) + if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0) return 0; - /* Ensure another device does not already have this name. */ - list_for_each_entry(rdev2, &cfg80211_rdev_list, list) - if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0) - return -EINVAL; + result = cfg80211_dev_check_name(rdev, newname); + if (result < 0) + return result; result = device_rename(&rdev->wiphy.dev, newname); if (result) @@ -309,7 +322,8 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work) /* exported functions */ -struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) +struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv, + const char *requested_name) { static atomic_t wiphy_counter = ATOMIC_INIT(0); @@ -346,7 +360,31 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy_idx--; /* give it a proper name */ - dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + if (requested_name && requested_name[0]) { + int rv; + + rtnl_lock(); + rv = cfg80211_dev_check_name(rdev, requested_name); + + if (rv < 0) { + rtnl_unlock(); + goto use_default_name; + } + + rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name); + rtnl_unlock(); + if (rv) + goto use_default_name; + } else { +use_default_name: + /* NOTE: This is *probably* safe w/out holding rtnl because of + * the restrictions on phy names. Probably this call could + * fail if some other part of the kernel (re)named a device + * phyX. But, might should add some locking and check return + * value, and use a different name if this one exists? + */ + dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); + } INIT_LIST_HEAD(&rdev->wdev_list); INIT_LIST_HEAD(&rdev->beacon_registrations); @@ -406,7 +444,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) return &rdev->wiphy; } -EXPORT_SYMBOL(wiphy_new); +EXPORT_SYMBOL(wiphy_new_nm); static int wiphy_verify_combinations(struct wiphy *wiphy) { @@ -831,7 +869,22 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_P2P_GO: __cfg80211_stop_ap(rdev, dev, true); break; - default: + case NL80211_IFTYPE_OCB: + __cfg80211_leave_ocb(rdev, dev); + break; + case NL80211_IFTYPE_WDS: + /* must be handled by mac80211/driver, has no APIs */ + break; + case NL80211_IFTYPE_P2P_DEVICE: + /* cannot happen, has no netdev */ + break; + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_MONITOR: + /* nothing to do */ + break; + case NL80211_IFTYPE_UNSPECIFIED: + case NUM_NL80211_IFTYPES: + /* invalid */ break; } } diff --git a/net/wireless/core.h b/net/wireless/core.h index 7e3a3cef7df9..61ee664cf2bd 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -290,6 +290,18 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct cfg80211_chan_def *chandef); +/* OCB */ +int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup); +int cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup); +int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev); +int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev); + /* AP */ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev, bool notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5839c85075f1..1a31736914e5 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -884,7 +884,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev) if (!wdev->current_bss) return -ENOLINK; break; - default: + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_P2P_DEVICE: + case NL80211_IFTYPE_WDS: + case NUM_NL80211_IFTYPES: return -EINVAL; } @@ -1514,8 +1519,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) CMD(channel_switch, CHANNEL_SWITCH); CMD(set_qos_map, SET_QOS_MAP); - if (rdev->wiphy.flags & - WIPHY_FLAG_SUPPORTS_WMM_ADMISSION) + if (rdev->wiphy.features & + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) CMD(add_tx_ts, ADD_TX_TS); } /* add into the if now */ @@ -2605,7 +2610,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) !(rdev->wiphy.interface_modes & (1 << type))) return -EOPNOTSUPP; - if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) { + if ((type == NL80211_IFTYPE_P2P_DEVICE || + rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) && + info->attrs[NL80211_ATTR_MAC]) { nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], ETH_ALEN); if (!is_valid_ether_addr(params.macaddr)) @@ -4398,10 +4405,12 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct net_device *dev = info->user_ptr[1]; - u8 *mac_addr = NULL; + struct station_del_parameters params; + + memset(¶ms, 0, sizeof(params)); if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]); if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && @@ -4412,7 +4421,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) if (!rdev->ops->del_station) return -EOPNOTSUPP; - return rdev_del_station(rdev, dev, mac_addr); + if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) { + params.subtype = + nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); + if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 && + params.subtype != IEEE80211_STYPE_DEAUTH >> 4) + return -EINVAL; + } else { + /* Default to Deauthentication frame */ + params.subtype = IEEE80211_STYPE_DEAUTH >> 4; + } + + if (info->attrs[NL80211_ATTR_REASON_CODE]) { + params.reason_code = + nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); + if (params.reason_code == 0) + return -EINVAL; /* 0 is reserved */ + } else { + /* Default to reason code 2 */ + params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; + } + + return rdev_del_station(rdev, dev, ¶ms); } static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, @@ -4624,6 +4654,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) return rdev_del_mpath(rdev, dev, dst); } +static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + int err; + struct net_device *dev = info->user_ptr[1]; + struct mpath_info pinfo; + struct sk_buff *msg; + u8 *dst = NULL; + u8 mpp[ETH_ALEN]; + + memset(&pinfo, 0, sizeof(pinfo)); + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + dst = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!rdev->ops->get_mpp) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo); + if (err) + return err; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0, + dev, dst, mpp, &pinfo) < 0) { + nlmsg_free(msg); + return -ENOBUFS; + } + + return genlmsg_reply(msg, info); +} + +static int nl80211_dump_mpp(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct mpath_info pinfo; + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + u8 dst[ETH_ALEN]; + u8 mpp[ETH_ALEN]; + int path_idx = cb->args[2]; + int err; + + err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); + if (err) + return err; + + if (!rdev->ops->dump_mpp) { + err = -EOPNOTSUPP; + goto out_err; + } + + if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) { + err = -EOPNOTSUPP; + goto out_err; + } + + while (1) { + err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst, + mpp, &pinfo); + if (err == -ENOENT) + break; + if (err) + goto out_err; + + if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, NLM_F_MULTI, + wdev->netdev, dst, mpp, + &pinfo) < 0) + goto out; + + path_idx++; + } + + out: + cb->args[2] = path_idx; + err = skb->len; + out_err: + nl80211_finish_wdev_dump(rdev); + return err; +} + static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -5923,7 +6043,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) * function is called under RTNL lock, so this should not be a problem. */ static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1]; - u8 radar_detect_width = 0; int err; bool need_new_beacon = false; int len, i; @@ -6059,10 +6178,8 @@ skip_beacons: if (err < 0) return err; - if (err > 0) { - radar_detect_width = BIT(params.chandef.width); + if (err > 0) params.radar_required = true; - } if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) params.block_tx = true; @@ -8159,6 +8276,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } +static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct ocb_setup setup = {}; + int err; + + err = nl80211_parse_chandef(rdev, info, &setup.chandef); + if (err) + return err; + + return cfg80211_join_ocb(rdev, dev, &setup); +} + +static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + return cfg80211_leave_ocb(rdev, dev); +} + static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -9444,7 +9583,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) u16 admitted_time = 0; int err; - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)) + if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)) return -EOPNOTSUPP; if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] || @@ -9460,12 +9599,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) return -EINVAL; /* WMM uses TIDs 0-7 even for TSPEC */ - if (tsid < IEEE80211_FIRST_TSPEC_TSID) { - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)) - return -EINVAL; - } else { + if (tsid >= IEEE80211_FIRST_TSPEC_TSID) { /* TODO: handle 802.11 TSPEC/admission control - * need more attributes for that (e.g. BA session requirement) + * need more attributes for that (e.g. BA session requirement); + * change the WMM adminssion test above to allow both then */ return -EINVAL; } @@ -9782,6 +9919,15 @@ static const struct genl_ops nl80211_ops[] = { NL80211_FLAG_NEED_RTNL, }, { + .cmd = NL80211_CMD_GET_MPP, + .doit = nl80211_get_mpp, + .dumpit = nl80211_dump_mpp, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { .cmd = NL80211_CMD_SET_MPATH, .doit = nl80211_set_mpath, .policy = nl80211_policy, @@ -10095,6 +10241,22 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_JOIN_OCB, + .doit = nl80211_join_ocb, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_LEAVE_OCB, + .doit = nl80211_leave_ocb, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, #ifdef CONFIG_PM { .cmd = NL80211_CMD_GET_WOWLAN, diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c new file mode 100644 index 000000000000..c00d4a792319 --- /dev/null +++ b/net/wireless/ocb.c @@ -0,0 +1,88 @@ +/* + * OCB mode implementation + * + * Copyright: (c) 2014 Czech Technical University in Prague + * (c) 2014 Volkswagen Group Research + * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz> + * Funded by: Volkswagen Group Research + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include "nl80211.h" +#include "core.h" +#include "rdev-ops.h" + +int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) + return -EOPNOTSUPP; + + if (WARN_ON(!setup->chandef.chan)) + return -EINVAL; + + err = rdev_join_ocb(rdev, dev, setup); + if (!err) + wdev->chandef = setup->chandef; + + return err; +} + +int cfg80211_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_ocb(rdev, dev, setup); + wdev_unlock(wdev); + + return err; +} + +int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) + return -EOPNOTSUPP; + + if (!rdev->ops->leave_ocb) + return -EOPNOTSUPP; + + err = rdev_leave_ocb(rdev, dev); + if (!err) + memset(&wdev->chandef, 0, sizeof(wdev->chandef)); + + return err; +} + +int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_ocb(rdev, dev); + wdev_unlock(wdev); + + return err; +} diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index f6d457d6a558..1b3864cd50ca 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -178,11 +178,12 @@ static inline int rdev_add_station(struct cfg80211_registered_device *rdev, } static inline int rdev_del_station(struct cfg80211_registered_device *rdev, - struct net_device *dev, u8 *mac) + struct net_device *dev, + struct station_del_parameters *params) { int ret; - trace_rdev_del_station(&rdev->wiphy, dev, mac); - ret = rdev->ops->del_station(&rdev->wiphy, dev, mac); + trace_rdev_del_station(&rdev->wiphy, dev, params); + ret = rdev->ops->del_station(&rdev->wiphy, dev, params); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } @@ -263,6 +264,18 @@ static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev, } +static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev, + struct net_device *dev, u8 *dst, u8 *mpp, + struct mpath_info *pinfo) +{ + int ret; + + trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp); + ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo); + trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); + return ret; +} + static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev, struct net_device *dev, int idx, u8 *dst, u8 *next_hop, struct mpath_info *pinfo) @@ -271,7 +284,20 @@ static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev, int ret; trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop); ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop, - pinfo); + pinfo); + trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); + return ret; +} + +static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev, + struct net_device *dev, int idx, u8 *dst, + u8 *mpp, struct mpath_info *pinfo) + +{ + int ret; + + trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp); + ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo); trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo); return ret; } @@ -322,6 +348,27 @@ static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ocb_setup *setup) +{ + int ret; + trace_rdev_join_ocb(&rdev->wiphy, dev, setup); + ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + int ret; + trace_rdev_leave_ocb(&rdev->wiphy, dev); + ret = rdev->ops->leave_ocb(&rdev->wiphy, dev); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_change_bss(struct cfg80211_registered_device *rdev, struct net_device *dev, struct bss_parameters *params) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dc1668ff543b..0ab3711c79a0 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -80,9 +80,18 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (!request) return -ENOMEM; - if (wdev->conn->params.channel) + if (wdev->conn->params.channel) { + enum ieee80211_band band = wdev->conn->params.channel->band; + struct ieee80211_supported_band *sband = + wdev->wiphy->bands[band]; + + if (!sband) { + kfree(request); + return -EINVAL; + } request->channels[0] = wdev->conn->params.channel; - else { + request->rates[band] = (1 << sband->n_bitrates) - 1; + } else { int i = 0, j; enum ieee80211_band band; struct ieee80211_supported_band *bands; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 625a6e6d1168..277a85df910e 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -600,6 +600,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss, TP_ARGS(wiphy, netdev) ); +DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), + TP_ARGS(wiphy, netdev) +); + DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev), TP_ARGS(wiphy, netdev) @@ -680,9 +685,34 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac)) ); -DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station, - TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac), - TP_ARGS(wiphy, netdev, mac) +DECLARE_EVENT_CLASS(station_del, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct station_del_parameters *params), + TP_ARGS(wiphy, netdev, params), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(sta_mac) + __field(u8, subtype) + __field(u16, reason_code) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(sta_mac, params->mac); + __entry->subtype = params->subtype; + __entry->reason_code = params->reason_code; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT + ", subtype: %u, reason_code: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), + __entry->subtype, __entry->reason_code) +); + +DEFINE_EVENT(station_del, rdev_del_station, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct station_del_parameters *params), + TP_ARGS(wiphy, netdev, params) ); DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station, @@ -801,6 +831,51 @@ TRACE_EVENT(rdev_dump_mpath, MAC_PR_ARG(next_hop)) ); +TRACE_EVENT(rdev_get_mpp, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + u8 *dst, u8 *mpp), + TP_ARGS(wiphy, netdev, dst, mpp), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dst) + MAC_ENTRY(mpp) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dst, dst); + MAC_ASSIGN(mpp, mpp); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT + ", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, + MAC_PR_ARG(dst), MAC_PR_ARG(mpp)) +); + +TRACE_EVENT(rdev_dump_mpp, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx, + u8 *dst, u8 *mpp), + TP_ARGS(wiphy, netdev, idx, mpp, dst), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(dst) + MAC_ENTRY(mpp) + __field(int, idx) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(dst, dst); + MAC_ASSIGN(mpp, mpp); + __entry->idx = idx; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: " + MAC_PR_FMT ", mpp: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst), + MAC_PR_ARG(mpp)) +); + TRACE_EVENT(rdev_return_int_mpath_info, TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo), TP_ARGS(wiphy, ret, pinfo), @@ -1246,6 +1321,22 @@ TRACE_EVENT(rdev_join_ibss, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid) ); +TRACE_EVENT(rdev_join_ocb, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const struct ocb_setup *setup), + TP_ARGS(wiphy, netdev, setup), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG) +); + TRACE_EVENT(rdev_set_wiphy_params, TP_PROTO(struct wiphy *wiphy, u32 changed), TP_ARGS(wiphy, changed), diff --git a/net/wireless/util.c b/net/wireless/util.c index 5e233a577d0f..d0ac795445b7 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -442,7 +442,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, break; case cpu_to_le16(0): if (iftype != NL80211_IFTYPE_ADHOC && - iftype != NL80211_IFTYPE_STATION) + iftype != NL80211_IFTYPE_STATION && + iftype != NL80211_IFTYPE_OCB) return -1; break; } @@ -519,6 +520,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, memcpy(hdr.addr3, skb->data, ETH_ALEN); hdrlen = 24; break; + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); @@ -937,6 +939,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (dev->ieee80211_ptr->use_4addr) break; /* fall through */ + case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_ADHOC: dev->priv_flags |= IFF_DONT_BRIDGE; |