diff options
85 files changed, 51 insertions, 56091 deletions
diff --git a/net/Kconfig b/net/Kconfig index b74076a..2d583ad 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -231,7 +231,7 @@ source "net/dns_resolver/Kconfig" source "net/batman-adv/Kconfig" config RPS - boolean "RPS" + boolean depends on SMP && SYSFS && USE_GENERIC_SMP_HELPERS default y @@ -314,7 +314,6 @@ source "net/can/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" source "net/bluetooth_mgmt/Kconfig" -source "net/bluetooth_tizen/Kconfig" source "net/rxrpc/Kconfig" config FIB_RULES diff --git a/net/Makefile b/net/Makefile index 11fee99..b62570f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -37,12 +37,8 @@ obj-$(CONFIG_IRDA) += irda/ ifeq ($(CONFIG_BT_MGMT),y) obj-$(CONFIG_BT) += bluetooth_mgmt/ else -ifeq ($(CONFIG_BT_TIZEN),y) -obj-$(CONFIG_BT) += bluetooth_tizen/ -else obj-$(CONFIG_BT) += bluetooth/ endif -endif obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_AF_RXRPC) += rxrpc/ obj-$(CONFIG_ATM) += atm/ @@ -56,11 +52,7 @@ endif obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ obj-$(CONFIG_RDS) += rds/ -ifeq ($(CONFIG_MACH_PX),y) -obj-$(CONFIG_WIRELESS) += wireless_ath/ -else obj-$(CONFIG_WIRELESS) += wireless/ -endif obj-$(CONFIG_MAC80211) += mac80211/ obj-$(CONFIG_TIPC) += tipc/ obj-$(CONFIG_NETLABEL) += netlabel/ diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 9047512..8ea4963 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -532,8 +532,9 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) BT_DBG("sk %p", sk); add_wait_queue(sk_sleep(sk), &wait); - set_current_state(TASK_INTERRUPTIBLE); while (sk->sk_state != state) { + set_current_state(TASK_INTERRUPTIBLE); + if (!timeo) { err = -EINPROGRESS; break; @@ -547,13 +548,12 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); - set_current_state(TASK_INTERRUPTIBLE); err = sock_error(sk); if (err) break; } - __set_current_state(TASK_RUNNING); + set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); return err; } diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c index 040f67b..744233c 100644 --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -326,7 +326,7 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) { struct capi_ctr *ctrl = &session->ctrl; struct cmtp_application *application; - __u16 appl; + __u16 cmd, appl; __u32 contr; BT_DBG("session %p skb %p len %d", session, skb, skb->len); @@ -344,6 +344,7 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) return; } + cmd = CAPICMD(CAPIMSG_COMMAND(skb->data), CAPIMSG_SUBCOMMAND(skb->data)); appl = CAPIMSG_APPID(skb->data); contr = CAPIMSG_CONTROL(skb->data); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b1b7b3d..c1a5685 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -146,7 +146,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, switch (hdev->req_status) { case HCI_REQ_DONE: - err = -bt_to_errno(hdev->req_result); + err = -bt_err(hdev->req_result); break; case HCI_REQ_CANCELED: @@ -545,7 +545,7 @@ int hci_dev_open(__u16 dev) ret = __hci_request(hdev, hci_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); - if (lmp_host_le_capable(hdev)) + if (lmp_le_capable(hdev)) ret = __hci_request(hdev, hci_le_init_req, 0, msecs_to_jiffies(HCI_INIT_TIMEOUT)); @@ -1214,6 +1214,7 @@ static void hci_cmd_timer(unsigned long arg) BT_ERR("%s command tx timeout", hdev->name); atomic_set(&hdev->cmd_cnt, 1); + clear_bit(HCI_RESET, &hdev->flags); tasklet_schedule(&hdev->cmd_task); } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 754b153..5759bb7 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -62,6 +62,7 @@ static DEFINE_MUTEX(rfcomm_mutex); #define rfcomm_lock() mutex_lock(&rfcomm_mutex) #define rfcomm_unlock() mutex_unlock(&rfcomm_mutex) +static unsigned long rfcomm_event; static LIST_HEAD(session_list); @@ -119,6 +120,7 @@ static inline void rfcomm_schedule(void) { if (!rfcomm_thread) return; + set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); wake_up_process(rfcomm_thread); } @@ -409,19 +411,11 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, return err; } - /* After L2sock created, increase refcnt immediately - * It can make connection drop in rfcomm_security_cfm because of refcnt. - */ - rfcomm_session_hold(s); - dlci = __dlci(!s->initiator, channel); /* Check if DLCI already exists */ - if (rfcomm_dlc_get(s, dlci)) { - /* decrement refcnt */ - rfcomm_session_put(s); + if (rfcomm_dlc_get(s, dlci)) return -EBUSY; - } rfcomm_dlc_clear_state(d); @@ -437,9 +431,6 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, d->mtu = s->mtu; d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; - /* decrement refcnt */ - rfcomm_session_put(s); - if (s->state == BT_CONNECTED) { if (rfcomm_check_security(d)) rfcomm_send_pn(s, 1, d); @@ -475,6 +466,7 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) switch (d->state) { case BT_CONNECT: + case BT_CONFIG: if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { set_bit(RFCOMM_AUTH_REJECT, &d->flags); rfcomm_schedule(); @@ -1156,7 +1148,6 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) if (list_empty(&s->dlcs)) { s->state = BT_DISCONN; rfcomm_send_disc(s, 0); - rfcomm_session_clear_timer(s); } break; @@ -1173,7 +1164,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) /* When socket is closed and we are not RFCOMM * initiator rfcomm_process_rx already calls * rfcomm_session_put() */ - if (s->sock->sk->sk_state != BT_CLOSED && !s->initiator) + if (s->sock->sk->sk_state != BT_CLOSED) if (list_empty(&s->dlcs)) rfcomm_session_put(s); break; @@ -1864,10 +1855,7 @@ static inline void rfcomm_process_rx(struct rfcomm_session *s) /* Get data directly from socket receive queue without copying it. */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - if (!skb_linearize(skb)) - rfcomm_recv_frame(s, skb); - else - kfree_skb(skb); + rfcomm_recv_frame(s, skb); } if (sk->sk_state == BT_CLOSED) { @@ -2050,18 +2038,19 @@ static int rfcomm_run(void *unused) rfcomm_add_listener(BDADDR_ANY); - while (1) { + while (!kthread_should_stop()) { set_current_state(TASK_INTERRUPTIBLE); - - if (kthread_should_stop()) - break; + if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { + /* No pending events. Let's sleep. + * Incoming connections and data will wake us up. */ + schedule(); + } + set_current_state(TASK_RUNNING); /* Process stuff */ + clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); rfcomm_process_sessions(); - - schedule(); } - __set_current_state(TASK_RUNNING); rfcomm_kill_listener(); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 56fcfd1..bf6f8f5 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -485,6 +485,11 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f lock_sock(sk); + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; + goto done; + } + if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; @@ -496,20 +501,19 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { + while (!(nsk = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; + if (!timeo) { + err = -EAGAIN; break; } - nsk = bt_accept_dequeue(sk, newsock); - if (nsk) - break; + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); - if (!timeo) { - err = -EAGAIN; + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; break; } @@ -517,12 +521,8 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f err = sock_intr_errno(timeo); break; } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); } - __set_current_state(TASK_RUNNING); + set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) @@ -681,8 +681,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c { struct sock *sk = sock->sk; struct bt_security sec; - int err = 0; - size_t len; + int len, err = 0; u32 opt; BT_DBG("sk %p", sk); @@ -744,6 +743,7 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; + struct sock *l2cap_sk; struct rfcomm_conninfo cinfo; struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; int len, err = 0; @@ -788,6 +788,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u break; } + l2cap_sk = rfcomm_pi(sk)->dlc->session->sock->sk; memset(&cinfo, 0, sizeof(cinfo)); cinfo.hci_handle = conn->hcon->handle; diff --git a/net/bluetooth_tizen/Kconfig b/net/bluetooth_tizen/Kconfig deleted file mode 100644 index 6d17deb..0000000 --- a/net/bluetooth_tizen/Kconfig +++ /dev/null @@ -1,49 +0,0 @@ -# -# Bluetooth subsystem configuration -# - -menuconfig BT - tristate "Bluetooth subsystem support(Tizen)" - depends on NET && !S390 - depends on RFKILL || !RFKILL - select CRC16 - select CRYPTO - select CRYPTO_BLKCIPHER - select CRYPTO_AES - select CRYPTO_ECB - help - Bluetooth is low-cost, low-power, short-range wireless technology. - It was designed as a replacement for cables and other short-range - technologies like IrDA. Bluetooth operates in personal area range - that typically extends up to 10 meters. More information about - Bluetooth can be found at <http://www.bluetooth.com/>. - - Linux Bluetooth subsystem consist of several layers: - Bluetooth Core - HCI device and connection manager, scheduler - SCO audio links - L2CAP (Logical Link Control and Adaptation Protocol) - SMP (Security Manager Protocol) on LE (Low Energy) links - HCI Device drivers (Interface to the hardware) - RFCOMM Module (RFCOMM Protocol) - BNEP Module (Bluetooth Network Encapsulation Protocol) - CMTP Module (CAPI Message Transport Protocol) - HIDP Module (Human Interface Device Protocol) - - Say Y here to compile Bluetooth support into the kernel or say M to - compile it as module (bluetooth). - - To use Linux Bluetooth subsystem, you will need several user-space - utilities like hciconfig and bluetoothd. These utilities and updates - to Bluetooth kernel modules are provided in the BlueZ packages. For - more information, see <http://www.bluez.org/>. - -source "net/bluetooth_tizen/rfcomm/Kconfig" - -source "net/bluetooth_tizen/bnep/Kconfig" - -source "net/bluetooth_tizen/cmtp/Kconfig" - -source "net/bluetooth_tizen/hidp/Kconfig" - -source "drivers/bluetooth_tizen/Kconfig" diff --git a/net/bluetooth_tizen/Makefile b/net/bluetooth_tizen/Makefile deleted file mode 100644 index 2dc5a57..0000000 --- a/net/bluetooth_tizen/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# -# Makefile for the Linux Bluetooth subsystem. -# - -obj-$(CONFIG_BT) += bluetooth.o -obj-$(CONFIG_BT_RFCOMM) += rfcomm/ -obj-$(CONFIG_BT_BNEP) += bnep/ -obj-$(CONFIG_BT_CMTP) += cmtp/ -obj-$(CONFIG_BT_HIDP) += hidp/ - -bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ - hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o diff --git a/net/bluetooth_tizen/af_bluetooth.c b/net/bluetooth_tizen/af_bluetooth.c deleted file mode 100644 index 6fb68a9..0000000 --- a/net/bluetooth_tizen/af_bluetooth.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth address family and sockets. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/list.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/skbuff.h> -#include <linux/init.h> -#include <linux/poll.h> -#include <net/sock.h> -#include <asm/ioctls.h> -#include <linux/kmod.h> - -#include <net/bluetooth/bluetooth.h> - -#define VERSION "2.16" - -/* Bluetooth sockets */ -#define BT_MAX_PROTO 8 -static const struct net_proto_family *bt_proto[BT_MAX_PROTO]; -static DEFINE_RWLOCK(bt_proto_lock); - -static struct lock_class_key bt_lock_key[BT_MAX_PROTO]; -static const char *const bt_key_strings[BT_MAX_PROTO] = { - "sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP", - "sk_lock-AF_BLUETOOTH-BTPROTO_HCI", - "sk_lock-AF_BLUETOOTH-BTPROTO_SCO", - "sk_lock-AF_BLUETOOTH-BTPROTO_RFCOMM", - "sk_lock-AF_BLUETOOTH-BTPROTO_BNEP", - "sk_lock-AF_BLUETOOTH-BTPROTO_CMTP", - "sk_lock-AF_BLUETOOTH-BTPROTO_HIDP", - "sk_lock-AF_BLUETOOTH-BTPROTO_AVDTP", -}; - -static struct lock_class_key bt_slock_key[BT_MAX_PROTO]; -static const char *const bt_slock_key_strings[BT_MAX_PROTO] = { - "slock-AF_BLUETOOTH-BTPROTO_L2CAP", - "slock-AF_BLUETOOTH-BTPROTO_HCI", - "slock-AF_BLUETOOTH-BTPROTO_SCO", - "slock-AF_BLUETOOTH-BTPROTO_RFCOMM", - "slock-AF_BLUETOOTH-BTPROTO_BNEP", - "slock-AF_BLUETOOTH-BTPROTO_CMTP", - "slock-AF_BLUETOOTH-BTPROTO_HIDP", - "slock-AF_BLUETOOTH-BTPROTO_AVDTP", -}; - -void bt_sock_reclassify_lock(struct sock *sk, int proto) -{ - BUG_ON(!sk); - BUG_ON(sock_owned_by_user(sk)); - - sock_lock_init_class_and_name(sk, - bt_slock_key_strings[proto], &bt_slock_key[proto], - bt_key_strings[proto], &bt_lock_key[proto]); -} -EXPORT_SYMBOL(bt_sock_reclassify_lock); - -int bt_sock_register(int proto, const struct net_proto_family *ops) -{ - int err = 0; - - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; - - write_lock(&bt_proto_lock); - - if (bt_proto[proto]) - err = -EEXIST; - else - bt_proto[proto] = ops; - - write_unlock(&bt_proto_lock); - - return err; -} -EXPORT_SYMBOL(bt_sock_register); - -int bt_sock_unregister(int proto) -{ - int err = 0; - - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; - - write_lock(&bt_proto_lock); - - if (!bt_proto[proto]) - err = -ENOENT; - else - bt_proto[proto] = NULL; - - write_unlock(&bt_proto_lock); - - return err; -} -EXPORT_SYMBOL(bt_sock_unregister); - -static int bt_sock_create(struct net *net, struct socket *sock, int proto, - int kern) -{ - int err; - - if (net != &init_net) - return -EAFNOSUPPORT; - - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; - - if (!bt_proto[proto]) - request_module("bt-proto-%d", proto); - - err = -EPROTONOSUPPORT; - - read_lock(&bt_proto_lock); - - if (bt_proto[proto] && try_module_get(bt_proto[proto]->owner)) { - err = bt_proto[proto]->create(net, sock, proto, kern); - if (!err) - bt_sock_reclassify_lock(sock->sk, proto); - module_put(bt_proto[proto]->owner); - } - - read_unlock(&bt_proto_lock); - - return err; -} - -void bt_sock_link(struct bt_sock_list *l, struct sock *sk) -{ - write_lock(&l->lock); - sk_add_node(sk, &l->head); - write_unlock(&l->lock); -} -EXPORT_SYMBOL(bt_sock_link); - -void bt_sock_unlink(struct bt_sock_list *l, struct sock *sk) -{ - write_lock(&l->lock); - sk_del_node_init(sk); - write_unlock(&l->lock); -} -EXPORT_SYMBOL(bt_sock_unlink); - -void bt_accept_enqueue(struct sock *parent, struct sock *sk) -{ - BT_DBG("parent %p, sk %p", parent, sk); - - sock_hold(sk); - list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); - bt_sk(sk)->parent = parent; - parent->sk_ack_backlog++; -} -EXPORT_SYMBOL(bt_accept_enqueue); - -void bt_accept_unlink(struct sock *sk) -{ - BT_DBG("sk %p state %d", sk, sk->sk_state); - - list_del_init(&bt_sk(sk)->accept_q); - bt_sk(sk)->parent->sk_ack_backlog--; - bt_sk(sk)->parent = NULL; - sock_put(sk); -} -EXPORT_SYMBOL(bt_accept_unlink); - -struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) -{ - struct list_head *p, *n; - struct sock *sk; - - BT_DBG("parent %p", parent); - - list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { - sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); - - lock_sock(sk); - - /* FIXME: Is this check still needed */ - if (sk->sk_state == BT_CLOSED) { - release_sock(sk); - bt_accept_unlink(sk); - continue; - } - - if (sk->sk_state == BT_CONNECTED || !newsock || - bt_sk(parent)->defer_setup) { - bt_accept_unlink(sk); - if (newsock) - sock_graft(sk, newsock); - - release_sock(sk); - return sk; - } - - release_sock(sk); - } - - return NULL; -} -EXPORT_SYMBOL(bt_accept_dequeue); - -int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) -{ - int noblock = flags & MSG_DONTWAIT; - struct sock *sk = sock->sk; - struct sk_buff *skb; - size_t copied; - int err; - - BT_DBG("sock %p sk %p len %zu", sock, sk, len); - - if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) { - if (sk->sk_shutdown & RCV_SHUTDOWN) - return 0; - return err; - } - - msg->msg_namelen = 0; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - if (err == 0) - sock_recv_ts_and_drops(msg, sk, skb); - - skb_free_datagram(sk, skb); - - return err ? : copied; -} -EXPORT_SYMBOL(bt_sock_recvmsg); - -static long bt_sock_data_wait(struct sock *sk, long timeo) -{ - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(sk_sleep(sk), &wait); - for (;;) { - set_current_state(TASK_INTERRUPTIBLE); - - if (!skb_queue_empty(&sk->sk_receive_queue)) - break; - - if (sk->sk_err || (sk->sk_shutdown & RCV_SHUTDOWN)) - break; - - if (signal_pending(current) || !timeo) - break; - - set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return timeo; -} - -int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) -{ - struct sock *sk = sock->sk; - int err = 0; - size_t target, copied = 0; - long timeo; - - if (flags & MSG_OOB) - return -EOPNOTSUPP; - - msg->msg_namelen = 0; - - BT_DBG("sk %p size %zu", sk, size); - - lock_sock(sk); - - target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); - timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); - - do { - struct sk_buff *skb; - int chunk; - - skb = skb_dequeue(&sk->sk_receive_queue); - if (!skb) { - if (copied >= target) - break; - - err = sock_error(sk); - if (err) - break; - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - - err = -EAGAIN; - if (!timeo) - break; - - timeo = bt_sock_data_wait(sk, timeo); - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - goto out; - } - continue; - } - - chunk = min_t(unsigned int, skb->len, size); - if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) { - skb_queue_head(&sk->sk_receive_queue, skb); - if (!copied) - copied = -EFAULT; - break; - } - copied += chunk; - size -= chunk; - - sock_recv_ts_and_drops(msg, sk, skb); - - if (!(flags & MSG_PEEK)) { - int skb_len = skb_headlen(skb); - - if (chunk <= skb_len) { - __skb_pull(skb, chunk); - } else { - struct sk_buff *frag; - - __skb_pull(skb, skb_len); - chunk -= skb_len; - - skb_walk_frags(skb, frag) { - if (chunk <= frag->len) { - /* Pulling partial data */ - skb->len -= chunk; - skb->data_len -= chunk; - __skb_pull(frag, chunk); - break; - } else if (frag->len) { - /* Pulling all frag data */ - chunk -= frag->len; - skb->len -= frag->len; - skb->data_len -= frag->len; - __skb_pull(frag, frag->len); - } - } - } - - if (skb->len) { - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - kfree_skb(skb); - - } else { - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - } while (size); - -out: - release_sock(sk); - return copied ? : err; -} -EXPORT_SYMBOL(bt_sock_stream_recvmsg); - -static inline unsigned int bt_accept_poll(struct sock *parent) -{ - struct list_head *p, *n; - struct sock *sk; - - list_for_each_safe(p, n, &bt_sk(parent)->accept_q) { - sk = (struct sock *) list_entry(p, struct bt_sock, accept_q); - if (sk->sk_state == BT_CONNECTED || - (bt_sk(parent)->defer_setup && - sk->sk_state == BT_CONNECT2)) - return POLLIN | POLLRDNORM; - } - - return 0; -} - -unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait) -{ - struct sock *sk = sock->sk; - unsigned int mask = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - poll_wait(file, sk_sleep(sk), wait); - - if (sk->sk_state == BT_LISTEN) - return bt_accept_poll(sk); - - if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue)) - mask |= POLLERR; - - if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= POLLRDHUP | POLLIN | POLLRDNORM; - - if (sk->sk_shutdown == SHUTDOWN_MASK) - mask |= POLLHUP; - - if (!skb_queue_empty(&sk->sk_receive_queue)) - mask |= POLLIN | POLLRDNORM; - - if (sk->sk_state == BT_CLOSED) - mask |= POLLHUP; - - if (sk->sk_state == BT_CONNECT || - sk->sk_state == BT_CONNECT2 || - sk->sk_state == BT_CONFIG) - return mask; - - if (!bt_sk(sk)->suspended && sock_writeable(sk)) - mask |= POLLOUT | POLLWRNORM | POLLWRBAND; - else - set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - - return mask; -} -EXPORT_SYMBOL(bt_sock_poll); - -int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - struct sk_buff *skb; - long amount; - int err; - - BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); - - switch (cmd) { - case TIOCOUTQ: - if (sk->sk_state == BT_LISTEN) - return -EINVAL; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - err = put_user(amount, (int __user *) arg); - break; - - case TIOCINQ: - if (sk->sk_state == BT_LISTEN) - return -EINVAL; - - lock_sock(sk); - skb = skb_peek(&sk->sk_receive_queue); - amount = skb ? skb->len : 0; - release_sock(sk); - err = put_user(amount, (int __user *) arg); - break; - - case SIOCGSTAMP: - err = sock_get_timestamp(sk, (struct timeval __user *) arg); - break; - - case SIOCGSTAMPNS: - err = sock_get_timestampns(sk, (struct timespec __user *) arg); - break; - - default: - err = -ENOIOCTLCMD; - break; - } - - return err; -} -EXPORT_SYMBOL(bt_sock_ioctl); - -int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) -{ - DECLARE_WAITQUEUE(wait, current); - int err = 0; - - BT_DBG("sk %p", sk); - - add_wait_queue(sk_sleep(sk), &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (sk->sk_state != state) { - if (!timeo) { - err = -EINPROGRESS; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - set_current_state(TASK_INTERRUPTIBLE); - - err = sock_error(sk); - if (err) - break; - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return err; -} -EXPORT_SYMBOL(bt_sock_wait_state); - -static struct net_proto_family bt_sock_family_ops = { - .owner = THIS_MODULE, - .family = PF_BLUETOOTH, - .create = bt_sock_create, -}; - -static int __init bt_init(void) -{ - int err; - - BT_INFO("Core ver %s", VERSION); - - err = bt_sysfs_init(); - if (err < 0) - return err; - - err = sock_register(&bt_sock_family_ops); - if (err < 0) { - bt_sysfs_cleanup(); - return err; - } - - BT_INFO("HCI device and connection manager initialized"); - - err = hci_sock_init(); - if (err < 0) - goto error; - - err = l2cap_init(); - if (err < 0) - goto sock_err; - - err = sco_init(); - if (err < 0) { - l2cap_exit(); - goto sock_err; - } - - return 0; - -sock_err: - hci_sock_cleanup(); - -error: - sock_unregister(PF_BLUETOOTH); - bt_sysfs_cleanup(); - - return err; -} - -static void __exit bt_exit(void) -{ - - sco_exit(); - - l2cap_exit(); - - hci_sock_cleanup(); - - sock_unregister(PF_BLUETOOTH); - - bt_sysfs_cleanup(); -} - -subsys_initcall(bt_init); -module_exit(bt_exit); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth Core ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_BLUETOOTH); diff --git a/net/bluetooth_tizen/bnep/Kconfig b/net/bluetooth_tizen/bnep/Kconfig deleted file mode 100644 index 71791fc..0000000 --- a/net/bluetooth_tizen/bnep/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config BT_BNEP - tristate "BNEP protocol support" - depends on BT - select CRC32 - help - BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet - emulation layer on top of Bluetooth. BNEP is required for - Bluetooth PAN (Personal Area Network). - - Say Y here to compile BNEP support into the kernel or say M to - compile it as module (bnep). - -config BT_BNEP_MC_FILTER - bool "Multicast filter support" - depends on BT_BNEP - help - This option enables the multicast filter support for BNEP. - -config BT_BNEP_PROTO_FILTER - bool "Protocol filter support" - depends on BT_BNEP - help - This option enables the protocol filter support for BNEP. - diff --git a/net/bluetooth_tizen/bnep/Makefile b/net/bluetooth_tizen/bnep/Makefile deleted file mode 100644 index c7821e7..0000000 --- a/net/bluetooth_tizen/bnep/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux Bluetooth BNEP layer. -# - -obj-$(CONFIG_BT_BNEP) += bnep.o - -bnep-objs := core.o sock.o netdev.o diff --git a/net/bluetooth_tizen/bnep/bnep.h b/net/bluetooth_tizen/bnep/bnep.h deleted file mode 100644 index e7ee531..0000000 --- a/net/bluetooth_tizen/bnep/bnep.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - BNEP protocol definition for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.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. - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef _BNEP_H -#define _BNEP_H - -#include <linux/types.h> -#include <linux/crc32.h> -#include <net/bluetooth/bluetooth.h> - -/* Limits */ -#define BNEP_MAX_PROTO_FILTERS 5 -#define BNEP_MAX_MULTICAST_FILTERS 20 - -/* UUIDs */ -#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB -#define BNEP_UUID16 0x02 -#define BNEP_UUID32 0x04 -#define BNEP_UUID128 0x16 - -#define BNEP_SVC_PANU 0x1115 -#define BNEP_SVC_NAP 0x1116 -#define BNEP_SVC_GN 0x1117 - -/* Packet types */ -#define BNEP_GENERAL 0x00 -#define BNEP_CONTROL 0x01 -#define BNEP_COMPRESSED 0x02 -#define BNEP_COMPRESSED_SRC_ONLY 0x03 -#define BNEP_COMPRESSED_DST_ONLY 0x04 - -/* Control types */ -#define BNEP_CMD_NOT_UNDERSTOOD 0x00 -#define BNEP_SETUP_CONN_REQ 0x01 -#define BNEP_SETUP_CONN_RSP 0x02 -#define BNEP_FILTER_NET_TYPE_SET 0x03 -#define BNEP_FILTER_NET_TYPE_RSP 0x04 -#define BNEP_FILTER_MULTI_ADDR_SET 0x05 -#define BNEP_FILTER_MULTI_ADDR_RSP 0x06 - -/* Extension types */ -#define BNEP_EXT_CONTROL 0x00 - -/* Response messages */ -#define BNEP_SUCCESS 0x00 - -#define BNEP_CONN_INVALID_DST 0x01 -#define BNEP_CONN_INVALID_SRC 0x02 -#define BNEP_CONN_INVALID_SVC 0x03 -#define BNEP_CONN_NOT_ALLOWED 0x04 - -#define BNEP_FILTER_UNSUPPORTED_REQ 0x01 -#define BNEP_FILTER_INVALID_RANGE 0x02 -#define BNEP_FILTER_INVALID_MCADDR 0x02 -#define BNEP_FILTER_LIMIT_REACHED 0x03 -#define BNEP_FILTER_DENIED_SECURITY 0x04 - -/* L2CAP settings */ -#define BNEP_MTU 1691 -#define BNEP_PSM 0x0f -#define BNEP_FLUSH_TO 0xffff -#define BNEP_CONNECT_TO 15 -#define BNEP_FILTER_TO 15 - -/* Headers */ -#define BNEP_TYPE_MASK 0x7f -#define BNEP_EXT_HEADER 0x80 - -struct bnep_setup_conn_req { - __u8 type; - __u8 ctrl; - __u8 uuid_size; - __u8 service[0]; -} __packed; - -struct bnep_set_filter_req { - __u8 type; - __u8 ctrl; - __be16 len; - __u8 list[0]; -} __packed; - -struct bnep_control_rsp { - __u8 type; - __u8 ctrl; - __be16 resp; -} __packed; - -struct bnep_ext_hdr { - __u8 type; - __u8 len; - __u8 data[0]; -} __packed; - -/* BNEP ioctl defines */ -#define BNEPCONNADD _IOW('B', 200, int) -#define BNEPCONNDEL _IOW('B', 201, int) -#define BNEPGETCONNLIST _IOR('B', 210, int) -#define BNEPGETCONNINFO _IOR('B', 211, int) - -struct bnep_connadd_req { - int sock; /* Connected socket */ - __u32 flags; - __u16 role; - char device[16]; /* Name of the Ethernet device */ -}; - -struct bnep_conndel_req { - __u32 flags; - __u8 dst[ETH_ALEN]; -}; - -struct bnep_conninfo { - __u32 flags; - __u16 role; - __u16 state; - __u8 dst[ETH_ALEN]; - char device[16]; -}; - -struct bnep_connlist_req { - __u32 cnum; - struct bnep_conninfo __user *ci; -}; - -struct bnep_proto_filter { - __u16 start; - __u16 end; -}; - -int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock); -int bnep_del_connection(struct bnep_conndel_req *req); -int bnep_get_connlist(struct bnep_connlist_req *req); -int bnep_get_conninfo(struct bnep_conninfo *ci); - -/* BNEP sessions */ -struct bnep_session { - struct list_head list; - - unsigned int role; - unsigned long state; - unsigned long flags; - atomic_t terminate; - struct task_struct *task; - - struct ethhdr eh; - struct msghdr msg; - - struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS]; - unsigned long long mc_filter; - - struct socket *sock; - struct net_device *dev; -}; - -void bnep_net_setup(struct net_device *dev); -int bnep_sock_init(void); -void bnep_sock_cleanup(void); - -static inline int bnep_mc_hash(__u8 *addr) -{ - return crc32_be(~0, addr, ETH_ALEN) >> 26; -} - -#endif diff --git a/net/bluetooth_tizen/bnep/core.c b/net/bluetooth_tizen/bnep/core.c deleted file mode 100644 index a779ec7..0000000 --- a/net/bluetooth_tizen/bnep/core.c +++ /dev/null @@ -1,747 +0,0 @@ -/* - BNEP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2001-2002 Inventel Systemes - Written 2001-2002 by - Clément Moreau <clement.moreau@inventel.fr> - David Libault <david.libault@inventel.fr> - - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/freezer.h> -#include <linux/errno.h> -#include <linux/net.h> -#include <linux/slab.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/socket.h> -#include <linux/file.h> - -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> - -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> - -#include "bnep.h" - -#define VERSION "1.3" - -static bool compress_src = true; -static bool compress_dst = true; - -static LIST_HEAD(bnep_session_list); -static DECLARE_RWSEM(bnep_session_sem); - -static struct bnep_session *__bnep_get_session(u8 *dst) -{ - struct bnep_session *s; - - BT_DBG(""); - - list_for_each_entry(s, &bnep_session_list, list) - if (!compare_ether_addr(dst, s->eh.h_source)) - return s; - - return NULL; -} - -static void __bnep_link_session(struct bnep_session *s) -{ - list_add(&s->list, &bnep_session_list); -} - -static void __bnep_unlink_session(struct bnep_session *s) -{ - list_del(&s->list); -} - -static int bnep_send(struct bnep_session *s, void *data, size_t len) -{ - struct socket *sock = s->sock; - struct kvec iv = { data, len }; - - return kernel_sendmsg(sock, &s->msg, &iv, 1, len); -} - -static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp) -{ - struct bnep_control_rsp rsp; - rsp.type = BNEP_CONTROL; - rsp.ctrl = ctrl; - rsp.resp = htons(resp); - return bnep_send(s, &rsp, sizeof(rsp)); -} - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER -static inline void bnep_set_default_proto_filter(struct bnep_session *s) -{ - /* (IPv4, ARP) */ - s->proto_filter[0].start = ETH_P_IP; - s->proto_filter[0].end = ETH_P_ARP; - /* (RARP, AppleTalk) */ - s->proto_filter[1].start = ETH_P_RARP; - s->proto_filter[1].end = ETH_P_AARP; - /* (IPX, IPv6) */ - s->proto_filter[2].start = ETH_P_IPX; - s->proto_filter[2].end = ETH_P_IPV6; -} -#endif - -static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len) -{ - int n; - - if (len < 2) - return -EILSEQ; - - n = get_unaligned_be16(data); - data++; - len -= 2; - - if (len < n) - return -EILSEQ; - - BT_DBG("filter len %d", n); - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - n /= 4; - if (n <= BNEP_MAX_PROTO_FILTERS) { - struct bnep_proto_filter *f = s->proto_filter; - int i; - - for (i = 0; i < n; i++) { - f[i].start = get_unaligned_be16(data++); - f[i].end = get_unaligned_be16(data++); - - BT_DBG("proto filter start %d end %d", - f[i].start, f[i].end); - } - - if (i < BNEP_MAX_PROTO_FILTERS) - memset(f + i, 0, sizeof(*f)); - - if (n == 0) - bnep_set_default_proto_filter(s); - - bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS); - } else { - bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED); - } -#else - bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ); -#endif - return 0; -} - -static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len) -{ - int n; - - if (len < 2) - return -EILSEQ; - - n = get_unaligned_be16(data); - data += 2; - len -= 2; - - if (len < n) - return -EILSEQ; - - BT_DBG("filter len %d", n); - -#ifdef CONFIG_BT_BNEP_MC_FILTER - n /= (ETH_ALEN * 2); - - if (n > 0) { - int i; - - s->mc_filter = 0; - - /* Always send broadcast */ - set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter); - - /* Add address ranges to the multicast hash */ - for (; n > 0; n--) { - u8 a1[6], *a2; - - memcpy(a1, data, ETH_ALEN); - data += ETH_ALEN; - a2 = data; - data += ETH_ALEN; - - BT_DBG("mc filter %s -> %s", - batostr((void *) a1), batostr((void *) a2)); - - /* Iterate from a1 to a2 */ - set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); - while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) { - /* Increment a1 */ - i = 5; - while (i >= 0 && ++a1[i--] == 0) - ; - - set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter); - } - } - } - - BT_DBG("mc filter hash 0x%llx", s->mc_filter); - - bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS); -#else - bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ); -#endif - return 0; -} - -static int bnep_rx_control(struct bnep_session *s, void *data, int len) -{ - u8 cmd = *(u8 *)data; - int err = 0; - - data++; - len--; - - switch (cmd) { - case BNEP_CMD_NOT_UNDERSTOOD: - case BNEP_SETUP_CONN_RSP: - case BNEP_FILTER_NET_TYPE_RSP: - case BNEP_FILTER_MULTI_ADDR_RSP: - /* Ignore these for now */ - break; - - case BNEP_FILTER_NET_TYPE_SET: - err = bnep_ctrl_set_netfilter(s, data, len); - break; - - case BNEP_FILTER_MULTI_ADDR_SET: - err = bnep_ctrl_set_mcfilter(s, data, len); - break; - - case BNEP_SETUP_CONN_REQ: - err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED); - break; - - default: { - u8 pkt[3]; - pkt[0] = BNEP_CONTROL; - pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; - pkt[2] = cmd; - bnep_send(s, pkt, sizeof(pkt)); - } - break; - } - - return err; -} - -static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb) -{ - struct bnep_ext_hdr *h; - int err = 0; - - do { - h = (void *) skb->data; - if (!skb_pull(skb, sizeof(*h))) { - err = -EILSEQ; - break; - } - - BT_DBG("type 0x%x len %d", h->type, h->len); - - switch (h->type & BNEP_TYPE_MASK) { - case BNEP_EXT_CONTROL: - bnep_rx_control(s, skb->data, skb->len); - break; - - default: - /* Unknown extension, skip it. */ - break; - } - - if (!skb_pull(skb, h->len)) { - err = -EILSEQ; - break; - } - } while (!err && (h->type & BNEP_EXT_HEADER)); - - return err; -} - -static u8 __bnep_rx_hlen[] = { - ETH_HLEN, /* BNEP_GENERAL */ - 0, /* BNEP_CONTROL */ - 2, /* BNEP_COMPRESSED */ - ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */ - ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */ -}; - -static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb) -{ - struct net_device *dev = s->dev; - struct sk_buff *nskb; - u8 type; - - dev->stats.rx_bytes += skb->len; - - type = *(u8 *) skb->data; - skb_pull(skb, 1); - - if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen)) - goto badframe; - - if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) { - bnep_rx_control(s, skb->data, skb->len); - kfree_skb(skb); - return 0; - } - - skb_reset_mac_header(skb); - - /* Verify and pull out header */ - if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK])) - goto badframe; - - s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); - - if (type & BNEP_EXT_HEADER) { - if (bnep_rx_extension(s, skb) < 0) - goto badframe; - } - - /* Strip 802.1p header */ - if (ntohs(s->eh.h_proto) == 0x8100) { - if (!skb_pull(skb, 4)) - goto badframe; - s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2)); - } - - /* We have to alloc new skb and copy data here :(. Because original skb - * may not be modified and because of the alignment requirements. */ - nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL); - if (!nskb) { - dev->stats.rx_dropped++; - kfree_skb(skb); - return -ENOMEM; - } - skb_reserve(nskb, 2); - - /* Decompress header and construct ether frame */ - switch (type & BNEP_TYPE_MASK) { - case BNEP_COMPRESSED: - memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN); - break; - - case BNEP_COMPRESSED_SRC_ONLY: - memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN); - memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN); - put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); - break; - - case BNEP_COMPRESSED_DST_ONLY: - memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), - ETH_ALEN); - memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, - ETH_ALEN + 2); - break; - - case BNEP_GENERAL: - memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb), - ETH_ALEN * 2); - put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2)); - break; - } - - skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len); - kfree_skb(skb); - - dev->stats.rx_packets++; - nskb->ip_summed = CHECKSUM_NONE; - nskb->protocol = eth_type_trans(nskb, dev); - netif_rx_ni(nskb); - return 0; - -badframe: - dev->stats.rx_errors++; - kfree_skb(skb); - return 0; -} - -static u8 __bnep_tx_types[] = { - BNEP_GENERAL, - BNEP_COMPRESSED_SRC_ONLY, - BNEP_COMPRESSED_DST_ONLY, - BNEP_COMPRESSED -}; - -static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb) -{ - struct ethhdr *eh = (void *) skb->data; - struct socket *sock = s->sock; - struct kvec iv[3]; - int len = 0, il = 0; - u8 type = 0; - - BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type); - - if (!skb->dev) { - /* Control frame sent by us */ - goto send; - } - - iv[il++] = (struct kvec) { &type, 1 }; - len++; - - if (compress_src && !compare_ether_addr(eh->h_dest, s->eh.h_source)) - type |= 0x01; - - if (compress_dst && !compare_ether_addr(eh->h_source, s->eh.h_dest)) - type |= 0x02; - - if (type) - skb_pull(skb, ETH_ALEN * 2); - - type = __bnep_tx_types[type]; - switch (type) { - case BNEP_COMPRESSED_SRC_ONLY: - iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN }; - len += ETH_ALEN; - break; - - case BNEP_COMPRESSED_DST_ONLY: - iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN }; - len += ETH_ALEN; - break; - } - -send: - iv[il++] = (struct kvec) { skb->data, skb->len }; - len += skb->len; - - /* FIXME: linearize skb */ - { - len = kernel_sendmsg(sock, &s->msg, iv, il, len); - } - kfree_skb(skb); - - if (len > 0) { - s->dev->stats.tx_bytes += len; - s->dev->stats.tx_packets++; - return 0; - } - - return len; -} - -static int bnep_session(void *arg) -{ - struct bnep_session *s = arg; - struct net_device *dev = s->dev; - struct sock *sk = s->sock->sk; - struct sk_buff *skb; - wait_queue_t wait; - - BT_DBG(""); - - set_user_nice(current, -15); - - init_waitqueue_entry(&wait, current); - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (atomic_read(&s->terminate)) - break; - /* RX */ - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - bnep_rx_frame(s, skb); - else - kfree_skb(skb); - } - - if (sk->sk_state != BT_CONNECTED) - break; - - /* TX */ - while ((skb = skb_dequeue(&sk->sk_write_queue))) - if (bnep_tx_frame(s, skb)) - break; - netif_wake_queue(dev); - - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - /* Cleanup session */ - down_write(&bnep_session_sem); - - /* Delete network device */ - unregister_netdev(dev); - - /* Wakeup user-space polling for socket errors */ - s->sock->sk->sk_err = EUNATCH; - - wake_up_interruptible(sk_sleep(s->sock->sk)); - - /* Release the socket */ - fput(s->sock->file); - - __bnep_unlink_session(s); - - up_write(&bnep_session_sem); - free_netdev(dev); - module_put_and_exit(0); - return 0; -} - -static struct device *bnep_get_device(struct bnep_session *session) -{ - bdaddr_t *src = &bt_sk(session->sock->sk)->src; - bdaddr_t *dst = &bt_sk(session->sock->sk)->dst; - struct hci_dev *hdev; - struct hci_conn *conn; - - hdev = hci_get_route(dst, src); - if (!hdev) - return NULL; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - - hci_dev_put(hdev); - - return conn ? &conn->dev : NULL; -} - -static struct device_type bnep_type = { - .name = "bluetooth", -}; - -int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) -{ - struct net_device *dev; - struct bnep_session *s, *ss; - u8 dst[ETH_ALEN], src[ETH_ALEN]; - int err; - - BT_DBG(""); - - baswap((void *) dst, &bt_sk(sock->sk)->dst); - baswap((void *) src, &bt_sk(sock->sk)->src); - - /* session struct allocated as private part of net_device */ - dev = alloc_netdev(sizeof(struct bnep_session), - (*req->device) ? req->device : "bnep%d", - bnep_net_setup); - if (!dev) - return -ENOMEM; - - down_write(&bnep_session_sem); - - ss = __bnep_get_session(dst); - if (ss && ss->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; - } - - s = netdev_priv(dev); - - /* This is rx header therefore addresses are swapped. - * ie. eh.h_dest is our local address. */ - memcpy(s->eh.h_dest, &src, ETH_ALEN); - memcpy(s->eh.h_source, &dst, ETH_ALEN); - memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN); - - s->dev = dev; - s->sock = sock; - s->role = req->role; - s->state = BT_CONNECTED; - - s->msg.msg_flags = MSG_NOSIGNAL; - -#ifdef CONFIG_BT_BNEP_MC_FILTER - /* Set default mc filter */ - set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter); -#endif - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - /* Set default protocol filter */ - bnep_set_default_proto_filter(s); -#endif - - SET_NETDEV_DEV(dev, bnep_get_device(s)); - SET_NETDEV_DEVTYPE(dev, &bnep_type); - - err = register_netdev(dev); - if (err) - goto failed; - - __bnep_link_session(s); - - __module_get(THIS_MODULE); - s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name); - if (IS_ERR(s->task)) { - /* Session thread start failed, gotta cleanup. */ - module_put(THIS_MODULE); - unregister_netdev(dev); - __bnep_unlink_session(s); - err = PTR_ERR(s->task); - goto failed; - } - - up_write(&bnep_session_sem); - strcpy(req->device, dev->name); - return 0; - -failed: - up_write(&bnep_session_sem); - free_netdev(dev); - return err; -} - -int bnep_del_connection(struct bnep_conndel_req *req) -{ - struct bnep_session *s; - int err = 0; - - BT_DBG(""); - - down_read(&bnep_session_sem); - - s = __bnep_get_session(req->dst); - if (s) { - atomic_inc(&s->terminate); - wake_up_process(s->task); - } else - err = -ENOENT; - - up_read(&bnep_session_sem); - return err; -} - -static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) -{ - memset(ci, 0, sizeof(*ci)); - memcpy(ci->dst, s->eh.h_source, ETH_ALEN); - strcpy(ci->device, s->dev->name); - ci->flags = s->flags; - ci->state = s->state; - ci->role = s->role; -} - -int bnep_get_connlist(struct bnep_connlist_req *req) -{ - struct bnep_session *s; - int err = 0, n = 0; - - down_read(&bnep_session_sem); - - list_for_each_entry(s, &bnep_session_list, list) { - struct bnep_conninfo ci; - - __bnep_copy_ci(&ci, s); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&bnep_session_sem); - return err; -} - -int bnep_get_conninfo(struct bnep_conninfo *ci) -{ - struct bnep_session *s; - int err = 0; - - down_read(&bnep_session_sem); - - s = __bnep_get_session(ci->dst); - if (s) - __bnep_copy_ci(ci, s); - else - err = -ENOENT; - - up_read(&bnep_session_sem); - return err; -} - -static int __init bnep_init(void) -{ - char flt[50] = ""; - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - strcat(flt, "protocol "); -#endif - -#ifdef CONFIG_BT_BNEP_MC_FILTER - strcat(flt, "multicast"); -#endif - - BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION); - if (flt[0]) - BT_INFO("BNEP filters: %s", flt); - - bnep_sock_init(); - return 0; -} - -static void __exit bnep_exit(void) -{ - bnep_sock_cleanup(); -} - -module_init(bnep_init); -module_exit(bnep_exit); - -module_param(compress_src, bool, 0644); -MODULE_PARM_DESC(compress_src, "Compress sources headers"); - -module_param(compress_dst, bool, 0644); -MODULE_PARM_DESC(compress_dst, "Compress destination headers"); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-4"); diff --git a/net/bluetooth_tizen/bnep/netdev.c b/net/bluetooth_tizen/bnep/netdev.c deleted file mode 100644 index bc40864..0000000 --- a/net/bluetooth_tizen/bnep/netdev.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - BNEP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2001-2002 Inventel Systemes - Written 2001-2002 by - Clément Moreau <clement.moreau@inventel.fr> - David Libault <david.libault@inventel.fr> - - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> -#include <linux/slab.h> - -#include <linux/socket.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/wait.h> - -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> - -#include "bnep.h" - -#define BNEP_TX_QUEUE_LEN 20 - -static int bnep_net_open(struct net_device *dev) -{ - netif_start_queue(dev); - return 0; -} - -static int bnep_net_close(struct net_device *dev) -{ - netif_stop_queue(dev); - return 0; -} - -static void bnep_net_set_mc_list(struct net_device *dev) -{ -#ifdef CONFIG_BT_BNEP_MC_FILTER - struct bnep_session *s = netdev_priv(dev); - struct sock *sk = s->sock->sk; - struct bnep_set_filter_req *r; - struct sk_buff *skb; - int size; - - BT_DBG("%s mc_count %d", dev->name, netdev_mc_count(dev)); - - size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2; - skb = alloc_skb(size, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s Multicast list allocation failed", dev->name); - return; - } - - r = (void *) skb->data; - __skb_put(skb, sizeof(*r)); - - r->type = BNEP_CONTROL; - r->ctrl = BNEP_FILTER_MULTI_ADDR_SET; - - if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { - u8 start[ETH_ALEN] = { 0x01 }; - - /* Request all addresses */ - memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); - r->len = htons(ETH_ALEN * 2); - } else { - struct netdev_hw_addr *ha; - int i, len = skb->len; - - if (dev->flags & IFF_BROADCAST) { - memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN); - } - - /* FIXME: We should group addresses here. */ - - i = 0; - netdev_for_each_mc_addr(ha, dev) { - if (i == BNEP_MAX_MULTICAST_FILTERS) - break; - memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); - memcpy(__skb_put(skb, ETH_ALEN), ha->addr, ETH_ALEN); - - i++; - } - r->len = htons(skb->len - len); - } - - skb_queue_tail(&sk->sk_write_queue, skb); - wake_up_interruptible(sk_sleep(sk)); -#endif -} - -static int bnep_net_set_mac_addr(struct net_device *dev, void *arg) -{ - BT_DBG("%s", dev->name); - return 0; -} - -static void bnep_net_timeout(struct net_device *dev) -{ - BT_DBG("net_timeout"); - netif_wake_queue(dev); -} - -#ifdef CONFIG_BT_BNEP_MC_FILTER -static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s) -{ - struct ethhdr *eh = (void *) skb->data; - - if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), (ulong *) &s->mc_filter)) - return 1; - return 0; -} -#endif - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER -/* Determine ether protocol. Based on eth_type_trans. */ -static inline u16 bnep_net_eth_proto(struct sk_buff *skb) -{ - struct ethhdr *eh = (void *) skb->data; - u16 proto = ntohs(eh->h_proto); - - if (proto >= 1536) - return proto; - - if (get_unaligned((__be16 *) skb->data) == htons(0xFFFF)) - return ETH_P_802_3; - - return ETH_P_802_2; -} - -static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s) -{ - u16 proto = bnep_net_eth_proto(skb); - struct bnep_proto_filter *f = s->proto_filter; - int i; - - for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) { - if (proto >= f[i].start && proto <= f[i].end) - return 0; - } - - BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto); - return 1; -} -#endif - -static netdev_tx_t bnep_net_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct bnep_session *s = netdev_priv(dev); - struct sock *sk = s->sock->sk; - - BT_DBG("skb %p, dev %p", skb, dev); - -#ifdef CONFIG_BT_BNEP_MC_FILTER - if (bnep_net_mc_filter(skb, s)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } -#endif - -#ifdef CONFIG_BT_BNEP_PROTO_FILTER - if (bnep_net_proto_filter(skb, s)) { - kfree_skb(skb); - return NETDEV_TX_OK; - } -#endif - - /* - * We cannot send L2CAP packets from here as we are potentially in a bh. - * So we have to queue them and wake up session thread which is sleeping - * on the sk_sleep(sk). - */ - dev->trans_start = jiffies; - skb_queue_tail(&sk->sk_write_queue, skb); - wake_up_interruptible(sk_sleep(sk)); - - if (skb_queue_len(&sk->sk_write_queue) >= BNEP_TX_QUEUE_LEN) { - BT_DBG("tx queue is full"); - - /* Stop queuing. - * Session thread will do netif_wake_queue() */ - netif_stop_queue(dev); - } - - return NETDEV_TX_OK; -} - -static const struct net_device_ops bnep_netdev_ops = { - .ndo_open = bnep_net_open, - .ndo_stop = bnep_net_close, - .ndo_start_xmit = bnep_net_xmit, - .ndo_validate_addr = eth_validate_addr, - .ndo_set_rx_mode = bnep_net_set_mc_list, - .ndo_set_mac_address = bnep_net_set_mac_addr, - .ndo_tx_timeout = bnep_net_timeout, - .ndo_change_mtu = eth_change_mtu, - -}; - -void bnep_net_setup(struct net_device *dev) -{ - - memset(dev->broadcast, 0xff, ETH_ALEN); - dev->addr_len = ETH_ALEN; - - ether_setup(dev); - dev->priv_flags &= ~IFF_TX_SKB_SHARING; - dev->netdev_ops = &bnep_netdev_ops; - - dev->watchdog_timeo = HZ * 2; -} diff --git a/net/bluetooth_tizen/bnep/sock.c b/net/bluetooth_tizen/bnep/sock.c deleted file mode 100644 index 9f9c8dc..0000000 --- a/net/bluetooth_tizen/bnep/sock.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - BNEP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2001-2002 Inventel Systemes - Written 2001-2002 by - David Libault <david.libault@inventel.fr> - - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/compat.h> -#include <linux/gfp.h> -#include <linux/uaccess.h> -#include <net/sock.h> - -#include <asm/system.h> - -#include "bnep.h" - -static int bnep_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - sock_orphan(sk); - sock_put(sk); - return 0; -} - -static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct bnep_connlist_req cl; - struct bnep_connadd_req ca; - struct bnep_conndel_req cd; - struct bnep_conninfo ci; - struct socket *nsock; - void __user *argp = (void __user *)arg; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case BNEPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - nsock = sockfd_lookup(ca.sock, &err); - if (!nsock) - return err; - - if (nsock->sk->sk_state != BT_CONNECTED) { - sockfd_put(nsock); - return -EBADFD; - } - ca.device[sizeof(ca.device)-1] = 0; - - err = bnep_add_connection(&ca, nsock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else - sockfd_put(nsock); - - return err; - - case BNEPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return bnep_del_connection(&cd); - - case BNEPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = bnep_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case BNEPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = bnep_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - - default: - return -EINVAL; - } - - return 0; -} - -#ifdef CONFIG_COMPAT -static int bnep_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - if (cmd == BNEPGETCONNLIST) { - struct bnep_connlist_req cl; - u32 uci; - int err; - - if (get_user(cl.cnum, (u32 __user *) arg) || - get_user(uci, (u32 __user *) (arg + 4))) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = bnep_get_connlist(&cl); - - if (!err && put_user(cl.cnum, (u32 __user *) arg)) - err = -EFAULT; - - return err; - } - - return bnep_sock_ioctl(sock, cmd, arg); -} -#endif - -static const struct proto_ops bnep_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = bnep_sock_release, - .ioctl = bnep_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = bnep_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .poll = sock_no_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto bnep_proto = { - .name = "BNEP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int bnep_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &bnep_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - return 0; -} - -static const struct net_proto_family bnep_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = bnep_sock_create -}; - -int __init bnep_sock_init(void) -{ - int err; - - err = proto_register(&bnep_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops); - if (err < 0) - goto error; - - return 0; - -error: - BT_ERR("Can't register BNEP socket"); - proto_unregister(&bnep_proto); - return err; -} - -void __exit bnep_sock_cleanup(void) -{ - if (bt_sock_unregister(BTPROTO_BNEP) < 0) - BT_ERR("Can't unregister BNEP socket"); - - proto_unregister(&bnep_proto); -} diff --git a/net/bluetooth_tizen/cmtp/Kconfig b/net/bluetooth_tizen/cmtp/Kconfig deleted file mode 100644 index 94cbf42..0000000 --- a/net/bluetooth_tizen/cmtp/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -config BT_CMTP - tristate "CMTP protocol support" - depends on BT && ISDN_CAPI - help - CMTP (CAPI Message Transport Protocol) is a transport layer - for CAPI messages. CMTP is required for the Bluetooth Common - ISDN Access Profile. - - Say Y here to compile CMTP support into the kernel or say M to - compile it as module (cmtp). - diff --git a/net/bluetooth_tizen/cmtp/Makefile b/net/bluetooth_tizen/cmtp/Makefile deleted file mode 100644 index 890a9a5..0000000 --- a/net/bluetooth_tizen/cmtp/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux Bluetooth CMTP layer -# - -obj-$(CONFIG_BT_CMTP) += cmtp.o - -cmtp-objs := core.o sock.o capi.o diff --git a/net/bluetooth_tizen/cmtp/capi.c b/net/bluetooth_tizen/cmtp/capi.c deleted file mode 100644 index 50f0d13..0000000 --- a/net/bluetooth_tizen/cmtp/capi.c +++ /dev/null @@ -1,624 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/wait.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/isdn/capilli.h> -#include <linux/isdn/capicmd.h> -#include <linux/isdn/capiutil.h> - -#include "cmtp.h" - -#define CAPI_INTEROPERABILITY 0x20 - -#define CAPI_INTEROPERABILITY_REQ CAPICMD(CAPI_INTEROPERABILITY, CAPI_REQ) -#define CAPI_INTEROPERABILITY_CONF CAPICMD(CAPI_INTEROPERABILITY, CAPI_CONF) -#define CAPI_INTEROPERABILITY_IND CAPICMD(CAPI_INTEROPERABILITY, CAPI_IND) -#define CAPI_INTEROPERABILITY_RESP CAPICMD(CAPI_INTEROPERABILITY, CAPI_RESP) - -#define CAPI_INTEROPERABILITY_REQ_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_CONF_LEN (CAPI_MSG_BASELEN + 4) -#define CAPI_INTEROPERABILITY_IND_LEN (CAPI_MSG_BASELEN + 2) -#define CAPI_INTEROPERABILITY_RESP_LEN (CAPI_MSG_BASELEN + 2) - -#define CAPI_FUNCTION_REGISTER 0 -#define CAPI_FUNCTION_RELEASE 1 -#define CAPI_FUNCTION_GET_PROFILE 2 -#define CAPI_FUNCTION_GET_MANUFACTURER 3 -#define CAPI_FUNCTION_GET_VERSION 4 -#define CAPI_FUNCTION_GET_SERIAL_NUMBER 5 -#define CAPI_FUNCTION_MANUFACTURER 6 -#define CAPI_FUNCTION_LOOPBACK 7 - - -#define CMTP_MSGNUM 1 -#define CMTP_APPLID 2 -#define CMTP_MAPPING 3 - -static struct cmtp_application *cmtp_application_add(struct cmtp_session *session, __u16 appl) -{ - struct cmtp_application *app = kzalloc(sizeof(*app), GFP_KERNEL); - - BT_DBG("session %p application %p appl %d", session, app, appl); - - if (!app) - return NULL; - - app->state = BT_OPEN; - app->appl = appl; - - list_add_tail(&app->list, &session->applications); - - return app; -} - -static void cmtp_application_del(struct cmtp_session *session, struct cmtp_application *app) -{ - BT_DBG("session %p application %p", session, app); - - if (app) { - list_del(&app->list); - kfree(app); - } -} - -static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value) -{ - struct cmtp_application *app; - struct list_head *p, *n; - - list_for_each_safe(p, n, &session->applications) { - app = list_entry(p, struct cmtp_application, list); - switch (pattern) { - case CMTP_MSGNUM: - if (app->msgnum == value) - return app; - break; - case CMTP_APPLID: - if (app->appl == value) - return app; - break; - case CMTP_MAPPING: - if (app->mapping == value) - return app; - break; - } - } - - return NULL; -} - -static int cmtp_msgnum_get(struct cmtp_session *session) -{ - session->msgnum++; - - if ((session->msgnum & 0xff) > 200) - session->msgnum = CMTP_INITIAL_MSGNUM + 1; - - return session->msgnum; -} - -static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct cmtp_scb *scb = (void *) skb->cb; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - scb->id = -1; - scb->data = (CAPIMSG_COMMAND(skb->data) == CAPI_DATA_B3); - - skb_queue_tail(&session->transmit, skb); - - wake_up_interruptible(sk_sleep(session->sock->sk)); -} - -static void cmtp_send_interopmsg(struct cmtp_session *session, - __u8 subcmd, __u16 appl, __u16 msgnum, - __u16 function, unsigned char *buf, int len) -{ - struct sk_buff *skb; - unsigned char *s; - - BT_DBG("session %p subcmd 0x%02x appl %d msgnum %d", session, subcmd, appl, msgnum); - - skb = alloc_skb(CAPI_MSG_BASELEN + 6 + len, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for interoperability packet"); - return; - } - - s = skb_put(skb, CAPI_MSG_BASELEN + 6 + len); - - capimsg_setu16(s, 0, CAPI_MSG_BASELEN + 6 + len); - capimsg_setu16(s, 2, appl); - capimsg_setu8 (s, 4, CAPI_INTEROPERABILITY); - capimsg_setu8 (s, 5, subcmd); - capimsg_setu16(s, 6, msgnum); - - /* Interoperability selector (Bluetooth Device Management) */ - capimsg_setu16(s, 8, 0x0001); - - capimsg_setu8 (s, 10, 3 + len); - capimsg_setu16(s, 11, function); - capimsg_setu8 (s, 13, len); - - if (len > 0) - memcpy(s + 14, buf, len); - - cmtp_send_capimsg(session, skb); -} - -static void cmtp_recv_interopmsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl, msgnum, func, info; - __u32 controller; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - switch (CAPIMSG_SUBCOMMAND(skb->data)) { - case CAPI_CONF: - if (skb->len < CAPI_MSG_BASELEN + 10) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 5); - info = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 8); - - switch (func) { - case CAPI_FUNCTION_REGISTER: - msgnum = CAPIMSG_MSGID(skb->data); - - application = cmtp_application_get(session, CMTP_MSGNUM, msgnum); - if (application) { - application->state = BT_CONNECTED; - application->msgnum = 0; - application->mapping = CAPIMSG_APPID(skb->data); - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_RELEASE: - appl = CAPIMSG_APPID(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - application->state = BT_CLOSED; - application->msgnum = 0; - wake_up_interruptible(&session->wait); - } - - break; - - case CAPI_FUNCTION_GET_PROFILE: - if (skb->len < CAPI_MSG_BASELEN + 11 + sizeof(capi_profile)) - break; - - controller = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 11); - msgnum = CAPIMSG_MSGID(skb->data); - - if (!info && (msgnum == CMTP_INITIAL_MSGNUM)) { - session->ncontroller = controller; - wake_up_interruptible(&session->wait); - break; - } - - if (!info && ctrl) { - memcpy(&ctrl->profile, - skb->data + CAPI_MSG_BASELEN + 11, - sizeof(capi_profile)); - session->state = BT_CONNECTED; - capi_ctr_ready(ctrl); - } - - break; - - case CAPI_FUNCTION_GET_MANUFACTURER: - if (skb->len < CAPI_MSG_BASELEN + 15) - break; - - controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 10); - - if (!info && ctrl) { - int len = min_t(uint, CAPI_MANUFACTURER_LEN, - skb->data[CAPI_MSG_BASELEN + 14]); - - memset(ctrl->manu, 0, CAPI_MANUFACTURER_LEN); - strncpy(ctrl->manu, - skb->data + CAPI_MSG_BASELEN + 15, len); - } - - break; - - case CAPI_FUNCTION_GET_VERSION: - if (skb->len < CAPI_MSG_BASELEN + 32) - break; - - controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); - - if (!info && ctrl) { - ctrl->version.majorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 16); - ctrl->version.minorversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 20); - ctrl->version.majormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 24); - ctrl->version.minormanuversion = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 28); - } - - break; - - case CAPI_FUNCTION_GET_SERIAL_NUMBER: - if (skb->len < CAPI_MSG_BASELEN + 17) - break; - - controller = CAPIMSG_U32(skb->data, CAPI_MSG_BASELEN + 12); - - if (!info && ctrl) { - int len = min_t(uint, CAPI_SERIAL_LEN, - skb->data[CAPI_MSG_BASELEN + 16]); - - memset(ctrl->serial, 0, CAPI_SERIAL_LEN); - strncpy(ctrl->serial, - skb->data + CAPI_MSG_BASELEN + 17, len); - } - - break; - } - - break; - - case CAPI_IND: - if (skb->len < CAPI_MSG_BASELEN + 6) - break; - - func = CAPIMSG_U16(skb->data, CAPI_MSG_BASELEN + 3); - - if (func == CAPI_FUNCTION_LOOPBACK) { - int len = min_t(uint, skb->len - CAPI_MSG_BASELEN - 6, - skb->data[CAPI_MSG_BASELEN + 5]); - appl = CAPIMSG_APPID(skb->data); - msgnum = CAPIMSG_MSGID(skb->data); - cmtp_send_interopmsg(session, CAPI_RESP, appl, msgnum, func, - skb->data + CAPI_MSG_BASELEN + 6, len); - } - - break; - } - - kfree_skb(skb); -} - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) -{ - struct capi_ctr *ctrl = &session->ctrl; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - if (skb->len < CAPI_MSG_BASELEN) - return; - - if (CAPIMSG_COMMAND(skb->data) == CAPI_INTEROPERABILITY) { - cmtp_recv_interopmsg(session, skb); - return; - } - - if (session->flags & (1 << CMTP_LOOPBACK)) { - kfree_skb(skb); - return; - } - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_MAPPING, appl); - if (application) { - appl = application->appl; - CAPIMSG_SETAPPID(skb->data, appl); - } else { - BT_ERR("Can't find application with id %d", appl); - kfree_skb(skb); - return; - } - - if ((contr & 0x7f) == 0x01) { - contr = (contr & 0xffffff80) | session->num; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - if (!ctrl) { - BT_ERR("Can't find controller %d for message", session->num); - kfree_skb(skb); - return; - } - - capi_ctr_handle_message(ctrl, appl, skb); -} - -static int cmtp_load_firmware(struct capi_ctr *ctrl, capiloaddata *data) -{ - BT_DBG("ctrl %p data %p", ctrl, data); - - return 0; -} - -static void cmtp_reset_ctr(struct capi_ctr *ctrl) -{ - struct cmtp_session *session = ctrl->driverdata; - - BT_DBG("ctrl %p", ctrl); - - capi_ctr_down(ctrl); - - atomic_inc(&session->terminate); - wake_up_process(session->task); -} - -static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp) -{ - DECLARE_WAITQUEUE(wait, current); - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - unsigned long timeo = CMTP_INTEROP_TIMEOUT; - unsigned char buf[8]; - int err = 0, nconn, want = rp->level3cnt; - - BT_DBG("ctrl %p appl %d level3cnt %d datablkcnt %d datablklen %d", - ctrl, appl, rp->level3cnt, rp->datablkcnt, rp->datablklen); - - application = cmtp_application_add(session, appl); - if (!application) { - BT_ERR("Can't allocate memory for new application"); - return; - } - - if (want < 0) - nconn = ctrl->profile.nbchannel * -want; - else - nconn = want; - - if (nconn == 0) - nconn = ctrl->profile.nbchannel; - - capimsg_setu16(buf, 0, nconn); - capimsg_setu16(buf, 2, rp->datablkcnt); - capimsg_setu16(buf, 4, rp->datablklen); - - application->state = BT_CONFIG; - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, 0x0000, application->msgnum, - CAPI_FUNCTION_REGISTER, buf, 6); - - add_wait_queue(&session->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (application->state == BT_CLOSED) { - err = -application->err; - break; - } - - if (application->state == BT_CONNECTED) - break; - - if (signal_pending(current)) { - err = -EINTR; - break; - } - - timeo = schedule_timeout(timeo); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&session->wait, &wait); - - if (err) { - cmtp_application_del(session, application); - return; - } -} - -static void cmtp_release_appl(struct capi_ctr *ctrl, __u16 appl) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - - BT_DBG("ctrl %p appl %d", ctrl, appl); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if (!application) { - BT_ERR("Can't find application"); - return; - } - - application->msgnum = cmtp_msgnum_get(session); - - cmtp_send_interopmsg(session, CAPI_REQ, application->mapping, application->msgnum, - CAPI_FUNCTION_RELEASE, NULL, 0); - - wait_event_interruptible_timeout(session->wait, - (application->state == BT_CLOSED), CMTP_INTEROP_TIMEOUT); - - cmtp_application_del(session, application); -} - -static u16 cmtp_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) -{ - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *application; - __u16 appl; - __u32 contr; - - BT_DBG("ctrl %p skb %p", ctrl, skb); - - appl = CAPIMSG_APPID(skb->data); - contr = CAPIMSG_CONTROL(skb->data); - - application = cmtp_application_get(session, CMTP_APPLID, appl); - if ((!application) || (application->state != BT_CONNECTED)) { - BT_ERR("Can't find application with id %d", appl); - return CAPI_ILLAPPNR; - } - - CAPIMSG_SETAPPID(skb->data, application->mapping); - - if ((contr & 0x7f) == session->num) { - contr = (contr & 0xffffff80) | 0x01; - CAPIMSG_SETCONTROL(skb->data, contr); - } - - cmtp_send_capimsg(session, skb); - - return CAPI_NOERROR; -} - -static char *cmtp_procinfo(struct capi_ctr *ctrl) -{ - return "CAPI Message Transport Protocol"; -} - -static int cmtp_proc_show(struct seq_file *m, void *v) -{ - struct capi_ctr *ctrl = m->private; - struct cmtp_session *session = ctrl->driverdata; - struct cmtp_application *app; - struct list_head *p, *n; - - seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl)); - seq_printf(m, "addr %s\n", session->name); - seq_printf(m, "ctrl %d\n", session->num); - - list_for_each_safe(p, n, &session->applications) { - app = list_entry(p, struct cmtp_application, list); - seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping); - } - - return 0; -} - -static int cmtp_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, cmtp_proc_show, PDE(inode)->data); -} - -static const struct file_operations cmtp_proc_fops = { - .owner = THIS_MODULE, - .open = cmtp_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -int cmtp_attach_device(struct cmtp_session *session) -{ - unsigned char buf[4]; - long ret; - - BT_DBG("session %p", session); - - capimsg_setu32(buf, 0, 0); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, CMTP_INITIAL_MSGNUM, - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - ret = wait_event_interruptible_timeout(session->wait, - session->ncontroller, CMTP_INTEROP_TIMEOUT); - - BT_INFO("Found %d CAPI controller(s) on device %s", session->ncontroller, session->name); - - if (!ret) - return -ETIMEDOUT; - - if (!session->ncontroller) - return -ENODEV; - - if (session->ncontroller > 1) - BT_INFO("Setting up only CAPI controller 1"); - - session->ctrl.owner = THIS_MODULE; - session->ctrl.driverdata = session; - strcpy(session->ctrl.name, session->name); - - session->ctrl.driver_name = "cmtp"; - session->ctrl.load_firmware = cmtp_load_firmware; - session->ctrl.reset_ctr = cmtp_reset_ctr; - session->ctrl.register_appl = cmtp_register_appl; - session->ctrl.release_appl = cmtp_release_appl; - session->ctrl.send_message = cmtp_send_message; - - session->ctrl.procinfo = cmtp_procinfo; - session->ctrl.proc_fops = &cmtp_proc_fops; - - if (attach_capi_ctr(&session->ctrl) < 0) { - BT_ERR("Can't attach new controller"); - return -EBUSY; - } - - session->num = session->ctrl.cnr; - - BT_DBG("session %p num %d", session, session->num); - - capimsg_setu32(buf, 0, 1); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_MANUFACTURER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_VERSION, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_SERIAL_NUMBER, buf, 4); - - cmtp_send_interopmsg(session, CAPI_REQ, 0xffff, cmtp_msgnum_get(session), - CAPI_FUNCTION_GET_PROFILE, buf, 4); - - return 0; -} - -void cmtp_detach_device(struct cmtp_session *session) -{ - BT_DBG("session %p", session); - - detach_capi_ctr(&session->ctrl); -} diff --git a/net/bluetooth_tizen/cmtp/cmtp.h b/net/bluetooth_tizen/cmtp/cmtp.h deleted file mode 100644 index c32638d..0000000 --- a/net/bluetooth_tizen/cmtp/cmtp.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#ifndef __CMTP_H -#define __CMTP_H - -#include <linux/types.h> -#include <net/bluetooth/bluetooth.h> - -#define BTNAMSIZ 18 - -/* CMTP ioctl defines */ -#define CMTPCONNADD _IOW('C', 200, int) -#define CMTPCONNDEL _IOW('C', 201, int) -#define CMTPGETCONNLIST _IOR('C', 210, int) -#define CMTPGETCONNINFO _IOR('C', 211, int) - -#define CMTP_LOOPBACK 0 - -struct cmtp_connadd_req { - int sock; /* Connected socket */ - __u32 flags; -}; - -struct cmtp_conndel_req { - bdaddr_t bdaddr; - __u32 flags; -}; - -struct cmtp_conninfo { - bdaddr_t bdaddr; - __u32 flags; - __u16 state; - int num; -}; - -struct cmtp_connlist_req { - __u32 cnum; - struct cmtp_conninfo __user *ci; -}; - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock); -int cmtp_del_connection(struct cmtp_conndel_req *req); -int cmtp_get_connlist(struct cmtp_connlist_req *req); -int cmtp_get_conninfo(struct cmtp_conninfo *ci); - -/* CMTP session defines */ -#define CMTP_INTEROP_TIMEOUT (HZ * 5) -#define CMTP_INITIAL_MSGNUM 0xff00 - -struct cmtp_session { - struct list_head list; - - struct socket *sock; - - bdaddr_t bdaddr; - - unsigned long state; - unsigned long flags; - - uint mtu; - - char name[BTNAMSIZ]; - - atomic_t terminate; - struct task_struct *task; - - wait_queue_head_t wait; - - int ncontroller; - int num; - struct capi_ctr ctrl; - - struct list_head applications; - - unsigned long blockids; - int msgnum; - - struct sk_buff_head transmit; - - struct sk_buff *reassembly[16]; -}; - -struct cmtp_application { - struct list_head list; - - unsigned long state; - int err; - - __u16 appl; - __u16 mapping; - - __u16 msgnum; -}; - -struct cmtp_scb { - int id; - int data; -}; - -int cmtp_attach_device(struct cmtp_session *session); -void cmtp_detach_device(struct cmtp_session *session); - -void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb); - -/* CMTP init defines */ -int cmtp_init_sockets(void); -void cmtp_cleanup_sockets(void); - -#endif /* __CMTP_H */ diff --git a/net/bluetooth_tizen/cmtp/core.c b/net/bluetooth_tizen/cmtp/core.c deleted file mode 100644 index 6c9c1fd..0000000 --- a/net/bluetooth_tizen/cmtp/core.c +++ /dev/null @@ -1,500 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/freezer.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/isdn/capilli.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/l2cap.h> - -#include "cmtp.h" - -#define VERSION "1.0" - -static DECLARE_RWSEM(cmtp_session_sem); -static LIST_HEAD(cmtp_session_list); - -static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) -{ - struct cmtp_session *session; - - BT_DBG(""); - - list_for_each_entry(session, &cmtp_session_list, list) - if (!bacmp(bdaddr, &session->bdaddr)) - return session; - - return NULL; -} - -static void __cmtp_link_session(struct cmtp_session *session) -{ - list_add(&session->list, &cmtp_session_list); -} - -static void __cmtp_unlink_session(struct cmtp_session *session) -{ - list_del(&session->list); -} - -static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci) -{ - memset(ci, 0, sizeof(*ci)); - bacpy(&ci->bdaddr, &session->bdaddr); - - ci->flags = session->flags; - ci->state = session->state; - - ci->num = session->num; -} - - -static inline int cmtp_alloc_block_id(struct cmtp_session *session) -{ - int i, id = -1; - - for (i = 0; i < 16; i++) - if (!test_and_set_bit(i, &session->blockids)) { - id = i; - break; - } - - return id; -} - -static inline void cmtp_free_block_id(struct cmtp_session *session, int id) -{ - clear_bit(id, &session->blockids); -} - -static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const unsigned char *buf, int count) -{ - struct sk_buff *skb = session->reassembly[id], *nskb; - int size; - - BT_DBG("session %p buf %p count %d", session, buf, count); - - size = (skb) ? skb->len + count : count; - - nskb = alloc_skb(size, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for CAPI message"); - return; - } - - if (skb && (skb->len > 0)) - skb_copy_from_linear_data(skb, skb_put(nskb, skb->len), skb->len); - - memcpy(skb_put(nskb, count), buf, count); - - session->reassembly[id] = nskb; - - kfree_skb(skb); -} - -static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb) -{ - __u8 hdr, hdrlen, id; - __u16 len; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - while (skb->len > 0) { - hdr = skb->data[0]; - - switch (hdr & 0xc0) { - case 0x40: - hdrlen = 2; - len = skb->data[1]; - break; - case 0x80: - hdrlen = 3; - len = skb->data[1] | (skb->data[2] << 8); - break; - default: - hdrlen = 1; - len = 0; - break; - } - - id = (hdr & 0x3c) >> 2; - - BT_DBG("hdr 0x%02x hdrlen %d len %d id %d", hdr, hdrlen, len, id); - - if (hdrlen + len > skb->len) { - BT_ERR("Wrong size or header information in CMTP frame"); - break; - } - - if (len == 0) { - skb_pull(skb, hdrlen); - continue; - } - - switch (hdr & 0x03) { - case 0x00: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - cmtp_recv_capimsg(session, session->reassembly[id]); - session->reassembly[id] = NULL; - break; - case 0x01: - cmtp_add_msgpart(session, id, skb->data + hdrlen, len); - break; - default: - if (session->reassembly[id] != NULL) - kfree_skb(session->reassembly[id]); - session->reassembly[id] = NULL; - break; - } - - skb_pull(skb, hdrlen + len); - } - - kfree_skb(skb); - return 0; -} - -static int cmtp_send_frame(struct cmtp_session *session, unsigned char *data, int len) -{ - struct socket *sock = session->sock; - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("session %p data %p len %d", session, data, len); - - if (!len) - return 0; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, &iv, 1, len); -} - -static void cmtp_process_transmit(struct cmtp_session *session) -{ - struct sk_buff *skb, *nskb; - unsigned char *hdr; - unsigned int size, tail; - - BT_DBG("session %p", session); - - nskb = alloc_skb(session->mtu, GFP_ATOMIC); - if (!nskb) { - BT_ERR("Can't allocate memory for new frame"); - return; - } - - while ((skb = skb_dequeue(&session->transmit))) { - struct cmtp_scb *scb = (void *) skb->cb; - - tail = session->mtu - nskb->len; - if (tail < 5) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - tail = session->mtu; - } - - size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len); - - if (scb->id < 0) { - scb->id = cmtp_alloc_block_id(session); - if (scb->id < 0) { - skb_queue_head(&session->transmit, skb); - break; - } - } - - if (size < 256) { - hdr = skb_put(nskb, 2); - hdr[0] = 0x40 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size; - } else { - hdr = skb_put(nskb, 3); - hdr[0] = 0x80 - | ((scb->id << 2) & 0x3c) - | ((skb->len == size) ? 0x00 : 0x01); - hdr[1] = size & 0xff; - hdr[2] = size >> 8; - } - - skb_copy_from_linear_data(skb, skb_put(nskb, size), size); - skb_pull(skb, size); - - if (skb->len > 0) { - skb_queue_head(&session->transmit, skb); - } else { - cmtp_free_block_id(session, scb->id); - if (scb->data) { - cmtp_send_frame(session, nskb->data, nskb->len); - skb_trim(nskb, 0); - } - kfree_skb(skb); - } - } - - cmtp_send_frame(session, nskb->data, nskb->len); - - kfree_skb(nskb); -} - -static int cmtp_session(void *arg) -{ - struct cmtp_session *session = arg; - struct sock *sk = session->sock->sk; - struct sk_buff *skb; - wait_queue_t wait; - - BT_DBG("session %p", session); - - set_user_nice(current, -15); - - init_waitqueue_entry(&wait, current); - add_wait_queue(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (atomic_read(&session->terminate)) - break; - if (sk->sk_state != BT_CONNECTED) - break; - - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - cmtp_recv_frame(session, skb); - else - kfree_skb(skb); - } - - cmtp_process_transmit(session); - - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - down_write(&cmtp_session_sem); - - if (!(session->flags & (1 << CMTP_LOOPBACK))) - cmtp_detach_device(session); - - fput(session->sock->file); - - __cmtp_unlink_session(session); - - up_write(&cmtp_session_sem); - - kfree(session); - module_put_and_exit(0); - return 0; -} - -int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) -{ - struct cmtp_session *session, *s; - int i, err; - - BT_DBG(""); - - session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL); - if (!session) - return -ENOMEM; - - down_write(&cmtp_session_sem); - - s = __cmtp_get_session(&bt_sk(sock->sk)->dst); - if (s && s->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; - } - - bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst); - - session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, - l2cap_pi(sock->sk)->chan->imtu); - - BT_DBG("mtu %d", session->mtu); - - sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst)); - - session->sock = sock; - session->state = BT_CONFIG; - - init_waitqueue_head(&session->wait); - - session->msgnum = CMTP_INITIAL_MSGNUM; - - INIT_LIST_HEAD(&session->applications); - - skb_queue_head_init(&session->transmit); - - for (i = 0; i < 16; i++) - session->reassembly[i] = NULL; - - session->flags = req->flags; - - __cmtp_link_session(session); - - __module_get(THIS_MODULE); - session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d", - session->num); - if (IS_ERR(session->task)) { - module_put(THIS_MODULE); - err = PTR_ERR(session->task); - goto unlink; - } - - if (!(session->flags & (1 << CMTP_LOOPBACK))) { - err = cmtp_attach_device(session); - if (err < 0) { - atomic_inc(&session->terminate); - wake_up_process(session->task); - up_write(&cmtp_session_sem); - return err; - } - } - - up_write(&cmtp_session_sem); - return 0; - -unlink: - __cmtp_unlink_session(session); - -failed: - up_write(&cmtp_session_sem); - kfree(session); - return err; -} - -int cmtp_del_connection(struct cmtp_conndel_req *req) -{ - struct cmtp_session *session; - int err = 0; - - BT_DBG(""); - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&req->bdaddr); - if (session) { - /* Flush the transmit queue */ - skb_queue_purge(&session->transmit); - - /* Stop session thread */ - atomic_inc(&session->terminate); - wake_up_process(session->task); - } else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_connlist(struct cmtp_connlist_req *req) -{ - struct cmtp_session *session; - int err = 0, n = 0; - - BT_DBG(""); - - down_read(&cmtp_session_sem); - - list_for_each_entry(session, &cmtp_session_list, list) { - struct cmtp_conninfo ci; - - __cmtp_copy_session(session, &ci); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&cmtp_session_sem); - return err; -} - -int cmtp_get_conninfo(struct cmtp_conninfo *ci) -{ - struct cmtp_session *session; - int err = 0; - - down_read(&cmtp_session_sem); - - session = __cmtp_get_session(&ci->bdaddr); - if (session) - __cmtp_copy_session(session, ci); - else - err = -ENOENT; - - up_read(&cmtp_session_sem); - return err; -} - - -static int __init cmtp_init(void) -{ - BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION); - - cmtp_init_sockets(); - - return 0; -} - -static void __exit cmtp_exit(void) -{ - cmtp_cleanup_sockets(); -} - -module_init(cmtp_init); -module_exit(cmtp_exit); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth CMTP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-5"); diff --git a/net/bluetooth_tizen/cmtp/sock.c b/net/bluetooth_tizen/cmtp/sock.c deleted file mode 100644 index 1230faa..0000000 --- a/net/bluetooth_tizen/cmtp/sock.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - CMTP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002-2003 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/compat.h> -#include <linux/gfp.h> -#include <linux/uaccess.h> -#include <net/sock.h> - -#include <linux/isdn/capilli.h> - -#include <asm/system.h> - -#include "cmtp.h" - -static int cmtp_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct cmtp_connadd_req ca; - struct cmtp_conndel_req cd; - struct cmtp_connlist_req cl; - struct cmtp_conninfo ci; - struct socket *nsock; - void __user *argp = (void __user *)arg; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case CMTPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - nsock = sockfd_lookup(ca.sock, &err); - if (!nsock) - return err; - - if (nsock->sk->sk_state != BT_CONNECTED) { - sockfd_put(nsock); - return -EBADFD; - } - - err = cmtp_add_connection(&ca, nsock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else - sockfd_put(nsock); - - return err; - - case CMTPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return cmtp_del_connection(&cd); - - case CMTPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case CMTPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = cmtp_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - } - - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -static int cmtp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - if (cmd == CMTPGETCONNLIST) { - struct cmtp_connlist_req cl; - u32 uci; - int err; - - if (get_user(cl.cnum, (u32 __user *) arg) || - get_user(uci, (u32 __user *) (arg + 4))) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = cmtp_get_connlist(&cl); - - if (!err && put_user(cl.cnum, (u32 __user *) arg)) - err = -EFAULT; - - return err; - } - - return cmtp_sock_ioctl(sock, cmd, arg); -} -#endif - -static const struct proto_ops cmtp_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = cmtp_sock_release, - .ioctl = cmtp_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = cmtp_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .poll = sock_no_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto cmtp_proto = { - .name = "CMTP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int cmtp_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &cmtp_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &cmtp_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - return 0; -} - -static const struct net_proto_family cmtp_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = cmtp_sock_create -}; - -int cmtp_init_sockets(void) -{ - int err; - - err = proto_register(&cmtp_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_CMTP, &cmtp_sock_family_ops); - if (err < 0) - goto error; - - return 0; - -error: - BT_ERR("Can't register CMTP socket"); - proto_unregister(&cmtp_proto); - return err; -} - -void cmtp_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_CMTP) < 0) - BT_ERR("Can't unregister CMTP socket"); - - proto_unregister(&cmtp_proto); -} diff --git a/net/bluetooth_tizen/hci_conn.c b/net/bluetooth_tizen/hci_conn.c deleted file mode 100644 index 459227d..0000000 --- a/net/bluetooth_tizen/hci_conn.c +++ /dev/null @@ -1,984 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI connection handling. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -static void hci_le_connect(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_create_conn cp; - - conn->state = BT_CONNECT; - conn->out = true; - conn->link_mode |= HCI_LM_MASTER; - conn->sec_level = BT_SECURITY_LOW; - - memset(&cp, 0, sizeof(cp)); - cp.scan_interval = cpu_to_le16(0x0060); - cp.scan_window = cpu_to_le16(0x0030); - bacpy(&cp.peer_addr, &conn->dst); - cp.peer_addr_type = conn->dst_type; - cp.conn_interval_min = cpu_to_le16(0x0028); - cp.conn_interval_max = cpu_to_le16(0x0038); - cp.supervision_timeout = cpu_to_le16(0x002a); - cp.min_ce_len = cpu_to_le16(0x0000); - cp.max_ce_len = cpu_to_le16(0x0000); - - hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); -} - -static void hci_le_connect_cancel(struct hci_conn *conn) -{ - hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); -} - -void hci_acl_connect(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct inquiry_entry *ie; - struct hci_cp_create_conn cp; - - BT_DBG("hcon %p", conn); - - conn->state = BT_CONNECT; - conn->out = true; - - conn->link_mode = HCI_LM_MASTER; - - conn->attempt++; - - conn->link_policy = hdev->link_policy; - - memset(&cp, 0, sizeof(cp)); - bacpy(&cp.bdaddr, &conn->dst); - cp.pscan_rep_mode = 0x02; - - ie = hci_inquiry_cache_lookup(hdev, &conn->dst); - if (ie) { - if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) { - cp.pscan_rep_mode = ie->data.pscan_rep_mode; - cp.pscan_mode = ie->data.pscan_mode; - cp.clock_offset = ie->data.clock_offset | - cpu_to_le16(0x8000); - } - - memcpy(conn->dev_class, ie->data.dev_class, 3); - if (ie->data.ssp_mode > 0) - set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); - } - - cp.pkt_type = cpu_to_le16(conn->pkt_type); - if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) - cp.role_switch = 0x01; - else - cp.role_switch = 0x00; - - hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp); -} - -static void hci_acl_connect_cancel(struct hci_conn *conn) -{ - struct hci_cp_create_conn_cancel cp; - - BT_DBG("%p", conn); - - if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2) - return; - - bacpy(&cp.bdaddr, &conn->dst); - hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp); -} - -void hci_acl_disconn(struct hci_conn *conn, __u8 reason) -{ - struct hci_cp_disconnect cp; - - BT_DBG("%p", conn); - - conn->state = BT_DISCONN; - - cp.handle = cpu_to_le16(conn->handle); - cp.reason = reason; - hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); -} - -void hci_add_sco(struct hci_conn *conn, __u16 handle) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_add_sco cp; - - BT_DBG("%p", conn); - - conn->state = BT_CONNECT; - conn->out = true; - - conn->attempt++; - - cp.handle = cpu_to_le16(handle); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp); -} - -void hci_setup_sync(struct hci_conn *conn, __u16 handle) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_setup_sync_conn cp; - - BT_DBG("%p", conn); - - conn->state = BT_CONNECT; - conn->out = true; - - conn->attempt++; - - cp.handle = cpu_to_le16(handle); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - cp.tx_bandwidth = cpu_to_le32(0x00001f40); - cp.rx_bandwidth = cpu_to_le32(0x00001f40); - cp.max_latency = cpu_to_le16(0xffff); - cp.voice_setting = cpu_to_le16(hdev->voice_setting); - cp.retrans_effort = 0xff; - - hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); -} - -void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, - u16 latency, u16 to_multiplier) -{ - struct hci_cp_le_conn_update cp; - struct hci_dev *hdev = conn->hdev; - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - cp.conn_interval_min = cpu_to_le16(min); - cp.conn_interval_max = cpu_to_le16(max); - cp.conn_latency = cpu_to_le16(latency); - cp.supervision_timeout = cpu_to_le16(to_multiplier); - cp.min_ce_len = cpu_to_le16(0x0001); - cp.max_ce_len = cpu_to_le16(0x0001); - - hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_le_conn_update); - -void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], - __u8 ltk[16]) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_start_enc cp; - - BT_DBG("%p", conn); - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, ltk, sizeof(cp.ltk)); - cp.ediv = ediv; - memcpy(cp.rand, rand, sizeof(cp.rand)); - - hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_le_start_enc); - -void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_ltk_reply cp; - - BT_DBG("%p", conn); - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - memcpy(cp.ltk, ltk, sizeof(ltk)); - - hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); -} -EXPORT_SYMBOL(hci_le_ltk_reply); - -void hci_le_ltk_neg_reply(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_ltk_neg_reply cp; - - BT_DBG("%p", conn); - - memset(&cp, 0, sizeof(cp)); - - cp.handle = cpu_to_le16(conn->handle); - - hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(cp), &cp); -} - -/* Device _must_ be locked */ -void hci_sco_setup(struct hci_conn *conn, __u8 status) -{ - struct hci_conn *sco = conn->link; - - BT_DBG("%p", conn); - - if (!sco) - return; - - if (!status) { - if (lmp_esco_capable(conn->hdev)) - hci_setup_sync(sco, conn->handle); - else - hci_add_sco(sco, conn->handle); - } else { - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } -} - -static void hci_conn_timeout(struct work_struct *work) -{ - struct hci_conn *conn = container_of(work, struct hci_conn, - disc_work.work); - __u8 reason; - - BT_DBG("conn %p state %s", conn, state_to_string(conn->state)); - - if (atomic_read(&conn->refcnt)) - return; - - switch (conn->state) { - case BT_CONNECT: - case BT_CONNECT2: - if (conn->out) { - if (conn->type == ACL_LINK) - hci_acl_connect_cancel(conn); - else if (conn->type == LE_LINK) - hci_le_connect_cancel(conn); - } - break; - case BT_CONFIG: - case BT_CONNECTED: - reason = hci_proto_disconn_ind(conn); - hci_acl_disconn(conn, reason); - break; - default: - conn->state = BT_CLOSED; - break; - } -} - -/* Enter sniff mode */ -static void hci_conn_enter_sniff_mode(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p mode %d", conn, conn->mode); - - if (test_bit(HCI_RAW, &hdev->flags)) - return; - - if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) - return; - - if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) - return; - - if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { - struct hci_cp_sniff_subrate cp; - cp.handle = cpu_to_le16(conn->handle); - cp.max_latency = cpu_to_le16(0); - cp.min_remote_timeout = cpu_to_le16(0); - cp.min_local_timeout = cpu_to_le16(0); - hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp); - } - - if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { - struct hci_cp_sniff_mode cp; - cp.handle = cpu_to_le16(conn->handle); - cp.max_interval = cpu_to_le16(hdev->sniff_max_interval); - cp.min_interval = cpu_to_le16(hdev->sniff_min_interval); - cp.attempt = cpu_to_le16(4); - cp.timeout = cpu_to_le16(1); - hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp); - } -} - -static void hci_conn_idle(unsigned long arg) -{ - struct hci_conn *conn = (void *) arg; - - BT_DBG("conn %p mode %d", conn, conn->mode); - - hci_conn_enter_sniff_mode(conn); -} - -static void hci_conn_auto_accept(unsigned long arg) -{ - struct hci_conn *conn = (void *) arg; - struct hci_dev *hdev = conn->hdev; - - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), - &conn->dst); -} - -struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) -{ - struct hci_conn *conn; - - BT_DBG("%s dst %s", hdev->name, batostr(dst)); - - conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL); - if (!conn) - return NULL; - - bacpy(&conn->dst, dst); - conn->hdev = hdev; - conn->type = type; - conn->mode = HCI_CM_ACTIVE; - conn->state = BT_OPEN; - conn->auth_type = HCI_AT_GENERAL_BONDING; - conn->io_capability = hdev->io_capability; - conn->remote_auth = 0xff; - conn->key_type = 0xff; - - set_bit(HCI_CONN_POWER_SAVE, &conn->flags); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - - switch (type) { - case ACL_LINK: - conn->pkt_type = hdev->pkt_type & ACL_PTYPE_MASK; - break; - case SCO_LINK: - if (lmp_esco_capable(hdev)) - conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | - (hdev->esco_type & EDR_ESCO_MASK); - else - conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK; - break; - case ESCO_LINK: - conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK; - break; - } - - skb_queue_head_init(&conn->data_q); - - INIT_LIST_HEAD(&conn->chan_list); - - INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); - setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); - setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, - (unsigned long) conn); - - atomic_set(&conn->refcnt, 0); - - hci_dev_hold(hdev); - - hci_conn_hash_add(hdev, conn); - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); - - atomic_set(&conn->devref, 0); - - hci_conn_init_sysfs(conn); - - return conn; -} - -int hci_conn_del(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); - - del_timer(&conn->idle_timer); - - cancel_delayed_work_sync(&conn->disc_work); - - del_timer(&conn->auto_accept_timer); - - if (conn->type == ACL_LINK) { - struct hci_conn *sco = conn->link; - if (sco) - sco->link = NULL; - - /* Unacked frames */ - hdev->acl_cnt += conn->sent; - } else if (conn->type == LE_LINK) { - if (hdev->le_pkts) - hdev->le_cnt += conn->sent; - else - hdev->acl_cnt += conn->sent; - } else { - struct hci_conn *acl = conn->link; - if (acl) { - acl->link = NULL; - hci_conn_put(acl); - } - } - - - hci_chan_list_flush(conn); - - hci_conn_hash_del(hdev, conn); - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); - - skb_queue_purge(&conn->data_q); - - hci_conn_put_device(conn); - - hci_dev_put(hdev); - - if (conn->handle == 0) - kfree(conn); - - return 0; -} - -struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) -{ - int use_src = bacmp(src, BDADDR_ANY); - struct hci_dev *hdev = NULL, *d; - - BT_DBG("%s -> %s", batostr(src), batostr(dst)); - - read_lock(&hci_dev_list_lock); - - list_for_each_entry(d, &hci_dev_list, list) { - if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags)) - continue; - - /* Simple routing: - * No source address - find interface with bdaddr != dst - * Source address - find interface with bdaddr == src - */ - - if (use_src) { - if (!bacmp(&d->bdaddr, src)) { - hdev = d; break; - } - } else { - if (bacmp(&d->bdaddr, dst)) { - hdev = d; break; - } - } - } - - if (hdev) - hdev = hci_dev_hold(hdev); - - read_unlock(&hci_dev_list_lock); - return hdev; -} -EXPORT_SYMBOL(hci_get_route); - -/* Create SCO, ACL or LE connection. - * Device _must_ be locked */ -struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type) -{ - struct hci_conn *acl; - struct hci_conn *sco; - struct hci_conn *le; - - BT_DBG("%s dst %s", hdev->name, batostr(dst)); - - if (type == LE_LINK) { - struct adv_entry *entry; - - le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); - if (le) - return ERR_PTR(-EBUSY); - - entry = hci_find_adv_entry(hdev, dst); - if (!entry) - return ERR_PTR(-EHOSTUNREACH); - - le = hci_conn_add(hdev, LE_LINK, dst); - if (!le) - return ERR_PTR(-ENOMEM); - - le->dst_type = entry->bdaddr_type; - - hci_le_connect(le); - - hci_conn_hold(le); - - return le; - } - - acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (!acl) { - acl = hci_conn_add(hdev, ACL_LINK, dst); - if (!acl) - return ERR_PTR(-ENOMEM); - } - - hci_conn_hold(acl); - - if (acl->state == BT_OPEN || acl->state == BT_CLOSED) { - acl->sec_level = BT_SECURITY_LOW; - acl->pending_sec_level = sec_level; - acl->auth_type = auth_type; - hci_acl_connect(acl); - } - - if (type == ACL_LINK) - return acl; - - sco = hci_conn_hash_lookup_ba(hdev, type, dst); - if (!sco) { - sco = hci_conn_add(hdev, type, dst); - if (!sco) { - hci_conn_put(acl); - return ERR_PTR(-ENOMEM); - } - } - - acl->link = sco; - sco->link = acl; - - hci_conn_hold(sco); - - if (acl->state == BT_CONNECTED && - (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { - set_bit(HCI_CONN_POWER_SAVE, &acl->flags); - hci_conn_enter_active_mode(acl, BT_POWER_FORCE_ACTIVE_ON); - - if (test_bit(HCI_CONN_MODE_CHANGE_PEND, &acl->flags)) { - /* defer SCO setup until mode change completed */ - set_bit(HCI_CONN_SCO_SETUP_PEND, &acl->flags); - return sco; - } - - hci_sco_setup(acl, 0x00); - } - - return sco; -} -EXPORT_SYMBOL(hci_connect); - -/* Check link security requirement */ -int hci_conn_check_link_mode(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - - if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) - return 0; - - return 1; -} -EXPORT_SYMBOL(hci_conn_check_link_mode); - -/* Authenticate remote device */ -static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) -{ - BT_DBG("conn %p", conn); - - if (conn->pending_sec_level > sec_level) - sec_level = conn->pending_sec_level; - - if (sec_level > conn->sec_level) - conn->pending_sec_level = sec_level; - else if (conn->link_mode & HCI_LM_AUTH) - return 1; - - /* Make sure we preserve an existing MITM requirement*/ - auth_type |= (conn->auth_type & 0x01); - - conn->auth_type = auth_type; - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - - /* encrypt must be pending if auth is also pending */ - set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); - - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, - sizeof(cp), &cp); - if (conn->key_type != 0xff) - set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); - } - - return 0; -} - -/* Encrypt the the link */ -static void hci_conn_encrypt(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - - if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = cpu_to_le16(conn->handle); - cp.encrypt = 0x01; - hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), - &cp); - } -} - -/* Enable security */ -int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) -{ - BT_DBG("conn %p", conn); - - /* For sdp we don't need the link key. */ - if (sec_level == BT_SECURITY_SDP) - return 1; - - /* For non 2.1 devices and low security level we don't need the link - key. */ - if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn)) - return 1; - - /* For other security levels we need the link key. */ - if (!(conn->link_mode & HCI_LM_AUTH)) - goto auth; - - /* An authenticated combination key has sufficient security for any - security level. */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION) - goto encrypt; - - /* An unauthenticated combination key has sufficient security for - security level 1 and 2. */ - if (conn->key_type == HCI_LK_UNAUTH_COMBINATION && - (sec_level == BT_SECURITY_MEDIUM || - sec_level == BT_SECURITY_LOW)) - goto encrypt; - - /* A combination key has always sufficient security for the security - levels 1 or 2. High security level requires the combination key - is generated using maximum PIN code length (16). - For pre 2.1 units. */ - if (conn->key_type == HCI_LK_COMBINATION && - (sec_level != BT_SECURITY_HIGH || - conn->pin_length == 16)) - goto encrypt; - -auth: - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) - return 0; - - if (!hci_conn_auth(conn, sec_level, auth_type)) - return 0; - -encrypt: - if (conn->link_mode & HCI_LM_ENCRYPT) - return 1; - - hci_conn_encrypt(conn); - return 0; -} -EXPORT_SYMBOL(hci_conn_security); - -/* Check secure link requirement */ -int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) -{ - BT_DBG("conn %p", conn); - - if (sec_level != BT_SECURITY_HIGH) - return 1; /* Accept if non-secure is required */ - - if (conn->sec_level == BT_SECURITY_HIGH) - return 1; - - return 0; /* Reject not secure link */ -} -EXPORT_SYMBOL(hci_conn_check_secure); - -/* Change link key */ -int hci_conn_change_link_key(struct hci_conn *conn) -{ - BT_DBG("conn %p", conn); - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_change_conn_link_key cp; - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, - sizeof(cp), &cp); - } - - return 0; -} -EXPORT_SYMBOL(hci_conn_change_link_key); - -/* Switch role */ -int hci_conn_switch_role(struct hci_conn *conn, __u8 role) -{ - BT_DBG("conn %p", conn); - - if (!role && conn->link_mode & HCI_LM_MASTER) - return 1; - - if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { - struct hci_cp_switch_role cp; - bacpy(&cp.bdaddr, &conn->dst); - cp.role = role; - hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); - } - - return 0; -} -EXPORT_SYMBOL(hci_conn_switch_role); - -/* Enter active mode */ -void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p mode %d", conn, conn->mode); - - if (test_bit(HCI_RAW, &hdev->flags)) - return; - - if (conn->mode != HCI_CM_SNIFF) - goto timer; - - if (!test_bit(HCI_CONN_POWER_SAVE, &conn->flags) && !force_active) - goto timer; - - if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { - struct hci_cp_exit_sniff_mode cp; - cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp); - } - -timer: - if (hdev->idle_timeout > 0) - mod_timer(&conn->idle_timer, - jiffies + msecs_to_jiffies(hdev->idle_timeout)); -} - -/* Drop all connection on the device */ -void hci_conn_hash_flush(struct hci_dev *hdev) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *c, *n; - - BT_DBG("hdev %s", hdev->name); - - list_for_each_entry_safe(c, n, &h->list, list) { - c->state = BT_CLOSED; - - hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); - hci_conn_del(c); - } -} - -/* Check pending connect attempts */ -void hci_conn_check_pending(struct hci_dev *hdev) -{ - struct hci_conn *conn; - - BT_DBG("hdev %s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); - if (conn) - hci_acl_connect(conn); - - hci_dev_unlock(hdev); -} - -void hci_conn_hold_device(struct hci_conn *conn) -{ - atomic_inc(&conn->devref); -} -EXPORT_SYMBOL(hci_conn_hold_device); - -void hci_conn_put_device(struct hci_conn *conn) -{ - if (atomic_dec_and_test(&conn->devref)) - hci_conn_del_sysfs(conn); -} -EXPORT_SYMBOL(hci_conn_put_device); - -int hci_get_conn_list(void __user *arg) -{ - register struct hci_conn *c; - struct hci_conn_list_req req, *cl; - struct hci_conn_info *ci; - struct hci_dev *hdev; - int n = 0, size, err; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - if (!req.conn_num || req.conn_num > (PAGE_SIZE * 2) / sizeof(*ci)) - return -EINVAL; - - size = sizeof(req) + req.conn_num * sizeof(*ci); - - cl = kmalloc(size, GFP_KERNEL); - if (!cl) - return -ENOMEM; - - hdev = hci_dev_get(req.dev_id); - if (!hdev) { - kfree(cl); - return -ENODEV; - } - - ci = cl->conn_info; - - hci_dev_lock(hdev); - list_for_each_entry(c, &hdev->conn_hash.list, list) { - bacpy(&(ci + n)->bdaddr, &c->dst); - (ci + n)->handle = c->handle; - (ci + n)->type = c->type; - (ci + n)->out = c->out; - (ci + n)->state = c->state; - (ci + n)->link_mode = c->link_mode; - if (++n >= req.conn_num) - break; - } - hci_dev_unlock(hdev); - - cl->dev_id = hdev->id; - cl->conn_num = n; - size = sizeof(req) + n * sizeof(*ci); - - hci_dev_put(hdev); - - err = copy_to_user(arg, cl, size); - kfree(cl); - - return err ? -EFAULT : 0; -} - -int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) -{ - struct hci_conn_info_req req; - struct hci_conn_info ci; - struct hci_conn *conn; - char __user *ptr = arg + sizeof(req); - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, req.type, &req.bdaddr); - if (conn) { - bacpy(&ci.bdaddr, &conn->dst); - ci.handle = conn->handle; - ci.type = conn->type; - ci.out = conn->out; - ci.state = conn->state; - ci.link_mode = conn->link_mode; - } - hci_dev_unlock(hdev); - - if (!conn) - return -ENOENT; - - return copy_to_user(ptr, &ci, sizeof(ci)) ? -EFAULT : 0; -} - -int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) -{ - struct hci_auth_info_req req; - struct hci_conn *conn; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &req.bdaddr); - if (conn) - req.type = conn->auth_type; - hci_dev_unlock(hdev); - - if (!conn) - return -ENOENT; - - return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; -} - -struct hci_chan *hci_chan_create(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_chan *chan; - - BT_DBG("%s conn %p", hdev->name, conn); - - chan = kzalloc(sizeof(struct hci_chan), GFP_KERNEL); - if (!chan) - return NULL; - - chan->conn = conn; - skb_queue_head_init(&chan->data_q); - - list_add_rcu(&chan->list, &conn->chan_list); - - return chan; -} - -int hci_chan_del(struct hci_chan *chan) -{ - struct hci_conn *conn = chan->conn; - struct hci_dev *hdev = conn->hdev; - - BT_DBG("%s conn %p chan %p", hdev->name, conn, chan); - - list_del_rcu(&chan->list); - - synchronize_rcu(); - - skb_queue_purge(&chan->data_q); - kfree(chan); - - return 0; -} - -void hci_chan_list_flush(struct hci_conn *conn) -{ - struct hci_chan *chan, *n; - - BT_DBG("conn %p", conn); - - list_for_each_entry_safe(chan, n, &conn->chan_list, list) - hci_chan_del(chan); -} diff --git a/net/bluetooth_tizen/hci_core.c b/net/bluetooth_tizen/hci_core.c deleted file mode 100644 index 1c69c54..0000000 --- a/net/bluetooth_tizen/hci_core.c +++ /dev/null @@ -1,2966 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2011 ProFUSION Embedded Systems - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI core. */ - -#include <linux/jiffies.h> -#include <linux/module.h> -#include <linux/kmod.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/workqueue.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <linux/rfkill.h> -#include <linux/timer.h> -#include <linux/crypto.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -#define AUTO_OFF_TIMEOUT 2000 - -static void hci_rx_work(struct work_struct *work); -static void hci_cmd_work(struct work_struct *work); -static void hci_tx_work(struct work_struct *work); - -/* HCI device list */ -LIST_HEAD(hci_dev_list); -DEFINE_RWLOCK(hci_dev_list_lock); - -/* HCI callback list */ -LIST_HEAD(hci_cb_list); -DEFINE_RWLOCK(hci_cb_list_lock); - -/* HCI notifiers list */ -static ATOMIC_NOTIFIER_HEAD(hci_notifier); - -/* ---- HCI notifications ---- */ - -int hci_register_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&hci_notifier, nb); -} - -int hci_unregister_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_unregister(&hci_notifier, nb); -} - -static void hci_notify(struct hci_dev *hdev, int event) -{ - atomic_notifier_call_chain(&hci_notifier, event, hdev); -} - -/* ---- HCI requests ---- */ - -void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) -{ - BT_DBG("%s command 0x%04x result 0x%2.2x", hdev->name, cmd, result); - - /* If this is the init phase check if the completed command matches - * the last init command, and if not just return. - */ - if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) { - struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; - struct sk_buff *skb; - - /* Some CSR based controllers generate a spontaneous - * reset complete event during init and any pending - * command will never be completed. In such a case we - * need to resend whatever was the last sent - * command. - */ - - if (cmd != HCI_OP_RESET || sent->opcode == HCI_OP_RESET) - return; - - skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC); - if (skb) { - skb_queue_head(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - - return; - } - - if (hdev->req_status == HCI_REQ_PEND) { - hdev->req_result = result; - hdev->req_status = HCI_REQ_DONE; - wake_up_interruptible(&hdev->req_wait_q); - } -} - -static void hci_req_cancel(struct hci_dev *hdev, int err) -{ - BT_DBG("%s err 0x%2.2x", hdev->name, err); - - if (hdev->req_status == HCI_REQ_PEND) { - hdev->req_result = err; - hdev->req_status = HCI_REQ_CANCELED; - wake_up_interruptible(&hdev->req_wait_q); - } -} - -/* Execute request and wait for completion. */ -static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), - unsigned long opt, __u32 timeout) -{ - DECLARE_WAITQUEUE(wait, current); - int err = 0; - - BT_DBG("%s start", hdev->name); - - hdev->req_status = HCI_REQ_PEND; - - add_wait_queue(&hdev->req_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - req(hdev, opt); - schedule_timeout(timeout); - - remove_wait_queue(&hdev->req_wait_q, &wait); - - if (signal_pending(current)) - return -EINTR; - - switch (hdev->req_status) { - case HCI_REQ_DONE: - err = -bt_to_errno(hdev->req_result); - break; - - case HCI_REQ_CANCELED: - err = -hdev->req_result; - break; - - default: - err = -ETIMEDOUT; - break; - } - - hdev->req_status = hdev->req_result = 0; - - BT_DBG("%s end: err %d", hdev->name, err); - - return err; -} - -static inline int hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), - unsigned long opt, __u32 timeout) -{ - int ret; - - if (!test_bit(HCI_UP, &hdev->flags)) - return -ENETDOWN; - - /* Serialize all requests */ - hci_req_lock(hdev); - ret = __hci_request(hdev, req, opt, timeout); - hci_req_unlock(hdev); - - return ret; -} - -static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) -{ - BT_DBG("%s %ld", hdev->name, opt); - - /* Reset device */ - set_bit(HCI_RESET, &hdev->flags); - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); -} - -static void bredr_init(struct hci_dev *hdev) -{ - struct hci_cp_delete_stored_link_key cp; - __le16 param; - __u8 flt_type; - - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; - - /* Mandatory initialization */ - - /* Reset */ - if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { - set_bit(HCI_RESET, &hdev->flags); - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); - } - - /* Read Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); - - /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); - - /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); - - /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); - - /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); - - /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); - - /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); - - /* Optional initialization */ - - /* Clear Event Filters */ - flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); - - /* Connection accept timeout ~20 secs */ - param = cpu_to_le16(0x7d00); - hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - - bacpy(&cp.bdaddr, BDADDR_ANY); - cp.delete_all = 1; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); -} - -static void amp_init(struct hci_dev *hdev) -{ - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; - - /* Reset */ - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); - - /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); -} - -static void hci_init_req(struct hci_dev *hdev, unsigned long opt) -{ - struct sk_buff *skb; - - BT_DBG("%s %ld", hdev->name, opt); - - /* Driver initialization */ - - /* Special commands */ - while ((skb = skb_dequeue(&hdev->driver_init))) { - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - skb->dev = (void *) hdev; - - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - skb_queue_purge(&hdev->driver_init); - - switch (hdev->dev_type) { - case HCI_BREDR: - bredr_init(hdev); - break; - - case HCI_AMP: - amp_init(hdev); - break; - - default: - BT_ERR("Unknown device type %d", hdev->dev_type); - break; - } - -} - -static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt) -{ - BT_DBG("%s", hdev->name); - - /* Read LE buffer size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); -} - -static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) -{ - __u8 scan = opt; - - BT_DBG("%s %x", hdev->name, scan); - - /* Inquiry and Page scans */ - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); -} - -static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) -{ - __u8 auth = opt; - - BT_DBG("%s %x", hdev->name, auth); - - /* Authentication */ - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); -} - -static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) -{ - __u8 encrypt = opt; - - BT_DBG("%s %x", hdev->name, encrypt); - - /* Encryption */ - hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); -} - -static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) -{ - __le16 policy = cpu_to_le16(opt); - - BT_DBG("%s %x", hdev->name, policy); - - /* Default link policy */ - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); -} - -/* Get HCI device by index. - * Device is held on return. */ -struct hci_dev *hci_dev_get(int index) -{ - struct hci_dev *hdev = NULL, *d; - - BT_DBG("%d", index); - - if (index < 0) - return NULL; - - read_lock(&hci_dev_list_lock); - list_for_each_entry(d, &hci_dev_list, list) { - if (d->id == index) { - hdev = hci_dev_hold(d); - break; - } - } - read_unlock(&hci_dev_list_lock); - return hdev; -} - -/* ---- Inquiry support ---- */ - -bool hci_discovery_active(struct hci_dev *hdev) -{ - struct discovery_state *discov = &hdev->discovery; - - switch (discov->state) { - case DISCOVERY_FINDING: - case DISCOVERY_RESOLVING: - return true; - - default: - return false; - } -} - -void hci_discovery_set_state(struct hci_dev *hdev, int state) -{ - BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state); - - if (hdev->discovery.state == state) - return; - - switch (state) { - case DISCOVERY_STOPPED: - if (hdev->discovery.state != DISCOVERY_STARTING) - mgmt_discovering(hdev, 0); - hdev->discovery.type = 0; - break; - case DISCOVERY_STARTING: - break; - case DISCOVERY_FINDING: - mgmt_discovering(hdev, 1); - break; - case DISCOVERY_RESOLVING: - break; - case DISCOVERY_STOPPING: - break; - } - - hdev->discovery.state = state; -} - -static void inquiry_cache_flush(struct hci_dev *hdev) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *p, *n; - - list_for_each_entry_safe(p, n, &cache->all, all) { - list_del(&p->all); - kfree(p); - } - - INIT_LIST_HEAD(&cache->unknown); - INIT_LIST_HEAD(&cache->resolve); -} - -struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); - - list_for_each_entry(e, &cache->all, all) { - if (!bacmp(&e->data.bdaddr, bdaddr)) - return e; - } - - return NULL; -} - -struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev, - bdaddr_t *bdaddr) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("cache %p, %s", cache, batostr(bdaddr)); - - list_for_each_entry(e, &cache->unknown, list) { - if (!bacmp(&e->data.bdaddr, bdaddr)) - return e; - } - - return NULL; -} - -struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, - bdaddr_t *bdaddr, - int state) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state); - - list_for_each_entry(e, &cache->resolve, list) { - if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state) - return e; - if (!bacmp(&e->data.bdaddr, bdaddr)) - return e; - } - - return NULL; -} - -void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, - struct inquiry_entry *ie) -{ - struct discovery_state *cache = &hdev->discovery; - struct list_head *pos = &cache->resolve; - struct inquiry_entry *p; - - list_del(&ie->list); - - list_for_each_entry(p, &cache->resolve, list) { - if (p->name_state != NAME_PENDING && - abs(p->data.rssi) >= abs(ie->data.rssi)) - break; - pos = &p->list; - } - - list_add(&ie->list, pos); -} - -bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *ie; - - BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr)); - - if (ssp) - *ssp = data->ssp_mode; - - ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); - if (ie) { - if (ie->data.ssp_mode && ssp) - *ssp = true; - - if (ie->name_state == NAME_NEEDED && - data->rssi != ie->data.rssi) { - ie->data.rssi = data->rssi; - hci_inquiry_cache_update_resolve(hdev, ie); - } - - goto update; - } - - /* Entry not in the cache. Add new one. */ - ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); - if (!ie) - return false; - - list_add(&ie->all, &cache->all); - - if (name_known) { - ie->name_state = NAME_KNOWN; - } else { - ie->name_state = NAME_NOT_KNOWN; - list_add(&ie->list, &cache->unknown); - } - -update: - if (name_known && ie->name_state != NAME_KNOWN && - ie->name_state != NAME_PENDING) { - ie->name_state = NAME_KNOWN; - list_del(&ie->list); - } - - memcpy(&ie->data, data, sizeof(*data)); - ie->timestamp = jiffies; - cache->timestamp = jiffies; - - if (ie->name_state == NAME_NOT_KNOWN) - return false; - - return true; -} - -static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) -{ - struct discovery_state *cache = &hdev->discovery; - struct inquiry_info *info = (struct inquiry_info *) buf; - struct inquiry_entry *e; - int copied = 0; - - list_for_each_entry(e, &cache->all, all) { - struct inquiry_data *data = &e->data; - - if (copied >= num) - break; - - bacpy(&info->bdaddr, &data->bdaddr); - info->pscan_rep_mode = data->pscan_rep_mode; - info->pscan_period_mode = data->pscan_period_mode; - info->pscan_mode = data->pscan_mode; - memcpy(info->dev_class, data->dev_class, 3); - info->clock_offset = data->clock_offset; - - info++; - copied++; - } - - BT_DBG("cache %p, copied %d", cache, copied); - return copied; -} - -static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) -{ - struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt; - struct hci_cp_inquiry cp; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_INQUIRY, &hdev->flags)) - return; - - /* Start Inquiry */ - memcpy(&cp.lap, &ir->lap, 3); - cp.length = ir->length; - cp.num_rsp = ir->num_rsp; - hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); -} - -int hci_inquiry(void __user *arg) -{ - __u8 __user *ptr = arg; - struct hci_inquiry_req ir; - struct hci_dev *hdev; - int err = 0, do_inquiry = 0, max_rsp; - long timeo; - __u8 *buf; - - if (copy_from_user(&ir, ptr, sizeof(ir))) - return -EFAULT; - - hdev = hci_dev_get(ir.dev_id); - if (!hdev) - return -ENODEV; - - hci_dev_lock(hdev); - if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || - inquiry_cache_empty(hdev) || - ir.flags & IREQ_CACHE_FLUSH) { - inquiry_cache_flush(hdev); - do_inquiry = 1; - } - hci_dev_unlock(hdev); - - timeo = ir.length * msecs_to_jiffies(2000); - - if (do_inquiry) { - err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo); - if (err < 0) - goto done; - } - - /* for unlimited number of responses we will use buffer with 255 entries */ - max_rsp = (ir.num_rsp == 0) ? 255 : ir.num_rsp; - - /* cache_dump can't sleep. Therefore we allocate temp buffer and then - * copy it to the user space. - */ - buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL); - if (!buf) { - err = -ENOMEM; - goto done; - } - - hci_dev_lock(hdev); - ir.num_rsp = inquiry_cache_dump(hdev, max_rsp, buf); - hci_dev_unlock(hdev); - - BT_DBG("num_rsp %d", ir.num_rsp); - - if (!copy_to_user(ptr, &ir, sizeof(ir))) { - ptr += sizeof(ir); - if (copy_to_user(ptr, buf, sizeof(struct inquiry_info) * - ir.num_rsp)) - err = -EFAULT; - } else - err = -EFAULT; - - kfree(buf); - -done: - hci_dev_put(hdev); - return err; -} - -/* ---- HCI ioctl helpers ---- */ - -int hci_dev_open(__u16 dev) -{ - struct hci_dev *hdev; - int ret = 0; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - BT_DBG("%s %p", hdev->name, hdev); - - hci_req_lock(hdev); - - if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) { - ret = -ENODEV; - goto done; - } - - if (hdev->rfkill && rfkill_blocked(hdev->rfkill)) { - ret = -ERFKILL; - goto done; - } - - if (test_bit(HCI_UP, &hdev->flags)) { - ret = -EALREADY; - goto done; - } - - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - set_bit(HCI_RAW, &hdev->flags); - - /* Treat all non BR/EDR controllers as raw devices if - enable_hs is not set */ - if (hdev->dev_type != HCI_BREDR && !enable_hs) - set_bit(HCI_RAW, &hdev->flags); - - if (hdev->open(hdev)) { - ret = -EIO; - goto done; - } - - if (!test_bit(HCI_RAW, &hdev->flags)) { - atomic_set(&hdev->cmd_cnt, 1); - set_bit(HCI_INIT, &hdev->flags); - hdev->init_last_cmd = 0; - - ret = __hci_request(hdev, hci_init_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - - if (lmp_host_le_capable(hdev)) - ret = __hci_request(hdev, hci_le_init_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - - clear_bit(HCI_INIT, &hdev->flags); - } - - if (!ret) { - hci_dev_hold(hdev); - set_bit(HCI_UP, &hdev->flags); - hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { - hci_dev_lock(hdev); - mgmt_powered(hdev, 1); - hci_dev_unlock(hdev); - } - } else { - /* Init failed, cleanup */ - flush_work(&hdev->tx_work); - flush_work(&hdev->cmd_work); - flush_work(&hdev->rx_work); - - skb_queue_purge(&hdev->cmd_q); - skb_queue_purge(&hdev->rx_q); - - if (hdev->flush) - hdev->flush(hdev); - - if (hdev->sent_cmd) { - kfree_skb(hdev->sent_cmd); - hdev->sent_cmd = NULL; - } - - hdev->close(hdev); - hdev->flags = 0; - } - -done: - hci_req_unlock(hdev); - hci_dev_put(hdev); - return ret; -} - -static int hci_dev_do_close(struct hci_dev *hdev) -{ - BT_DBG("%s %p", hdev->name, hdev); - - cancel_work_sync(&hdev->le_scan); - - hci_req_cancel(hdev, ENODEV); - hci_req_lock(hdev); - - if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { - del_timer_sync(&hdev->cmd_timer); - hci_req_unlock(hdev); - return 0; - } - - /* Flush RX and TX works */ - flush_work(&hdev->tx_work); - flush_work(&hdev->rx_work); - - if (hdev->discov_timeout > 0) { - cancel_delayed_work(&hdev->discov_off); - hdev->discov_timeout = 0; - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - cancel_delayed_work(&hdev->service_cache); - - cancel_delayed_work_sync(&hdev->le_scan_disable); - - hci_dev_lock(hdev); - inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); - hci_dev_unlock(hdev); - - hci_notify(hdev, HCI_DEV_DOWN); - - if (hdev->flush) - hdev->flush(hdev); - - /* Reset device */ - skb_queue_purge(&hdev->cmd_q); - atomic_set(&hdev->cmd_cnt, 1); - if (!test_bit(HCI_RAW, &hdev->flags) && - test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) { - set_bit(HCI_INIT, &hdev->flags); - __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(250)); - clear_bit(HCI_INIT, &hdev->flags); - } - - /* flush cmd work */ - flush_work(&hdev->cmd_work); - - /* Drop queues */ - skb_queue_purge(&hdev->rx_q); - skb_queue_purge(&hdev->cmd_q); - skb_queue_purge(&hdev->raw_q); - - /* Drop last sent command */ - if (hdev->sent_cmd) { - del_timer_sync(&hdev->cmd_timer); - kfree_skb(hdev->sent_cmd); - hdev->sent_cmd = NULL; - } - - /* After this point our queues are empty - * and no tasks are scheduled. */ - hdev->close(hdev); - - if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { - hci_dev_lock(hdev); - mgmt_powered(hdev, 0); - hci_dev_unlock(hdev); - } - - /* Clear flags */ - hdev->flags = 0; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); - - hci_req_unlock(hdev); - - hci_dev_put(hdev); - return 0; -} - -int hci_dev_close(__u16 dev) -{ - struct hci_dev *hdev; - int err; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work(&hdev->power_off); - - err = hci_dev_do_close(hdev); - - hci_dev_put(hdev); - return err; -} - -int hci_dev_reset(__u16 dev) -{ - struct hci_dev *hdev; - int ret = 0; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - hci_req_lock(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) - goto done; - - /* Drop queues */ - skb_queue_purge(&hdev->rx_q); - skb_queue_purge(&hdev->cmd_q); - - hci_dev_lock(hdev); - inquiry_cache_flush(hdev); - hci_conn_hash_flush(hdev); - hci_dev_unlock(hdev); - - if (hdev->flush) - hdev->flush(hdev); - - atomic_set(&hdev->cmd_cnt, 1); - hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; - - if (!test_bit(HCI_RAW, &hdev->flags)) - ret = __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - -done: - hci_req_unlock(hdev); - hci_dev_put(hdev); - return ret; -} - -int hci_dev_reset_stat(__u16 dev) -{ - struct hci_dev *hdev; - int ret = 0; - - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - - memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); - - hci_dev_put(hdev); - - return ret; -} - -int hci_dev_cmd(unsigned int cmd, void __user *arg) -{ - struct hci_dev *hdev; - struct hci_dev_req dr; - int err = 0; - - if (copy_from_user(&dr, arg, sizeof(dr))) - return -EFAULT; - - hdev = hci_dev_get(dr.dev_id); - if (!hdev) - return -ENODEV; - - switch (cmd) { - case HCISETAUTH: - err = hci_request(hdev, hci_auth_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETENCRYPT: - if (!lmp_encrypt_capable(hdev)) { - err = -EOPNOTSUPP; - break; - } - - if (!test_bit(HCI_AUTH, &hdev->flags)) { - /* Auth must be enabled first */ - err = hci_request(hdev, hci_auth_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - if (err) - break; - } - - err = hci_request(hdev, hci_encrypt_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETSCAN: - err = hci_request(hdev, hci_scan_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETLINKPOL: - err = hci_request(hdev, hci_linkpol_req, dr.dev_opt, - msecs_to_jiffies(HCI_INIT_TIMEOUT)); - break; - - case HCISETLINKMODE: - hdev->link_mode = ((__u16) dr.dev_opt) & - (HCI_LM_MASTER | HCI_LM_ACCEPT); - break; - - case HCISETPTYPE: - hdev->pkt_type = (__u16) dr.dev_opt; - break; - - case HCISETACLMTU: - hdev->acl_mtu = *((__u16 *) &dr.dev_opt + 1); - hdev->acl_pkts = *((__u16 *) &dr.dev_opt + 0); - break; - - case HCISETSCOMTU: - hdev->sco_mtu = *((__u16 *) &dr.dev_opt + 1); - hdev->sco_pkts = *((__u16 *) &dr.dev_opt + 0); - break; - - default: - err = -EINVAL; - break; - } - - hci_dev_put(hdev); - return err; -} - -int hci_get_dev_list(void __user *arg) -{ - struct hci_dev *hdev; - struct hci_dev_list_req *dl; - struct hci_dev_req *dr; - int n = 0, size, err; - __u16 dev_num; - - if (get_user(dev_num, (__u16 __user *) arg)) - return -EFAULT; - - if (!dev_num || dev_num > (PAGE_SIZE * 2) / sizeof(*dr)) - return -EINVAL; - - size = sizeof(*dl) + dev_num * sizeof(*dr); - - dl = kzalloc(size, GFP_KERNEL); - if (!dl) - return -ENOMEM; - - dr = dl->dev_req; - - read_lock(&hci_dev_list_lock); - list_for_each_entry(hdev, &hci_dev_list, list) { - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work(&hdev->power_off); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - - (dr + n)->dev_id = hdev->id; - (dr + n)->dev_opt = hdev->flags; - - if (++n >= dev_num) - break; - } - read_unlock(&hci_dev_list_lock); - - dl->dev_num = n; - size = sizeof(*dl) + n * sizeof(*dr); - - err = copy_to_user(arg, dl, size); - kfree(dl); - - return err ? -EFAULT : 0; -} - -int hci_get_dev_info(void __user *arg) -{ - struct hci_dev *hdev; - struct hci_dev_info di; - int err = 0; - - if (copy_from_user(&di, arg, sizeof(di))) - return -EFAULT; - - hdev = hci_dev_get(di.dev_id); - if (!hdev) - return -ENODEV; - - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work_sync(&hdev->power_off); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - - strcpy(di.name, hdev->name); - di.bdaddr = hdev->bdaddr; - di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); - di.flags = hdev->flags; - di.pkt_type = hdev->pkt_type; - di.acl_mtu = hdev->acl_mtu; - di.acl_pkts = hdev->acl_pkts; - di.sco_mtu = hdev->sco_mtu; - di.sco_pkts = hdev->sco_pkts; - di.link_policy = hdev->link_policy; - di.link_mode = hdev->link_mode; - - memcpy(&di.stat, &hdev->stat, sizeof(di.stat)); - memcpy(&di.features, &hdev->features, sizeof(di.features)); - - if (copy_to_user(arg, &di, sizeof(di))) - err = -EFAULT; - - hci_dev_put(hdev); - - return err; -} - -/* ---- Interface to HCI drivers ---- */ - -static int hci_rfkill_set_block(void *data, bool blocked) -{ - struct hci_dev *hdev = data; - - BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked); - - if (!blocked) - return 0; - - hci_dev_do_close(hdev); - - return 0; -} - -static const struct rfkill_ops hci_rfkill_ops = { - .set_block = hci_rfkill_set_block, -}; - -/* Alloc HCI device */ -struct hci_dev *hci_alloc_dev(void) -{ - struct hci_dev *hdev; - - hdev = kzalloc(sizeof(struct hci_dev), GFP_KERNEL); - if (!hdev) - return NULL; - - hci_init_sysfs(hdev); - skb_queue_head_init(&hdev->driver_init); - - return hdev; -} -EXPORT_SYMBOL(hci_alloc_dev); - -/* Free HCI device */ -void hci_free_dev(struct hci_dev *hdev) -{ - skb_queue_purge(&hdev->driver_init); - - /* will free via device release */ - put_device(&hdev->dev); -} -EXPORT_SYMBOL(hci_free_dev); - -static void hci_power_on(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, power_on); - - BT_DBG("%s", hdev->name); - - if (hci_dev_open(hdev->id) < 0) - return; - - if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - schedule_delayed_work(&hdev->power_off, - msecs_to_jiffies(AUTO_OFF_TIMEOUT)); - - if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) - mgmt_index_added(hdev); -} - -static void hci_power_off(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - power_off.work); - - BT_DBG("%s", hdev->name); - - hci_dev_do_close(hdev); -} - -static void hci_discov_off(struct work_struct *work) -{ - struct hci_dev *hdev; - u8 scan = SCAN_PAGE; - - hdev = container_of(work, struct hci_dev, discov_off.work); - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); - - hdev->discov_timeout = 0; - - hci_dev_unlock(hdev); -} - -int hci_uuids_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *uuid; - - uuid = list_entry(p, struct bt_uuid, list); - - list_del(p); - kfree(uuid); - } - - return 0; -} - -int hci_link_keys_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->link_keys) { - struct link_key *key; - - key = list_entry(p, struct link_key, list); - - list_del(p); - kfree(key); - } - - return 0; -} - -int hci_smp_ltks_clear(struct hci_dev *hdev) -{ - struct smp_ltk *k, *tmp; - - list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { - list_del(&k->list); - kfree(k); - } - - return 0; -} - -struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct link_key *k; - - list_for_each_entry(k, &hdev->link_keys, list) - if (bacmp(bdaddr, &k->bdaddr) == 0) - return k; - - return NULL; -} - -static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, - u8 key_type, u8 old_key_type) -{ - /* Legacy key */ - if (key_type < 0x03) - return true; - - /* Debug keys are insecure so don't store them persistently */ - if (key_type == HCI_LK_DEBUG_COMBINATION) - return false; - - /* Changed combination key and there's no previous one */ - if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff) - return false; - - /* Security mode 3 case */ - if (!conn) - return true; - - /* Neither local nor remote side had no-bonding as requirement */ - if (conn->auth_type > 0x01 && conn->remote_auth > 0x01) - return true; - - /* Local side had dedicated bonding as requirement */ - if (conn->auth_type == 0x02 || conn->auth_type == 0x03) - return true; - - /* Remote side had dedicated bonding as requirement */ - if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) - return true; - - /* If none of the above criteria match, then don't store the key - * persistently */ - return false; -} - -struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]) -{ - struct smp_ltk *k; - - list_for_each_entry(k, &hdev->long_term_keys, list) { - if (k->ediv != ediv || - memcmp(rand, k->rand, sizeof(k->rand))) - continue; - - return k; - } - - return NULL; -} -EXPORT_SYMBOL(hci_find_ltk); - -struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type) -{ - struct smp_ltk *k; - - list_for_each_entry(k, &hdev->long_term_keys, list) - if (addr_type == k->bdaddr_type && - bacmp(bdaddr, &k->bdaddr) == 0) - return k; - - return NULL; -} -EXPORT_SYMBOL(hci_find_ltk_by_addr); - -int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) -{ - struct link_key *key, *old_key; - u8 old_key_type; - bool persistent; - - old_key = hci_find_link_key(hdev, bdaddr); - if (old_key) { - old_key_type = old_key->type; - key = old_key; - } else { - old_key_type = conn ? conn->key_type : 0xff; - key = kzalloc(sizeof(*key), GFP_ATOMIC); - if (!key) - return -ENOMEM; - list_add(&key->list, &hdev->link_keys); - } - - BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type); - - /* Some buggy controller combinations generate a changed - * combination key for legacy pairing even when there's no - * previous key */ - if (type == HCI_LK_CHANGED_COMBINATION && - (!conn || conn->remote_auth == 0xff) && - old_key_type == 0xff) { - type = HCI_LK_COMBINATION; - if (conn) - conn->key_type = type; - } - - bacpy(&key->bdaddr, bdaddr); - memcpy(key->val, val, 16); - key->pin_len = pin_len; - - if (type == HCI_LK_CHANGED_COMBINATION) - key->type = old_key_type; - else - key->type = type; - - if (!new_key) - return 0; - - persistent = hci_persistent_key(hdev, conn, type, old_key_type); - - mgmt_new_link_key(hdev, key, persistent); - - if (conn) - conn->flush_key = !persistent; - - return 0; -} - -int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type, - int new_key, u8 authenticated, u8 tk[16], u8 enc_size, u16 - ediv, u8 rand[8]) -{ - struct smp_ltk *key, *old_key; - - if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK)) - return 0; - - old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type); - if (old_key) - key = old_key; - else { - key = kzalloc(sizeof(*key), GFP_ATOMIC); - if (!key) - return -ENOMEM; - list_add(&key->list, &hdev->long_term_keys); - } - - bacpy(&key->bdaddr, bdaddr); - key->bdaddr_type = addr_type; - memcpy(key->val, tk, sizeof(key->val)); - key->authenticated = authenticated; - key->ediv = ediv; - key->enc_size = enc_size; - key->type = type; - memcpy(key->rand, rand, sizeof(key->rand)); - - if (!new_key) - return 0; - - if (type & HCI_SMP_LTK) - mgmt_new_ltk(hdev, key, 1); - - return 0; -} - -int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct link_key *key; - - key = hci_find_link_key(hdev, bdaddr); - if (!key) - return -ENOENT; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&key->list); - kfree(key); - - return 0; -} - -int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct smp_ltk *k, *tmp; - - list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) { - if (bacmp(bdaddr, &k->bdaddr)) - continue; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&k->list); - kfree(k); - } - - return 0; -} - -/* HCI command timer function */ -static void hci_cmd_timer(unsigned long arg) -{ - struct hci_dev *hdev = (void *) arg; - - BT_ERR("%s command tx timeout", hdev->name); - atomic_set(&hdev->cmd_cnt, 1); - queue_work(hdev->workqueue, &hdev->cmd_work); -} - -struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev, - bdaddr_t *bdaddr) -{ - struct oob_data *data; - - list_for_each_entry(data, &hdev->remote_oob_data, list) - if (bacmp(bdaddr, &data->bdaddr) == 0) - return data; - - return NULL; -} - -int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct oob_data *data; - - data = hci_find_remote_oob_data(hdev, bdaddr); - if (!data) - return -ENOENT; - - BT_DBG("%s removing %s", hdev->name, batostr(bdaddr)); - - list_del(&data->list); - kfree(data); - - return 0; -} - -int hci_remote_oob_data_clear(struct hci_dev *hdev) -{ - struct oob_data *data, *n; - - list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) { - list_del(&data->list); - kfree(data); - } - - return 0; -} - -int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, - u8 *randomizer) -{ - struct oob_data *data; - - data = hci_find_remote_oob_data(hdev, bdaddr); - - if (!data) { - data = kmalloc(sizeof(*data), GFP_ATOMIC); - if (!data) - return -ENOMEM; - - bacpy(&data->bdaddr, bdaddr); - list_add(&data->list, &hdev->remote_oob_data); - } - - memcpy(data->hash, hash, sizeof(data->hash)); - memcpy(data->randomizer, randomizer, sizeof(data->randomizer)); - - BT_DBG("%s for %s", hdev->name, batostr(bdaddr)); - - return 0; -} - -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct bdaddr_list *b; - - list_for_each_entry(b, &hdev->blacklist, list) - if (bacmp(bdaddr, &b->bdaddr) == 0) - return b; - - return NULL; -} - -int hci_blacklist_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); - - list_del(p); - kfree(b); - } - - return 0; -} - -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (bacmp(bdaddr, BDADDR_ANY) == 0) - return -EBADF; - - if (hci_blacklist_lookup(hdev, bdaddr)) - return -EEXIST; - - entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, bdaddr); - - list_add(&entry->list, &hdev->blacklist); - - return mgmt_device_blocked(hdev, bdaddr, type); -} - -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (bacmp(bdaddr, BDADDR_ANY) == 0) - return hci_blacklist_clear(hdev); - - entry = hci_blacklist_lookup(hdev, bdaddr); - if (!entry) - return -ENOENT; - - list_del(&entry->list); - kfree(entry); - - return mgmt_device_unblocked(hdev, bdaddr, type); -} - -static void hci_clear_adv_cache(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - adv_work.work); - - hci_dev_lock(hdev); - - hci_adv_entries_clear(hdev); - - hci_dev_unlock(hdev); -} - -int hci_adv_entries_clear(struct hci_dev *hdev) -{ - struct adv_entry *entry, *tmp; - - list_for_each_entry_safe(entry, tmp, &hdev->adv_entries, list) { - list_del(&entry->list); - kfree(entry); - } - - BT_DBG("%s adv cache cleared", hdev->name); - - return 0; -} - -struct adv_entry *hci_find_adv_entry(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - struct adv_entry *entry; - - list_for_each_entry(entry, &hdev->adv_entries, list) - if (bacmp(bdaddr, &entry->bdaddr) == 0) - return entry; - - return NULL; -} - -static inline int is_connectable_adv(u8 evt_type) -{ - if (evt_type == ADV_IND || evt_type == ADV_DIRECT_IND) - return 1; - - return 0; -} - -int hci_add_adv_entry(struct hci_dev *hdev, - struct hci_ev_le_advertising_info *ev) { struct adv_entry *entry; if (!is_connectable_adv(ev->evt_type)) - return -EINVAL; - - /* Only new entries should be added to adv_entries. So, if - * bdaddr was found, don't add it. */ - if (hci_find_adv_entry(hdev, &ev->bdaddr)) - return 0; - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, &ev->bdaddr); - entry->bdaddr_type = ev->bdaddr_type; - - list_add(&entry->list, &hdev->adv_entries); - - BT_DBG("%s adv entry added: address %s type %u", hdev->name, - batostr(&entry->bdaddr), entry->bdaddr_type); - - return 0; -} - -static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) -{ - struct le_scan_params *param = (struct le_scan_params *) opt; - struct hci_cp_le_set_scan_param cp; - - memset(&cp, 0, sizeof(cp)); - cp.type = param->type; - cp.interval = cpu_to_le16(param->interval); - cp.window = cpu_to_le16(param->window); - - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); -} - -static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) -{ - struct hci_cp_le_set_scan_enable cp; - - memset(&cp, 0, sizeof(cp)); - cp.enable = 1; - - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); -} - -static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, - u16 window, int timeout) -{ - long timeo = msecs_to_jiffies(3000); - struct le_scan_params param; - int err; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) - return -EINPROGRESS; - - param.type = type; - param.interval = interval; - param.window = window; - - hci_req_lock(hdev); - - err = __hci_request(hdev, le_scan_param_req, (unsigned long) ¶m, - timeo); - if (!err) - err = __hci_request(hdev, le_scan_enable_req, 0, timeo); - - hci_req_unlock(hdev); - - if (err < 0) - return err; - - schedule_delayed_work(&hdev->le_scan_disable, - msecs_to_jiffies(timeout)); - - return 0; -} - -static void le_scan_disable_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - le_scan_disable.work); - struct hci_cp_le_set_scan_enable cp; - - BT_DBG("%s", hdev->name); - - memset(&cp, 0, sizeof(cp)); - - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); -} - -static void le_scan_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan); - struct le_scan_params *param = &hdev->le_scan_params; - - BT_DBG("%s", hdev->name); - - hci_do_le_scan(hdev, param->type, param->interval, param->window, - param->timeout); -} - -int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, - int timeout) -{ - struct le_scan_params *param = &hdev->le_scan_params; - - BT_DBG("%s", hdev->name); - - if (work_busy(&hdev->le_scan)) - return -EINPROGRESS; - - param->type = type; - param->interval = interval; - param->window = window; - param->timeout = timeout; - - queue_work(system_long_wq, &hdev->le_scan); - - return 0; -} - -/* Register HCI device */ -int hci_register_dev(struct hci_dev *hdev) -{ - struct list_head *head = &hci_dev_list, *p; - int i, id, error; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - if (!hdev->open || !hdev->close) - return -EINVAL; - - /* Do not allow HCI_AMP devices to register at index 0, - * so the index can be used as the AMP controller ID. - */ - id = (hdev->dev_type == HCI_BREDR) ? 0 : 1; - - write_lock(&hci_dev_list_lock); - - /* Find first available device id */ - list_for_each(p, &hci_dev_list) { - if (list_entry(p, struct hci_dev, list)->id != id) - break; - head = p; id++; - } - - sprintf(hdev->name, "hci%d", id); - hdev->id = id; - list_add_tail(&hdev->list, head); - - mutex_init(&hdev->lock); - - hdev->flags = 0; - hdev->dev_flags = 0; - hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); - hdev->esco_type = (ESCO_HV1); - hdev->link_mode = (HCI_LM_ACCEPT); - hdev->io_capability = 0x03; /* No Input No Output */ - - hdev->idle_timeout = 0; - hdev->sniff_max_interval = 800; - hdev->sniff_min_interval = 80; - - INIT_WORK(&hdev->rx_work, hci_rx_work); - INIT_WORK(&hdev->cmd_work, hci_cmd_work); - INIT_WORK(&hdev->tx_work, hci_tx_work); - - - skb_queue_head_init(&hdev->rx_q); - skb_queue_head_init(&hdev->cmd_q); - skb_queue_head_init(&hdev->raw_q); - - setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev); - - for (i = 0; i < NUM_REASSEMBLY; i++) - hdev->reassembly[i] = NULL; - - init_waitqueue_head(&hdev->req_wait_q); - mutex_init(&hdev->req_lock); - - discovery_init(hdev); - - hci_conn_hash_init(hdev); - - INIT_LIST_HEAD(&hdev->mgmt_pending); - - INIT_LIST_HEAD(&hdev->blacklist); - - INIT_LIST_HEAD(&hdev->uuids); - - INIT_LIST_HEAD(&hdev->link_keys); - INIT_LIST_HEAD(&hdev->long_term_keys); - - INIT_LIST_HEAD(&hdev->remote_oob_data); - - INIT_LIST_HEAD(&hdev->adv_entries); - - INIT_DELAYED_WORK(&hdev->adv_work, hci_clear_adv_cache); - INIT_WORK(&hdev->power_on, hci_power_on); - INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); - - INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); - - memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); - - atomic_set(&hdev->promisc, 0); - - INIT_WORK(&hdev->le_scan, le_scan_work); - - INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); - - write_unlock(&hci_dev_list_lock); - - hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1); - if (!hdev->workqueue) { - error = -ENOMEM; - goto err; - } - - error = hci_add_sysfs(hdev); - if (error < 0) - goto err_wqueue; - - hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, - RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); - if (hdev->rfkill) { - if (rfkill_register(hdev->rfkill) < 0) { - rfkill_destroy(hdev->rfkill); - hdev->rfkill = NULL; - } - } - - set_bit(HCI_AUTO_OFF, &hdev->dev_flags); - set_bit(HCI_SETUP, &hdev->dev_flags); - schedule_work(&hdev->power_on); - - hci_notify(hdev, HCI_DEV_REG); - hci_dev_hold(hdev); - - return id; - -err_wqueue: - destroy_workqueue(hdev->workqueue); -err: - write_lock(&hci_dev_list_lock); - list_del(&hdev->list); - write_unlock(&hci_dev_list_lock); - - return error; -} -EXPORT_SYMBOL(hci_register_dev); - -/* Unregister HCI device */ -void hci_unregister_dev(struct hci_dev *hdev) -{ - int i; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - set_bit(HCI_UNREGISTER, &hdev->dev_flags); - - write_lock(&hci_dev_list_lock); - list_del(&hdev->list); - write_unlock(&hci_dev_list_lock); - - hci_dev_do_close(hdev); - - for (i = 0; i < NUM_REASSEMBLY; i++) - kfree_skb(hdev->reassembly[i]); - - if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { - hci_dev_lock(hdev); - mgmt_index_removed(hdev); - hci_dev_unlock(hdev); - } - - /* mgmt_index_removed should take care of emptying the - * pending list */ - BUG_ON(!list_empty(&hdev->mgmt_pending)); - - hci_notify(hdev, HCI_DEV_UNREG); - - if (hdev->rfkill) { - rfkill_unregister(hdev->rfkill); - rfkill_destroy(hdev->rfkill); - } - - hci_del_sysfs(hdev); - - cancel_delayed_work_sync(&hdev->adv_work); - - destroy_workqueue(hdev->workqueue); - - hci_dev_lock(hdev); - hci_blacklist_clear(hdev); - hci_uuids_clear(hdev); - hci_link_keys_clear(hdev); - hci_smp_ltks_clear(hdev); - hci_remote_oob_data_clear(hdev); - hci_adv_entries_clear(hdev); - hci_dev_unlock(hdev); - - hci_dev_put(hdev); -} -EXPORT_SYMBOL(hci_unregister_dev); - -/* Suspend HCI device */ -int hci_suspend_dev(struct hci_dev *hdev) -{ - hci_notify(hdev, HCI_DEV_SUSPEND); - return 0; -} -EXPORT_SYMBOL(hci_suspend_dev); - -/* Resume HCI device */ -int hci_resume_dev(struct hci_dev *hdev) -{ - hci_notify(hdev, HCI_DEV_RESUME); - return 0; -} -EXPORT_SYMBOL(hci_resume_dev); - -/* Receive frame from HCI drivers */ -int hci_recv_frame(struct sk_buff *skb) -{ - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - if (!hdev || (!test_bit(HCI_UP, &hdev->flags) - && !test_bit(HCI_INIT, &hdev->flags))) { - kfree_skb(skb); - return -ENXIO; - } - - /* Incomming skb */ - bt_cb(skb)->incoming = 1; - - /* Time stamp */ - __net_timestamp(skb); - - skb_queue_tail(&hdev->rx_q, skb); - queue_work(hdev->workqueue, &hdev->rx_work); - - return 0; -} -EXPORT_SYMBOL(hci_recv_frame); - -static int hci_reassembly(struct hci_dev *hdev, int type, void *data, - int count, __u8 index) -{ - int len = 0; - int hlen = 0; - int remain = count; - struct sk_buff *skb; - struct bt_skb_cb *scb; - - if ((type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) || - index >= NUM_REASSEMBLY) - return -EILSEQ; - - skb = hdev->reassembly[index]; - - if (!skb) { - switch (type) { - case HCI_ACLDATA_PKT: - len = HCI_MAX_FRAME_SIZE; - hlen = HCI_ACL_HDR_SIZE; - break; - case HCI_EVENT_PKT: - len = HCI_MAX_EVENT_SIZE; - hlen = HCI_EVENT_HDR_SIZE; - break; - case HCI_SCODATA_PKT: - len = HCI_MAX_SCO_SIZE; - hlen = HCI_SCO_HDR_SIZE; - break; - } - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - scb = (void *) skb->cb; - scb->expect = hlen; - scb->pkt_type = type; - - skb->dev = (void *) hdev; - hdev->reassembly[index] = skb; - } - - while (count) { - scb = (void *) skb->cb; - len = min_t(uint, scb->expect, count); - - memcpy(skb_put(skb, len), data, len); - - count -= len; - data += len; - scb->expect -= len; - remain = count; - - switch (type) { - case HCI_EVENT_PKT: - if (skb->len == HCI_EVENT_HDR_SIZE) { - struct hci_event_hdr *h = hci_event_hdr(skb); - scb->expect = h->plen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_ACLDATA_PKT: - if (skb->len == HCI_ACL_HDR_SIZE) { - struct hci_acl_hdr *h = hci_acl_hdr(skb); - scb->expect = __le16_to_cpu(h->dlen); - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - - case HCI_SCODATA_PKT: - if (skb->len == HCI_SCO_HDR_SIZE) { - struct hci_sco_hdr *h = hci_sco_hdr(skb); - scb->expect = h->dlen; - - if (skb_tailroom(skb) < scb->expect) { - kfree_skb(skb); - hdev->reassembly[index] = NULL; - return -ENOMEM; - } - } - break; - } - - if (scb->expect == 0) { - /* Complete frame */ - - bt_cb(skb)->pkt_type = type; - hci_recv_frame(skb); - - hdev->reassembly[index] = NULL; - return remain; - } - } - - return remain; -} - -int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) -{ - int rem = 0; - - if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) - return -EILSEQ; - - while (count) { - rem = hci_reassembly(hdev, type, data, count, type - 1); - if (rem < 0) - return rem; - - data += (count - rem); - count = rem; - } - - return rem; -} -EXPORT_SYMBOL(hci_recv_fragment); - -#define STREAM_REASSEMBLY 0 - -int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) -{ - int type; - int rem = 0; - - while (count) { - struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; - - if (!skb) { - struct { char type; } *pkt; - - /* Start of the frame */ - pkt = data; - type = pkt->type; - - data++; - count--; - } else - type = bt_cb(skb)->pkt_type; - - rem = hci_reassembly(hdev, type, data, count, - STREAM_REASSEMBLY); - if (rem < 0) - return rem; - - data += (count - rem); - count = rem; - } - - return rem; -} -EXPORT_SYMBOL(hci_recv_stream_fragment); - -/* ---- Interface to upper protocols ---- */ - -int hci_register_cb(struct hci_cb *cb) -{ - BT_DBG("%p name %s", cb, cb->name); - - write_lock(&hci_cb_list_lock); - list_add(&cb->list, &hci_cb_list); - write_unlock(&hci_cb_list_lock); - - return 0; -} -EXPORT_SYMBOL(hci_register_cb); - -int hci_unregister_cb(struct hci_cb *cb) -{ - BT_DBG("%p name %s", cb, cb->name); - - write_lock(&hci_cb_list_lock); - list_del(&cb->list); - write_unlock(&hci_cb_list_lock); - - return 0; -} -EXPORT_SYMBOL(hci_unregister_cb); - -static int hci_send_frame(struct sk_buff *skb) -{ - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - - if (!hdev) { - kfree_skb(skb); - return -ENODEV; - } - - BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); - - /* Time stamp */ - __net_timestamp(skb); - - /* Send copy to monitor */ - hci_send_to_monitor(hdev, skb); - - if (atomic_read(&hdev->promisc)) { - /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb); - } - - /* Get rid of skb owner, prior to sending to the driver. */ - skb_orphan(skb); - - return hdev->send(skb); -} - -/* Send HCI command */ -int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) -{ - int len = HCI_COMMAND_HDR_SIZE + plen; - struct hci_command_hdr *hdr; - struct sk_buff *skb; - - BT_DBG("%s opcode 0x%x plen %d", hdev->name, opcode, plen); - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for command", hdev->name); - return -ENOMEM; - } - - hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); - hdr->opcode = cpu_to_le16(opcode); - hdr->plen = plen; - - if (plen) - memcpy(skb_put(skb, plen), param, plen); - - BT_DBG("skb len %d", skb->len); - - bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - skb->dev = (void *) hdev; - - if (test_bit(HCI_INIT, &hdev->flags)) - hdev->init_last_cmd = opcode; - - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - - return 0; -} - -/* Get data from the previously sent command */ -void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) -{ - struct hci_command_hdr *hdr; - - if (!hdev->sent_cmd) - return NULL; - - hdr = (void *) hdev->sent_cmd->data; - - if (hdr->opcode != cpu_to_le16(opcode)) - return NULL; - - BT_DBG("%s opcode 0x%x", hdev->name, opcode); - - return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; -} - -/* Send ACL data */ -static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) -{ - struct hci_acl_hdr *hdr; - int len = skb->len; - - skb_push(skb, HCI_ACL_HDR_SIZE); - skb_reset_transport_header(skb); - hdr = (struct hci_acl_hdr *)skb_transport_header(skb); - hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags)); - hdr->dlen = cpu_to_le16(len); -} - -static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, - struct sk_buff *skb, __u16 flags) -{ - struct hci_dev *hdev = conn->hdev; - struct sk_buff *list; - - list = skb_shinfo(skb)->frag_list; - if (!list) { - /* Non fragmented */ - BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); - - skb_queue_tail(queue, skb); - } else { - /* Fragmented */ - BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - - skb_shinfo(skb)->frag_list = NULL; - - /* Queue all fragments atomically */ - spin_lock(&queue->lock); - - __skb_queue_tail(queue, skb); - - flags &= ~ACL_START; - flags |= ACL_CONT; - do { - skb = list; list = list->next; - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); - - BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - - __skb_queue_tail(queue, skb); - } while (list); - - spin_unlock(&queue->lock); - } -} - -void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) -{ - struct hci_conn *conn = chan->conn; - struct hci_dev *hdev = conn->hdev; - - BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags); - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); - - hci_queue_acl(conn, &chan->data_q, skb, flags); - - queue_work(hdev->workqueue, &hdev->tx_work); -} -EXPORT_SYMBOL(hci_send_acl); - -/* Send SCO data */ -void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_sco_hdr hdr; - - BT_DBG("%s len %d", hdev->name, skb->len); - - hdr.handle = cpu_to_le16(conn->handle); - hdr.dlen = skb->len; - - skb_push(skb, HCI_SCO_HDR_SIZE); - skb_reset_transport_header(skb); - memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE); - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; - - skb_queue_tail(&conn->data_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); -} -EXPORT_SYMBOL(hci_send_sco); - -/* ---- HCI TX task (outgoing data) ---- */ - -/* HCI Connection scheduler */ -static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *conn = NULL, *c; - int num = 0, min = ~0; - - /* We don't have to lock device here. Connections are always - * added and removed with TX task disabled. */ - - rcu_read_lock(); - - list_for_each_entry_rcu(c, &h->list, list) { - if (c->type != type || skb_queue_empty(&c->data_q)) - continue; - - if (c->state != BT_CONNECTED && c->state != BT_CONFIG) - continue; - - num++; - - if (c->sent < min) { - min = c->sent; - conn = c; - } - - if (hci_conn_num(hdev, type) == num) - break; - } - - rcu_read_unlock(); - - if (conn) { - int cnt, q; - - switch (conn->type) { - case ACL_LINK: - cnt = hdev->acl_cnt; - break; - case SCO_LINK: - case ESCO_LINK: - cnt = hdev->sco_cnt; - break; - case LE_LINK: - cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; - break; - default: - cnt = 0; - BT_ERR("Unknown link type"); - } - - q = cnt / num; - *quote = q ? q : 1; - } else - *quote = 0; - - BT_DBG("conn %p quote %d", conn, *quote); - return conn; -} - -static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *c; - - BT_ERR("%s link tx timeout", hdev->name); - - rcu_read_lock(); - - /* Kill stalled connections */ - list_for_each_entry_rcu(c, &h->list, list) { - if (c->type == type && c->sent) { - BT_ERR("%s killing stalled connection %s", - hdev->name, batostr(&c->dst)); - hci_acl_disconn(c, 0x13); - } - } - - rcu_read_unlock(); -} - -static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, - int *quote) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_chan *chan = NULL; - int num = 0, min = ~0, cur_prio = 0; - struct hci_conn *conn; - int cnt, q, conn_num = 0; - - BT_DBG("%s", hdev->name); - - rcu_read_lock(); - - list_for_each_entry_rcu(conn, &h->list, list) { - struct hci_chan *tmp; - - if (conn->type != type) - continue; - - if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) - continue; - - conn_num++; - - list_for_each_entry_rcu(tmp, &conn->chan_list, list) { - struct sk_buff *skb; - - if (skb_queue_empty(&tmp->data_q)) - continue; - - skb = skb_peek(&tmp->data_q); - if (skb->priority < cur_prio) - continue; - - if (skb->priority > cur_prio) { - num = 0; - min = ~0; - cur_prio = skb->priority; - } - - num++; - - if (conn->sent < min) { - min = conn->sent; - chan = tmp; - } - } - - if (hci_conn_num(hdev, type) == conn_num) - break; - } - - rcu_read_unlock(); - - if (!chan) - return NULL; - - switch (chan->conn->type) { - case ACL_LINK: - cnt = hdev->acl_cnt; - break; - case SCO_LINK: - case ESCO_LINK: - cnt = hdev->sco_cnt; - break; - case LE_LINK: - cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; - break; - default: - cnt = 0; - BT_ERR("Unknown link type"); - } - - q = cnt / num; - *quote = q ? q : 1; - BT_DBG("chan %p quote %d", chan, *quote); - return chan; -} - -static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) -{ - struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *conn; - int num = 0; - - BT_DBG("%s", hdev->name); - - rcu_read_lock(); - - list_for_each_entry_rcu(conn, &h->list, list) { - struct hci_chan *chan; - - if (conn->type != type) - continue; - - if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) - continue; - - num++; - - list_for_each_entry_rcu(chan, &conn->chan_list, list) { - struct sk_buff *skb; - - if (chan->sent) { - chan->sent = 0; - continue; - } - - if (skb_queue_empty(&chan->data_q)) - continue; - - skb = skb_peek(&chan->data_q); - if (skb->priority >= HCI_PRIO_MAX - 1) - continue; - - skb->priority = HCI_PRIO_MAX - 1; - - BT_DBG("chan %p skb %p promoted to %d", chan, skb, - skb->priority); - } - - if (hci_conn_num(hdev, type) == num) - break; - } - - rcu_read_unlock(); - -} - -static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) -{ - /* Calculate count of blocks used by this packet */ - return DIV_ROUND_UP(skb->len - HCI_ACL_HDR_SIZE, hdev->block_len); -} - -static inline void __check_timeout(struct hci_dev *hdev, unsigned int cnt) -{ - if (!test_bit(HCI_RAW, &hdev->flags)) { - /* ACL tx timeout must be longer than maximum - * link supervision timeout (40.9 seconds) */ - if (!cnt && time_after(jiffies, hdev->acl_last_tx + - msecs_to_jiffies(HCI_ACL_TX_TIMEOUT))) - hci_link_tx_to(hdev, ACL_LINK); - } -} - -static inline void hci_sched_acl_pkt(struct hci_dev *hdev) -{ - unsigned int cnt = hdev->acl_cnt; - struct hci_chan *chan; - struct sk_buff *skb; - int quote; - - __check_timeout(hdev, cnt); - - while (hdev->acl_cnt && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { - u32 priority = (skb_peek(&chan->data_q))->priority; - while (quote-- && (skb = skb_peek(&chan->data_q))) { - BT_DBG("chan %p skb %p len %d priority %u", chan, skb, - skb->len, skb->priority); - - /* Stop if priority has changed */ - if (skb->priority < priority) - break; - - skb = skb_dequeue(&chan->data_q); - - hci_conn_enter_active_mode(chan->conn, - bt_cb(skb)->force_active); - - hci_send_frame(skb); - hdev->acl_last_tx = jiffies; - - hdev->acl_cnt--; - chan->sent++; - chan->conn->sent++; - } - } - - if (cnt != hdev->acl_cnt) - hci_prio_recalculate(hdev, ACL_LINK); -} - -static inline void hci_sched_acl_blk(struct hci_dev *hdev) -{ - unsigned int cnt = hdev->block_cnt; - struct hci_chan *chan; - struct sk_buff *skb; - int quote; - - __check_timeout(hdev, cnt); - - while (hdev->block_cnt > 0 && - (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { - u32 priority = (skb_peek(&chan->data_q))->priority; - while (quote > 0 && (skb = skb_peek(&chan->data_q))) { - int blocks; - - BT_DBG("chan %p skb %p len %d priority %u", chan, skb, - skb->len, skb->priority); - - /* Stop if priority has changed */ - if (skb->priority < priority) - break; - - skb = skb_dequeue(&chan->data_q); - - blocks = __get_blocks(hdev, skb); - if (blocks > hdev->block_cnt) - return; - - hci_conn_enter_active_mode(chan->conn, - bt_cb(skb)->force_active); - - hci_send_frame(skb); - hdev->acl_last_tx = jiffies; - - hdev->block_cnt -= blocks; - quote -= blocks; - - chan->sent += blocks; - chan->conn->sent += blocks; - } - } - - if (cnt != hdev->block_cnt) - hci_prio_recalculate(hdev, ACL_LINK); -} - -static inline void hci_sched_acl(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, ACL_LINK)) - return; - - switch (hdev->flow_ctl_mode) { - case HCI_FLOW_CTL_MODE_PACKET_BASED: - hci_sched_acl_pkt(hdev); - break; - - case HCI_FLOW_CTL_MODE_BLOCK_BASED: - hci_sched_acl_blk(hdev); - break; - } -} - -/* Schedule SCO */ -static inline void hci_sched_sco(struct hci_dev *hdev) -{ - struct hci_conn *conn; - struct sk_buff *skb; - int quote; - - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, SCO_LINK)) - return; - - while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); - - conn->sent++; - if (conn->sent == ~0) - conn->sent = 0; - } - } -} - -static inline void hci_sched_esco(struct hci_dev *hdev) -{ - struct hci_conn *conn; - struct sk_buff *skb; - int quote; - - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, ESCO_LINK)) - return; - - while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); - - conn->sent++; - if (conn->sent == ~0) - conn->sent = 0; - } - } -} - -static inline void hci_sched_le(struct hci_dev *hdev) -{ - struct hci_chan *chan; - struct sk_buff *skb; - int quote, cnt, tmp; - - BT_DBG("%s", hdev->name); - - if (!hci_conn_num(hdev, LE_LINK)) - return; - - if (!test_bit(HCI_RAW, &hdev->flags)) { - /* LE tx timeout must be longer than maximum - * link supervision timeout (40.9 seconds) */ - if (!hdev->le_cnt && hdev->le_pkts && - time_after(jiffies, hdev->le_last_tx + HZ * 45)) - hci_link_tx_to(hdev, LE_LINK); - } - - cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; - tmp = cnt; - while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { - u32 priority = (skb_peek(&chan->data_q))->priority; - while (quote-- && (skb = skb_peek(&chan->data_q))) { - BT_DBG("chan %p skb %p len %d priority %u", chan, skb, - skb->len, skb->priority); - - /* Stop if priority has changed */ - if (skb->priority < priority) - break; - - skb = skb_dequeue(&chan->data_q); - - hci_send_frame(skb); - hdev->le_last_tx = jiffies; - - cnt--; - chan->sent++; - chan->conn->sent++; - } - } - - if (hdev->le_pkts) - hdev->le_cnt = cnt; - else - hdev->acl_cnt = cnt; - - if (cnt != tmp) - hci_prio_recalculate(hdev, LE_LINK); -} - -static void hci_tx_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work); - struct sk_buff *skb; - - BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, - hdev->sco_cnt, hdev->le_cnt); - - /* Schedule queues and send stuff to HCI driver */ - - hci_sched_acl(hdev); - - hci_sched_sco(hdev); - - hci_sched_esco(hdev); - - hci_sched_le(hdev); - - /* Send next queued raw (unknown type) packet */ - while ((skb = skb_dequeue(&hdev->raw_q))) - hci_send_frame(skb); -} - -/* ----- HCI RX task (incoming data processing) ----- */ - -/* ACL data packet */ -static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_acl_hdr *hdr = (void *) skb->data; - struct hci_conn *conn; - __u16 handle, flags; - - skb_pull(skb, HCI_ACL_HDR_SIZE); - - handle = __le16_to_cpu(hdr->handle); - flags = hci_flags(handle); - handle = hci_handle(handle); - - BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags); - - hdev->stat.acl_rx++; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); - hci_dev_unlock(hdev); - - if (conn) { - hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF); - - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !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); - hci_dev_unlock(hdev); - - /* Send to upper protocol */ - l2cap_recv_acldata(conn, skb, flags); - return; - } else { - BT_ERR("%s ACL packet for unknown connection handle %d", - hdev->name, handle); - } - - kfree_skb(skb); -} - -/* SCO data packet */ -static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_sco_hdr *hdr = (void *) skb->data; - struct hci_conn *conn; - __u16 handle; - - skb_pull(skb, HCI_SCO_HDR_SIZE); - - handle = __le16_to_cpu(hdr->handle); - - BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle); - - hdev->stat.sco_rx++; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); - hci_dev_unlock(hdev); - - if (conn) { - /* Send to upper protocol */ - sco_recv_scodata(conn, skb); - return; - } else { - BT_ERR("%s SCO packet for unknown connection handle %d", - hdev->name, handle); - } - - kfree_skb(skb); -} - -static void hci_rx_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work); - struct sk_buff *skb; - - BT_DBG("%s", hdev->name); - - while ((skb = skb_dequeue(&hdev->rx_q))) { - /* Send copy to monitor */ - hci_send_to_monitor(hdev, skb); - - if (atomic_read(&hdev->promisc)) { - /* Send copy to the sockets */ - hci_send_to_sock(hdev, skb); - } - - if (test_bit(HCI_RAW, &hdev->flags)) { - kfree_skb(skb); - continue; - } - - if (test_bit(HCI_INIT, &hdev->flags)) { - /* Don't process data packets in this states. */ - switch (bt_cb(skb)->pkt_type) { - case HCI_ACLDATA_PKT: - case HCI_SCODATA_PKT: - kfree_skb(skb); - continue; - } - } - - /* Process frame */ - switch (bt_cb(skb)->pkt_type) { - case HCI_EVENT_PKT: - BT_DBG("%s Event packet", hdev->name); - hci_event_packet(hdev, skb); - break; - - case HCI_ACLDATA_PKT: - BT_DBG("%s ACL data packet", hdev->name); - hci_acldata_packet(hdev, skb); - break; - - case HCI_SCODATA_PKT: - BT_DBG("%s SCO data packet", hdev->name); - hci_scodata_packet(hdev, skb); - break; - - default: - kfree_skb(skb); - break; - } - } -} - -static void hci_cmd_work(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, cmd_work); - struct sk_buff *skb; - - BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt)); - - /* Send queued commands */ - if (atomic_read(&hdev->cmd_cnt)) { - skb = skb_dequeue(&hdev->cmd_q); - if (!skb) - return; - - kfree_skb(hdev->sent_cmd); - - hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); - if (hdev->sent_cmd) { - atomic_dec(&hdev->cmd_cnt); - hci_send_frame(skb); - if (test_bit(HCI_RESET, &hdev->flags)) - del_timer(&hdev->cmd_timer); - else - mod_timer(&hdev->cmd_timer, - jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT)); - } else { - skb_queue_head(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - } -} - -int hci_do_inquiry(struct hci_dev *hdev, u8 length) -{ - /* General inquiry access code (GIAC) */ - u8 lap[3] = { 0x33, 0x8b, 0x9e }; - struct hci_cp_inquiry cp; - - BT_DBG("%s", hdev->name); - - if (test_bit(HCI_INQUIRY, &hdev->flags)) - return -EINPROGRESS; - - inquiry_cache_flush(hdev); - - memset(&cp, 0, sizeof(cp)); - memcpy(&cp.lap, lap, sizeof(cp.lap)); - cp.length = length; - - return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); -} - -int hci_cancel_inquiry(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); - - if (!test_bit(HCI_INQUIRY, &hdev->flags)) - return -EPERM; - - return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); -} diff --git a/net/bluetooth_tizen/hci_event.c b/net/bluetooth_tizen/hci_event.c deleted file mode 100644 index 17aaa11..0000000 --- a/net/bluetooth_tizen/hci_event.c +++ /dev/null @@ -1,3604 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI event handling. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -/* Handle HCI Event packets */ - -static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) { - hci_dev_lock(hdev); - mgmt_stop_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } - - clear_bit(HCI_INQUIRY, &hdev->flags); - - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - hci_dev_unlock(hdev); - - hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); - - hci_conn_check_pending(hdev); -} - -static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) - return; - - hci_conn_check_pending(hdev); -} - -static void hci_cc_remote_name_req_cancel(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_role_discovery *rp = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) { - if (rp->role) - conn->link_mode &= ~HCI_LM_MASTER; - else - conn->link_mode |= HCI_LM_MASTER; - } - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_link_policy *rp = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) - conn->link_policy = __le16_to_cpu(rp->policy); - - hci_dev_unlock(hdev); -} - -static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_write_link_policy *rp = (void *) skb->data; - struct hci_conn *conn; - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_POLICY); - if (!sent) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); - if (conn) - conn->link_policy = get_unaligned_le16(sent + 2); - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_def_link_policy *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->link_policy = __le16_to_cpu(rp->policy); -} - -static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY); - if (!sent) - return; - - if (!status) - hdev->link_policy = get_unaligned_le16(sent); - - hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status); -} - -static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - clear_bit(HCI_RESET, &hdev->flags); - - hci_req_complete(hdev, HCI_OP_RESET, status); - - /* Reset all non-persistent flags */ - hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS)); - - hdev->discovery.state = DISCOVERY_STOPPED; -} - -static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME); - if (!sent) - return; - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_set_local_name_complete(hdev, sent, status); - else if (!status) - memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); - - hci_dev_unlock(hdev); - - hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); -} - -static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_name *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - if (test_bit(HCI_SETUP, &hdev->dev_flags)) - memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH); -} - -static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_ENABLE); - if (!sent) - return; - - if (!status) { - __u8 param = *((__u8 *) sent); - - if (param == AUTH_ENABLED) - set_bit(HCI_AUTH, &hdev->flags); - else - clear_bit(HCI_AUTH, &hdev->flags); - } - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_auth_enable_complete(hdev, status); - - hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); -} - -static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE); - if (!sent) - return; - - if (!status) { - __u8 param = *((__u8 *) sent); - - if (param) - set_bit(HCI_ENCRYPT, &hdev->flags); - else - clear_bit(HCI_ENCRYPT, &hdev->flags); - } - - hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status); -} - -static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 param, status = *((__u8 *) skb->data); - int old_pscan, old_iscan; - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE); - if (!sent) - return; - - param = *((__u8 *) sent); - - hci_dev_lock(hdev); - - if (status != 0) { - mgmt_write_scan_failed(hdev, param, status); - hdev->discov_timeout = 0; - goto done; - } - - old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); - old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); - - if (param & SCAN_INQUIRY) { - set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev, 1); - if (hdev->discov_timeout > 0) { - int to = msecs_to_jiffies(hdev->discov_timeout * 1000); - queue_delayed_work(hdev->workqueue, &hdev->discov_off, - to); - } - } else if (old_iscan) - mgmt_discoverable(hdev, 0); - - if (param & SCAN_PAGE) { - set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev, 1); - } else if (old_pscan) - mgmt_connectable(hdev, 0); - -done: - hci_dev_unlock(hdev); - hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); -} - -static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_class_of_dev *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - memcpy(hdev->dev_class, rp->dev_class, 3); - - BT_DBG("%s class 0x%.2x%.2x%.2x", hdev->name, - hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); -} - -static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV); - if (!sent) - return; - - hci_dev_lock(hdev); - - if (status == 0) - memcpy(hdev->dev_class, sent, 3); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_set_class_of_dev_complete(hdev, sent, status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_voice_setting *rp = (void *) skb->data; - __u16 setting; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - setting = __le16_to_cpu(rp->voice_setting); - - if (hdev->voice_setting == setting) - return; - - hdev->voice_setting = setting; - - BT_DBG("%s voice setting 0x%04x", hdev->name, setting); - - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); -} - -static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - __u16 setting; - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) - return; - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_VOICE_SETTING); - if (!sent) - return; - - setting = get_unaligned_le16(sent); - - if (hdev->voice_setting == setting) - return; - - hdev->voice_setting = setting; - - BT_DBG("%s voice setting 0x%04x", hdev->name, setting); - - if (hdev->notify) - hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); -} - -static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status); -} - -static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - void *sent; - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE); - if (!sent) - return; - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); - else if (!status) { - if (*((u8 *) sent)) - set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - else - clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - } -} - -static u8 hci_get_inquiry_mode(struct hci_dev *hdev) -{ - if (hdev->features[6] & LMP_EXT_INQ) - return 2; - - if (hdev->features[3] & LMP_RSSI_INQ) - return 1; - - if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && - hdev->lmp_subver == 0x0757) - return 1; - - if (hdev->manufacturer == 15) { - if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) - return 1; - } - - if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && - hdev->lmp_subver == 0x1805) - return 1; - - return 0; -} - -static void hci_setup_inquiry_mode(struct hci_dev *hdev) -{ - u8 mode; - - mode = hci_get_inquiry_mode(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); -} - -static void hci_setup_event_mask(struct hci_dev *hdev) -{ - /* The second byte is 0xff instead of 0x9f (two reserved bits - * disabled) since a Broadcom 1.2 dongle doesn't respond to the - * command otherwise */ - u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; - - /* CSR 1.1 dongles does not accept any bitfield so don't try to set - * any event mask for pre 1.2 devices */ - if (hdev->hci_ver < BLUETOOTH_VER_1_2) - return; - - events[4] |= 0x01; /* Flow Specification Complete */ - events[4] |= 0x02; /* Inquiry Result with RSSI */ - events[4] |= 0x04; /* Read Remote Extended Features Complete */ - events[5] |= 0x08; /* Synchronous Connection Complete */ - events[5] |= 0x10; /* Synchronous Connection Changed */ - - if (hdev->features[3] & LMP_RSSI_INQ) - events[4] |= 0x02; /* Inquiry Result with RSSI */ - - if (hdev->features[5] & LMP_SNIFF_SUBR) - events[5] |= 0x20; /* Sniff Subrating */ - - if (hdev->features[5] & LMP_PAUSE_ENC) - events[5] |= 0x80; /* Encryption Key Refresh Complete */ - - if (hdev->features[6] & LMP_EXT_INQ) - events[5] |= 0x40; /* Extended Inquiry Result */ - - if (hdev->features[6] & LMP_NO_FLUSH) - events[7] |= 0x01; /* Enhanced Flush Complete */ - - if (hdev->features[7] & LMP_LSTO) - events[6] |= 0x80; /* Link Supervision Timeout Changed */ - - if (hdev->features[6] & LMP_SIMPLE_PAIR) { - events[6] |= 0x01; /* IO Capability Request */ - events[6] |= 0x02; /* IO Capability Response */ - events[6] |= 0x04; /* User Confirmation Request */ - events[6] |= 0x08; /* User Passkey Request */ - events[6] |= 0x10; /* Remote OOB Data Request */ - events[6] |= 0x20; /* Simple Pairing Complete */ - events[7] |= 0x04; /* User Passkey Notification */ - events[7] |= 0x08; /* Keypress Notification */ - events[7] |= 0x10; /* Remote Host Supported - * Features Notification */ - } - - if (hdev->features[4] & LMP_LE) - events[7] |= 0x20; /* LE Meta-Event */ - - hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); -} - -static void hci_setup(struct hci_dev *hdev) -{ - if (hdev->dev_type != HCI_BREDR) - return; - - hci_setup_event_mask(hdev); - - if (hdev->hci_ver > BLUETOOTH_VER_1_1) - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); - - if (hdev->features[6] & LMP_SIMPLE_PAIR) { - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - u8 mode = 0x01; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, - sizeof(mode), &mode); - } else { - struct hci_cp_write_eir cp; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - memset(&cp, 0, sizeof(cp)); - - hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); - } - } - - if (hdev->features[3] & LMP_RSSI_INQ) - hci_setup_inquiry_mode(hdev); - - if (hdev->features[7] & LMP_INQ_TX_PWR) - hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); - - if (hdev->features[7] & LMP_EXTFEATURES) { - struct hci_cp_read_local_ext_features cp; - - cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), - &cp); - } - - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { - u8 enable = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), - &enable); - } -} - -static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_version *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - goto done; - - hdev->hci_ver = rp->hci_ver; - hdev->hci_rev = __le16_to_cpu(rp->hci_rev); - hdev->lmp_ver = rp->lmp_ver; - hdev->manufacturer = __le16_to_cpu(rp->manufacturer); - hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); - - BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name, - hdev->manufacturer, - hdev->hci_ver, hdev->hci_rev); - - if (test_bit(HCI_INIT, &hdev->flags)) - hci_setup(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status); -} - -static void hci_setup_link_policy(struct hci_dev *hdev) -{ - u16 link_policy = 0; - - if (hdev->features[0] & LMP_RSWITCH) - link_policy |= HCI_LP_RSWITCH; - if (hdev->features[0] & LMP_HOLD) - link_policy |= HCI_LP_HOLD; - if (hdev->features[0] & LMP_SNIFF) - link_policy |= HCI_LP_SNIFF; - if (hdev->features[1] & LMP_PARK) - link_policy |= HCI_LP_PARK; - - link_policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(link_policy), - &link_policy); -} - -static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_commands *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - goto done; - - memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); - - if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10)) - hci_setup_link_policy(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); -} - -static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_local_features *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - memcpy(hdev->features, rp->features, 8); - - /* Adjust default settings according to features - * supported by device. */ - - if (hdev->features[0] & LMP_3SLOT) - hdev->pkt_type |= (HCI_DM3 | HCI_DH3); - - if (hdev->features[0] & LMP_5SLOT) - hdev->pkt_type |= (HCI_DM5 | HCI_DH5); - - if (hdev->features[1] & LMP_HV2) { - hdev->pkt_type |= (HCI_HV2); - hdev->esco_type |= (ESCO_HV2); - } - - if (hdev->features[1] & LMP_HV3) { - hdev->pkt_type |= (HCI_HV3); - hdev->esco_type |= (ESCO_HV3); - } - - if (hdev->features[3] & LMP_ESCO) - hdev->esco_type |= (ESCO_EV3); - - if (hdev->features[4] & LMP_EV4) - hdev->esco_type |= (ESCO_EV4); - - if (hdev->features[4] & LMP_EV5) - hdev->esco_type |= (ESCO_EV5); - - if (hdev->features[5] & LMP_EDR_ESCO_2M) - hdev->esco_type |= (ESCO_2EV3); - - if (hdev->features[5] & LMP_EDR_ESCO_3M) - hdev->esco_type |= (ESCO_3EV3); - - if (hdev->features[5] & LMP_EDR_3S_ESCO) - hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5); - - BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name, - hdev->features[0], hdev->features[1], - hdev->features[2], hdev->features[3], - hdev->features[4], hdev->features[5], - hdev->features[6], hdev->features[7]); -} - -static void hci_set_le_support(struct hci_dev *hdev) -{ - struct hci_cp_write_le_host_supported cp; - - memset(&cp, 0, sizeof(cp)); - - if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { - cp.le = 1; - cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); - } - - if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), - &cp); -} - -static void hci_cc_read_local_ext_features(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_local_ext_features *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - goto done; - - switch (rp->page) { - case 0: - memcpy(hdev->features, rp->features, 8); - break; - case 1: - memcpy(hdev->host_features, rp->features, 8); - break; - } - - if (test_bit(HCI_INIT, &hdev->flags) && hdev->features[4] & LMP_LE) - hci_set_le_support(hdev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); -} - -static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_flow_control_mode *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->flow_ctl_mode = rp->mode; - - hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status); -} - -static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_buffer_size *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->acl_mtu = __le16_to_cpu(rp->acl_mtu); - hdev->sco_mtu = rp->sco_mtu; - hdev->acl_pkts = __le16_to_cpu(rp->acl_max_pkt); - hdev->sco_pkts = __le16_to_cpu(rp->sco_max_pkt); - - if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) { - hdev->sco_mtu = 64; - hdev->sco_pkts = 8; - } - - hdev->acl_cnt = hdev->acl_pkts; - hdev->sco_cnt = hdev->sco_pkts; - - BT_DBG("%s acl mtu %d:%d sco mtu %d:%d", hdev->name, - hdev->acl_mtu, hdev->acl_pkts, - hdev->sco_mtu, hdev->sco_pkts); -} - -static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_read_bd_addr *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (!rp->status) - bacpy(&hdev->bdaddr, &rp->bdaddr); - - hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); -} - -static void hci_cc_read_data_block_size(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_data_block_size *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->block_mtu = __le16_to_cpu(rp->max_acl_len); - hdev->block_len = __le16_to_cpu(rp->block_len); - hdev->num_blocks = __le16_to_cpu(rp->num_blocks); - - hdev->block_cnt = hdev->num_blocks; - - BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu, - hdev->block_cnt, hdev->block_len); - - hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status); -} - -static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); -} - -static void hci_cc_read_local_amp_info(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_local_amp_info *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->amp_status = rp->amp_status; - hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); - hdev->amp_max_bw = __le32_to_cpu(rp->max_bw); - hdev->amp_min_latency = __le32_to_cpu(rp->min_latency); - hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu); - hdev->amp_type = rp->amp_type; - hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap); - hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size); - hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to); - hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); - - hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); -} - -static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); -} - -static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); -} - -static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); -} - -static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, status); -} - -static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); -} - -static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_pin_code_reply *rp = (void *) skb->data; - struct hci_cp_pin_code_reply *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); - - if (rp->status != 0) - goto unlock; - - cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); - if (!cp) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (conn) - conn->pin_length = cp->pin_len; - -unlock: - hci_dev_unlock(hdev); -} - -static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_pin_code_neg_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, - rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_le_read_buffer_size *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hdev->le_mtu = __le16_to_cpu(rp->le_mtu); - hdev->le_pkts = rp->le_max_pkt; - - hdev->le_cnt = hdev->le_pkts; - - BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts); - - hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); -} - -static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK, 0, - rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, - ACL_LINK, 0, rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK, - 0, rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_user_confirm_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, - ACL_LINK, 0, rp->status); - - hci_dev_unlock(hdev); -} - -static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_rp_read_local_oob_data *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - hci_dev_lock(hdev); - mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, - rp->randomizer, rp->status); - hci_dev_unlock(hdev); -} - -static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status); - - if (status) { - hci_dev_lock(hdev); - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } -} - -static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_cp_le_set_scan_enable *cp; - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); - if (!cp) - return; - - switch (cp->enable) { - case LE_SCANNING_ENABLED: - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status); - - if (status) { - hci_dev_lock(hdev); - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } - - set_bit(HCI_LE_SCAN, &hdev->dev_flags); - - cancel_delayed_work_sync(&hdev->adv_work); - - hci_dev_lock(hdev); - hci_adv_entries_clear(hdev); - hci_discovery_set_state(hdev, DISCOVERY_FINDING); - hci_dev_unlock(hdev); - break; - - case LE_SCANNING_DISABLED: - if (status) - return; - - clear_bit(HCI_LE_SCAN, &hdev->dev_flags); - - schedule_delayed_work(&hdev->adv_work, ADV_CLEAR_TIMEOUT); - - if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED) { - mgmt_interleaved_discovery(hdev); - } else { - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - hci_dev_unlock(hdev); - } - - break; - - default: - BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable); - break; - } -} - -static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_le_ltk_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); -} - -static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%x", hdev->name, rp->status); - - if (rp->status) - return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); -} - -static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_cp_write_le_host_supported *sent; - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%x", hdev->name, status); - - sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED); - if (!sent) - return; - - if (!status) { - if (sent->le) - hdev->host_features[0] |= LMP_HOST_LE; - else - hdev->host_features[0] &= ~LMP_HOST_LE; - } - - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_bit(HCI_INIT, &hdev->flags)) - mgmt_le_enable_complete(hdev, sent->le, status); - - hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); -} - -static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) -{ - BT_DBG("%s status 0x%x", hdev->name, status); - - if (status) { - hci_req_complete(hdev, HCI_OP_INQUIRY, status); - hci_conn_check_pending(hdev); - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_start_discovery_failed(hdev, status); - hci_dev_unlock(hdev); - return; - } - - set_bit(HCI_INQUIRY, &hdev->flags); - - hci_dev_lock(hdev); - hci_discovery_set_state(hdev, DISCOVERY_FINDING); - hci_dev_unlock(hdev); -} - -static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_create_conn *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_CONN); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->bdaddr), conn); - - if (status) { - if (conn && conn->state == BT_CONNECT) { - if (status != 0x0c || conn->attempt > 2) { - conn->state = BT_CLOSED; - hci_proto_connect_cfm(conn, status); - hci_conn_del(conn); - } else - conn->state = BT_CONNECT2; - } - } else { - if (!conn) { - conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); - if (conn) { - conn->out = true; - conn->link_mode |= HCI_LM_MASTER; - } else - BT_ERR("No memory for new connection"); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_add_sco *cp; - struct hci_conn *acl, *sco; - __u16 handle; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_ADD_SCO); - if (!cp) - return; - - handle = __le16_to_cpu(cp->handle); - - BT_DBG("%s handle %d", hdev->name, handle); - - hci_dev_lock(hdev); - - acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; - - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_auth_requested *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_AUTH_REQUESTED); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_set_conn_encrypt *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SET_CONN_ENCRYPT); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static int hci_outgoing_auth_needed(struct hci_dev *hdev, - struct hci_conn *conn) -{ - if (conn->state != BT_CONFIG || !conn->out) - return 0; - - if (conn->pending_sec_level == BT_SECURITY_SDP) - return 0; - - /* Only request authentication for SSP connections or non-SSP - * devices with sec_level HIGH or if MITM protection is requested */ - if (!hci_conn_ssp_enabled(conn) && - conn->pending_sec_level != BT_SECURITY_HIGH && - !(conn->auth_type & 0x01)) - return 0; - - return 1; -} - -static inline int hci_resolve_name(struct hci_dev *hdev, - struct inquiry_entry *e) -{ - struct hci_cp_remote_name_req cp; - - memset(&cp, 0, sizeof(cp)); - - bacpy(&cp.bdaddr, &e->data.bdaddr); - cp.pscan_rep_mode = e->data.pscan_rep_mode; - cp.pscan_mode = e->data.pscan_mode; - cp.clock_offset = e->data.clock_offset; - - return hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp); -} - -static bool hci_resolve_next_name(struct hci_dev *hdev) -{ - struct discovery_state *discov = &hdev->discovery; - struct inquiry_entry *e; - - if (list_empty(&discov->resolve)) - return false; - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); - if (hci_resolve_name(hdev, e) == 0) { - e->name_state = NAME_PENDING; - return true; - } - - return false; -} - -static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn, - bdaddr_t *bdaddr, u8 *name, u8 name_len) -{ - 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); - - if (discov->state == DISCOVERY_STOPPED) - return; - - if (discov->state == DISCOVERY_STOPPING) - goto discov_complete; - - if (discov->state != DISCOVERY_RESOLVING) - return; - - e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING); - if (e) { - e->name_state = NAME_KNOWN; - list_del(&e->list); - if (name) - mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00, - e->data.rssi, name, name_len); - } - - if (hci_resolve_next_name(hdev)) - return; - -discov_complete: - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); -} - -static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_remote_name_req *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - /* If successful wait for the name req complete event before - * checking for the need to do authentication */ - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_REMOTE_NAME_REQ); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - hci_check_pending_name(hdev, conn, &cp->bdaddr, NULL, 0); - - if (!conn) - goto unlock; - - if (!hci_outgoing_auth_needed(hdev, conn)) - goto unlock; - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - cp.handle = __cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_read_remote_features *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_read_remote_ext_features *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - if (conn->state == BT_CONFIG) { - hci_proto_connect_cfm(conn, status); - hci_conn_put(conn); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_setup_sync_conn *cp; - struct hci_conn *acl, *sco; - __u16 handle; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN); - if (!cp) - return; - - handle = __le16_to_cpu(cp->handle); - - BT_DBG("%s handle %d", hdev->name, handle); - - hci_dev_lock(hdev); - - acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl) { - sco = acl->link; - if (sco) { - sco->state = BT_CLOSED; - - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_sniff_mode *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_SNIFF_MODE); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); - - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) - hci_sco_setup(conn, status); - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_exit_sniff_mode *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_EXIT_SNIFF_MODE); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags); - - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) - hci_sco_setup(conn, status); - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) -{ - struct hci_cp_disconnect *cp; - struct hci_conn *conn; - - if (!status) - return; - - cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) - mgmt_disconnect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, status); - - hci_dev_unlock(hdev); -} - -static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_cp_le_create_conn *cp; - struct hci_conn *conn; - - BT_DBG("%s status 0x%x", hdev->name, status); - - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN); - if (!cp) - return; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr); - - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr), - conn); - - if (status) { - if (conn && conn->state == BT_CONNECT) { - conn->state = BT_CLOSED; - hci_proto_connect_cfm(conn, status); - hci_conn_del(conn); - } - } else { - if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr); - if (conn) { - conn->dst_type = cp->peer_addr_type; - conn->out = true; - } else { - BT_ERR("No memory for new connection"); - } - } - } - - hci_dev_unlock(hdev); -} - -static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) -{ - BT_DBG("%s status 0x%x", hdev->name, status); -} - -static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - struct discovery_state *discov = &hdev->discovery; - struct inquiry_entry *e; - - BT_DBG("%s status %d", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_INQUIRY, status); - - hci_conn_check_pending(hdev); - - if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) - return; - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - return; - - hci_dev_lock(hdev); - - if (discov->state != DISCOVERY_FINDING) - goto unlock; - - if (list_empty(&discov->resolve)) { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - goto unlock; - } - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED); - if (e && hci_resolve_name(hdev, e) == 0) { - e->name_state = NAME_PENDING; - hci_discovery_set_state(hdev, DISCOVERY_RESOLVING); - } else { - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - struct inquiry_info *info = (void *) (skb->data + 1); - int num_rsp = *((__u8 *) skb->data); - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; - - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = info->pscan_mode; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = 0x00; - data.ssp_mode = 0x00; - - name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, ssp, NULL, - 0); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_conn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - if (ev->link_type != SCO_LINK) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - conn->type = SCO_LINK; - } - - if (!ev->status) { - conn->handle = __le16_to_cpu(ev->handle); - - if (conn->type == ACL_LINK) { - conn->state = BT_CONFIG; - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - } else - conn->state = BT_CONNECTED; - - hci_conn_hold_device(conn); - hci_conn_add_sysfs(conn); - - if (test_bit(HCI_AUTH, &hdev->flags)) - conn->link_mode |= HCI_LM_AUTH; - - if (test_bit(HCI_ENCRYPT, &hdev->flags)) - conn->link_mode |= HCI_LM_ENCRYPT; - - /* Get remote features */ - if (conn->type == ACL_LINK) { - struct hci_cp_read_remote_features cp; - cp.handle = ev->handle; - hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, - sizeof(cp), &cp); - } - - /* Set packet type for incoming connection */ - if (!conn->out && hdev->hci_ver < BLUETOOTH_VER_2_0) { - struct hci_cp_change_conn_ptype cp; - cp.handle = ev->handle; - cp.pkt_type = cpu_to_le16(conn->pkt_type); - hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), - &cp); - } - } else { - conn->state = BT_CLOSED; - if (conn->type == ACL_LINK) - mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, - conn->dst_type, ev->status); - } - - if (conn->type == ACL_LINK) - hci_sco_setup(conn, ev->status); - - if (ev->status) { - hci_proto_connect_cfm(conn, ev->status); - hci_conn_del(conn); - } else if (ev->link_type != ACL_LINK) - hci_proto_connect_cfm(conn, ev->status); - -unlock: - hci_dev_unlock(hdev); - - hci_conn_check_pending(hdev); -} - -static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_conn_request *ev = (void *) skb->data; - int mask = hdev->link_mode; - - BT_DBG("%s bdaddr %s type 0x%x", hdev->name, - batostr(&ev->bdaddr), ev->link_type); - - mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); - - if ((mask & HCI_LM_ACCEPT) && - !hci_blacklist_lookup(hdev, &ev->bdaddr)) { - /* Connection accepted */ - struct inquiry_entry *ie; - struct hci_conn *conn; - - hci_dev_lock(hdev); - - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) - memcpy(ie->data.dev_class, ev->dev_class, 3); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - BT_ERR("No memory for new connection"); - hci_dev_unlock(hdev); - return; - } - } - - memcpy(conn->dev_class, ev->dev_class, 3); - conn->state = BT_CONNECT; - - hci_dev_unlock(hdev); - - if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { - struct hci_cp_accept_conn_req cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - - if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) - cp.role = 0x00; /* Become master */ - else - cp.role = 0x01; /* Remain slave */ - - hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), - &cp); - } else { - struct hci_cp_accept_sync_conn_req cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - cp.tx_bandwidth = cpu_to_le32(0x00001f40); - cp.rx_bandwidth = cpu_to_le32(0x00001f40); - cp.max_latency = cpu_to_le16(0xffff); - cp.content_format = cpu_to_le16(hdev->voice_setting); - cp.retrans_effort = 0xff; - - hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, - sizeof(cp), &cp); - } - } else { - /* Connection rejected */ - struct hci_cp_reject_conn_req cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = HCI_ERROR_REJ_BAD_ADDR; - hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); - } -} - -static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_disconn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (ev->status == 0) - conn->state = BT_CLOSED; - - if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && - (conn->type == ACL_LINK || conn->type == LE_LINK)) { - if (ev->status != 0) - mgmt_disconnect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, ev->status); - else - mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type); - } - - if (ev->status == 0) { - if (conn->type == ACL_LINK && conn->flush_key) - hci_remove_link_key(hdev, &conn->dst); - hci_proto_disconn_cfm(conn, ev->reason); - hci_conn_del(conn); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_auth_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (!ev->status) { - if (!hci_conn_ssp_enabled(conn) && - test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { - BT_INFO("re-auth of legacy device is not possible."); - } else { - conn->link_mode |= HCI_LM_AUTH; - conn->sec_level = conn->pending_sec_level; - } - } else { - mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, - ev->status); - } - - clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); - clear_bit(HCI_CONN_REAUTH_PEND, &conn->flags); - - if (conn->state == BT_CONFIG) { - if (!ev->status && hci_conn_ssp_enabled(conn)) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), - &cp); - } else { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } - } else { - hci_auth_cfm(conn, ev->status); - - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - hci_conn_put(conn); - } - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) { - if (!ev->status) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = ev->handle; - cp.encrypt = 0x01; - hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), - &cp); - } else { - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); - hci_encrypt_cfm(conn, ev->status, 0x00); - } - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_name *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_conn_check_pending(hdev); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto check_auth; - - if (ev->status == 0) - hci_check_pending_name(hdev, conn, &ev->bdaddr, ev->name, - strnlen(ev->name, HCI_MAX_NAME_LENGTH)); - else - hci_check_pending_name(hdev, conn, &ev->bdaddr, NULL, 0); - -check_auth: - if (!conn) - goto unlock; - - if (!hci_outgoing_auth_needed(hdev, conn)) - goto unlock; - - if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - cp.handle = __cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_encrypt_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) { - if (ev->encrypt) { - /* Encryption implies authentication */ - conn->link_mode |= HCI_LM_AUTH; - conn->link_mode |= HCI_LM_ENCRYPT; - conn->sec_level = conn->pending_sec_level; - } else - conn->link_mode &= ~HCI_LM_ENCRYPT; - } - - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); - - if (ev->status && conn->state == BT_CONNECTED) { - hci_acl_disconn(conn, 0x13); - hci_conn_put(conn); - goto unlock; - } - - if (conn->state == BT_CONFIG) { - if (!ev->status) - conn->state = BT_CONNECTED; - - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } else - hci_encrypt_cfm(conn, ev->status, ev->encrypt); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_change_link_key_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) - conn->link_mode |= HCI_LM_SECURE; - - clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); - - hci_key_change_cfm(conn, ev->status); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_features *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (!ev->status) - memcpy(conn->features, ev->features, 8); - - if (conn->state != BT_CONFIG) - goto unlock; - - if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) { - struct hci_cp_read_remote_ext_features cp; - cp.handle = ev->handle; - cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES, - sizeof(cp), &cp); - goto unlock; - } - - if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { - struct hci_cp_remote_name_req cp; - memset(&cp, 0, sizeof(cp)); - bacpy(&cp.bdaddr, &conn->dst); - 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); - - if (!hci_outgoing_auth_needed(hdev, conn)) { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static inline void hci_qos_setup_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_cmd_complete *ev = (void *) skb->data; - __u16 opcode; - - skb_pull(skb, sizeof(*ev)); - - opcode = __le16_to_cpu(ev->opcode); - - switch (opcode) { - case HCI_OP_INQUIRY_CANCEL: - hci_cc_inquiry_cancel(hdev, skb); - break; - - case HCI_OP_EXIT_PERIODIC_INQ: - hci_cc_exit_periodic_inq(hdev, skb); - break; - - case HCI_OP_REMOTE_NAME_REQ_CANCEL: - hci_cc_remote_name_req_cancel(hdev, skb); - break; - - case HCI_OP_ROLE_DISCOVERY: - hci_cc_role_discovery(hdev, skb); - break; - - case HCI_OP_READ_LINK_POLICY: - hci_cc_read_link_policy(hdev, skb); - break; - - case HCI_OP_WRITE_LINK_POLICY: - hci_cc_write_link_policy(hdev, skb); - break; - - case HCI_OP_READ_DEF_LINK_POLICY: - hci_cc_read_def_link_policy(hdev, skb); - break; - - case HCI_OP_WRITE_DEF_LINK_POLICY: - hci_cc_write_def_link_policy(hdev, skb); - break; - - case HCI_OP_RESET: - hci_cc_reset(hdev, skb); - break; - - case HCI_OP_WRITE_LOCAL_NAME: - hci_cc_write_local_name(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_NAME: - hci_cc_read_local_name(hdev, skb); - break; - - case HCI_OP_WRITE_AUTH_ENABLE: - hci_cc_write_auth_enable(hdev, skb); - break; - - case HCI_OP_WRITE_ENCRYPT_MODE: - hci_cc_write_encrypt_mode(hdev, skb); - break; - - case HCI_OP_WRITE_SCAN_ENABLE: - hci_cc_write_scan_enable(hdev, skb); - break; - - case HCI_OP_READ_CLASS_OF_DEV: - hci_cc_read_class_of_dev(hdev, skb); - break; - - case HCI_OP_WRITE_CLASS_OF_DEV: - hci_cc_write_class_of_dev(hdev, skb); - break; - - case HCI_OP_READ_VOICE_SETTING: - hci_cc_read_voice_setting(hdev, skb); - break; - - case HCI_OP_WRITE_VOICE_SETTING: - hci_cc_write_voice_setting(hdev, skb); - break; - - case HCI_OP_HOST_BUFFER_SIZE: - hci_cc_host_buffer_size(hdev, skb); - break; - - case HCI_OP_WRITE_SSP_MODE: - hci_cc_write_ssp_mode(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_VERSION: - hci_cc_read_local_version(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_COMMANDS: - hci_cc_read_local_commands(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_FEATURES: - hci_cc_read_local_features(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_EXT_FEATURES: - hci_cc_read_local_ext_features(hdev, skb); - break; - - case HCI_OP_READ_BUFFER_SIZE: - hci_cc_read_buffer_size(hdev, skb); - break; - - case HCI_OP_READ_BD_ADDR: - hci_cc_read_bd_addr(hdev, skb); - break; - - case HCI_OP_READ_DATA_BLOCK_SIZE: - hci_cc_read_data_block_size(hdev, skb); - break; - - case HCI_OP_WRITE_CA_TIMEOUT: - hci_cc_write_ca_timeout(hdev, skb); - break; - - case HCI_OP_READ_FLOW_CONTROL_MODE: - hci_cc_read_flow_control_mode(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_AMP_INFO: - hci_cc_read_local_amp_info(hdev, skb); - break; - - case HCI_OP_DELETE_STORED_LINK_KEY: - hci_cc_delete_stored_link_key(hdev, skb); - break; - - case HCI_OP_SET_EVENT_MASK: - hci_cc_set_event_mask(hdev, skb); - break; - - case HCI_OP_WRITE_INQUIRY_MODE: - hci_cc_write_inquiry_mode(hdev, skb); - break; - - case HCI_OP_READ_INQ_RSP_TX_POWER: - hci_cc_read_inq_rsp_tx_power(hdev, skb); - break; - - case HCI_OP_SET_EVENT_FLT: - hci_cc_set_event_flt(hdev, skb); - break; - - case HCI_OP_PIN_CODE_REPLY: - hci_cc_pin_code_reply(hdev, skb); - break; - - case HCI_OP_PIN_CODE_NEG_REPLY: - hci_cc_pin_code_neg_reply(hdev, skb); - break; - - case HCI_OP_READ_LOCAL_OOB_DATA: - hci_cc_read_local_oob_data_reply(hdev, skb); - break; - - case HCI_OP_LE_READ_BUFFER_SIZE: - hci_cc_le_read_buffer_size(hdev, skb); - break; - - case HCI_OP_USER_CONFIRM_REPLY: - hci_cc_user_confirm_reply(hdev, skb); - break; - - case HCI_OP_USER_CONFIRM_NEG_REPLY: - hci_cc_user_confirm_neg_reply(hdev, skb); - break; - - case HCI_OP_USER_PASSKEY_REPLY: - hci_cc_user_passkey_reply(hdev, skb); - break; - - case HCI_OP_USER_PASSKEY_NEG_REPLY: - hci_cc_user_passkey_neg_reply(hdev, skb); - break; - - case HCI_OP_LE_SET_SCAN_PARAM: - hci_cc_le_set_scan_param(hdev, skb); - break; - - case HCI_OP_LE_SET_SCAN_ENABLE: - hci_cc_le_set_scan_enable(hdev, skb); - break; - - case HCI_OP_LE_LTK_REPLY: - hci_cc_le_ltk_reply(hdev, skb); - break; - - case HCI_OP_LE_LTK_NEG_REPLY: - hci_cc_le_ltk_neg_reply(hdev, skb); - break; - - case HCI_OP_WRITE_LE_HOST_SUPPORTED: - hci_cc_write_le_host_supported(hdev, skb); - break; - - default: - BT_DBG("%s opcode 0x%x", hdev->name, opcode); - break; - } - - if (ev->opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); - - if (ev->ncmd) { - atomic_set(&hdev->cmd_cnt, 1); - if (!skb_queue_empty(&hdev->cmd_q)) - queue_work(hdev->workqueue, &hdev->cmd_work); - } -} - -static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_cmd_status *ev = (void *) skb->data; - __u16 opcode; - - skb_pull(skb, sizeof(*ev)); - - opcode = __le16_to_cpu(ev->opcode); - - switch (opcode) { - case HCI_OP_INQUIRY: - hci_cs_inquiry(hdev, ev->status); - break; - - case HCI_OP_CREATE_CONN: - hci_cs_create_conn(hdev, ev->status); - break; - - case HCI_OP_ADD_SCO: - hci_cs_add_sco(hdev, ev->status); - break; - - case HCI_OP_AUTH_REQUESTED: - hci_cs_auth_requested(hdev, ev->status); - break; - - case HCI_OP_SET_CONN_ENCRYPT: - hci_cs_set_conn_encrypt(hdev, ev->status); - break; - - case HCI_OP_REMOTE_NAME_REQ: - hci_cs_remote_name_req(hdev, ev->status); - break; - - case HCI_OP_READ_REMOTE_FEATURES: - hci_cs_read_remote_features(hdev, ev->status); - break; - - case HCI_OP_READ_REMOTE_EXT_FEATURES: - hci_cs_read_remote_ext_features(hdev, ev->status); - break; - - case HCI_OP_SETUP_SYNC_CONN: - hci_cs_setup_sync_conn(hdev, ev->status); - break; - - case HCI_OP_SNIFF_MODE: - hci_cs_sniff_mode(hdev, ev->status); - break; - - case HCI_OP_EXIT_SNIFF_MODE: - hci_cs_exit_sniff_mode(hdev, ev->status); - break; - - case HCI_OP_DISCONNECT: - hci_cs_disconnect(hdev, ev->status); - break; - - case HCI_OP_LE_CREATE_CONN: - hci_cs_le_create_conn(hdev, ev->status); - break; - - case HCI_OP_LE_START_ENC: - hci_cs_le_start_enc(hdev, ev->status); - break; - - default: - BT_DBG("%s opcode 0x%x", hdev->name, opcode); - break; - } - - if (ev->opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); - - if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { - atomic_set(&hdev->cmd_cnt, 1); - if (!skb_queue_empty(&hdev->cmd_q)) - queue_work(hdev->workqueue, &hdev->cmd_work); - } -} - -static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_role_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - if (!ev->status) { - if (ev->role) - conn->link_mode &= ~HCI_LM_MASTER; - else - conn->link_mode |= HCI_LM_MASTER; - } - - clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); - - hci_role_switch_cfm(conn, ev->status, ev->role); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_num_comp_pkts *ev = (void *) skb->data; - int i; - - if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_PACKET_BASED) { - BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); - return; - } - - if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + - ev->num_hndl * sizeof(struct hci_comp_pkts_info)) { - BT_DBG("%s bad parameters", hdev->name); - return; - } - - BT_DBG("%s num_hndl %d", hdev->name, ev->num_hndl); - - for (i = 0; i < ev->num_hndl; i++) { - struct hci_comp_pkts_info *info = &ev->handles[i]; - struct hci_conn *conn; - __u16 handle, count; - - handle = __le16_to_cpu(info->handle); - count = __le16_to_cpu(info->count); - - conn = hci_conn_hash_lookup_handle(hdev, handle); - if (!conn) - continue; - - conn->sent -= count; - - switch (conn->type) { - case ACL_LINK: - hdev->acl_cnt += count; - if (hdev->acl_cnt > hdev->acl_pkts) - hdev->acl_cnt = hdev->acl_pkts; - break; - - case LE_LINK: - if (hdev->le_pkts) { - hdev->le_cnt += count; - if (hdev->le_cnt > hdev->le_pkts) - hdev->le_cnt = hdev->le_pkts; - } else { - hdev->acl_cnt += count; - if (hdev->acl_cnt > hdev->acl_pkts) - hdev->acl_cnt = hdev->acl_pkts; - } - break; - - case SCO_LINK: - hdev->sco_cnt += count; - if (hdev->sco_cnt > hdev->sco_pkts) - hdev->sco_cnt = hdev->sco_pkts; - break; - - default: - BT_ERR("Unknown type %d conn %p", conn->type, conn); - break; - } - } - - queue_work(hdev->workqueue, &hdev->tx_work); -} - -static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_num_comp_blocks *ev = (void *) skb->data; - int i; - - if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) { - BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); - return; - } - - if (skb->len < sizeof(*ev) || skb->len < sizeof(*ev) + - ev->num_hndl * sizeof(struct hci_comp_blocks_info)) { - BT_DBG("%s bad parameters", hdev->name); - return; - } - - BT_DBG("%s num_blocks %d num_hndl %d", hdev->name, ev->num_blocks, - ev->num_hndl); - - for (i = 0; i < ev->num_hndl; i++) { - struct hci_comp_blocks_info *info = &ev->handles[i]; - struct hci_conn *conn; - __u16 handle, block_count; - - handle = __le16_to_cpu(info->handle); - block_count = __le16_to_cpu(info->blocks); - - conn = hci_conn_hash_lookup_handle(hdev, handle); - if (!conn) - continue; - - conn->sent -= block_count; - - switch (conn->type) { - case ACL_LINK: - hdev->block_cnt += block_count; - if (hdev->block_cnt > hdev->num_blocks) - hdev->block_cnt = hdev->num_blocks; - break; - - default: - BT_ERR("Unknown type %d conn %p", conn->type, conn); - break; - } - } - - queue_work(hdev->workqueue, &hdev->tx_work); -} - -static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_mode_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - conn->mode = ev->mode; - conn->interval = __le16_to_cpu(ev->interval); - - if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { - if (conn->mode == HCI_CM_ACTIVE) - set_bit(HCI_CONN_POWER_SAVE, &conn->flags); - else - clear_bit(HCI_CONN_POWER_SAVE, &conn->flags); - } - - if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->flags)) - hci_sco_setup(conn, ev->status); - } - - hci_dev_unlock(hdev); -} - -static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_pin_code_req *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - if (conn->state == BT_CONNECTED) { - hci_conn_hold(conn); - conn->disc_timeout = HCI_PAIRING_TIMEOUT; - hci_conn_put(conn); - } - - if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags)) - hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - else if (test_bit(HCI_MGMT, &hdev->dev_flags)) { - u8 secure; - - if (conn->pending_sec_level == BT_SECURITY_HIGH) - secure = 1; - else - secure = 0; - - mgmt_pin_code_request(hdev, &ev->bdaddr, secure); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_link_key_req *ev = (void *) skb->data; - struct hci_cp_link_key_reply cp; - struct hci_conn *conn; - struct link_key *key; - - BT_DBG("%s", hdev->name); - - if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) - return; - - hci_dev_lock(hdev); - - key = hci_find_link_key(hdev, &ev->bdaddr); - if (!key) { - BT_DBG("%s link key not found for %s", hdev->name, - batostr(&ev->bdaddr)); - goto not_found; - } - - BT_DBG("%s found key type %u for %s", hdev->name, key->type, - batostr(&ev->bdaddr)); - - if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && - key->type == HCI_LK_DEBUG_COMBINATION) { - BT_DBG("%s ignoring debug key", hdev->name); - goto not_found; - } - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - if (key->type == HCI_LK_UNAUTH_COMBINATION && - conn->auth_type != 0xff && - (conn->auth_type & 0x01)) { - BT_DBG("%s ignoring unauthenticated key", hdev->name); - goto not_found; - } - - if (key->type == HCI_LK_COMBINATION && key->pin_len < 16 && - conn->pending_sec_level == BT_SECURITY_HIGH) { - BT_DBG("%s ignoring key unauthenticated for high \ - security", hdev->name); - goto not_found; - } - - conn->key_type = key->type; - conn->pin_length = key->pin_len; - } - - bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.link_key, key->val, 16); - - hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp); - - hci_dev_unlock(hdev); - - return; - -not_found: - hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr); - hci_dev_unlock(hdev); -} - -static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_link_key_notify *ev = (void *) skb->data; - struct hci_conn *conn; - u8 pin_len = 0; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - hci_conn_hold(conn); - conn->disc_timeout = HCI_DISCONN_TIMEOUT; - pin_len = conn->pin_length; - - if (ev->key_type != HCI_LK_CHANGED_COMBINATION) - conn->key_type = ev->key_type; - - hci_conn_put(conn); - } - - if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags)) - hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, - ev->key_type, pin_len); - - hci_dev_unlock(hdev); -} - -static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_clock_offset *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn && !ev->status) { - struct inquiry_entry *ie; - - ie = hci_inquiry_cache_lookup(hdev, &conn->dst); - if (ie) { - ie->data.clock_offset = ev->clock_offset; - ie->timestamp = jiffies; - } - } - - hci_dev_unlock(hdev); -} - -static inline void hci_pkt_type_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_pkt_type_change *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn && !ev->status) - conn->pkt_type = __le16_to_cpu(ev->pkt_type); - - hci_dev_unlock(hdev); -} - -static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_pscan_rep_mode *ev = (void *) skb->data; - struct inquiry_entry *ie; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) { - ie->data.pscan_rep_mode = ev->pscan_rep_mode; - ie->timestamp = jiffies; - } - - hci_dev_unlock(hdev); -} - -static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - int num_rsp = *((__u8 *) skb->data); - bool name_known, ssp; - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { - struct inquiry_info_with_rssi_and_pscan_mode *info; - info = (void *) (skb->data + 1); - - for (; num_rsp; num_rsp--, info++) { - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = info->pscan_mode; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - data.ssp_mode = 0x00; - - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); - } - } else { - struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); - - for (; num_rsp; num_rsp--, info++) { - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = 0x00; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, - !name_known, ssp, NULL, 0); - } - } - - hci_dev_unlock(hdev); -} - -static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_ext_features *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (!conn) - goto unlock; - - if (!ev->status && ev->page == 0x01) { - struct inquiry_entry *ie; - - ie = hci_inquiry_cache_lookup(hdev, &conn->dst); - if (ie) - ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP); - - if (ev->features[0] & LMP_HOST_SSP) - set_bit(HCI_CONN_SSP_ENABLED, &conn->flags); - } - - if (conn->state != BT_CONFIG) - goto unlock; - - if (!ev->status && !test_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) { - struct hci_cp_remote_name_req cp; - memset(&cp, 0, sizeof(cp)); - bacpy(&cp.bdaddr, &conn->dst); - 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); - - if (!hci_outgoing_auth_needed(hdev, conn)) { - conn->state = BT_CONNECTED; - hci_proto_connect_cfm(conn, ev->status); - hci_conn_put(conn); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_sync_conn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - if (ev->link_type == ESCO_LINK) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - conn->type = SCO_LINK; - } - - switch (ev->status) { - case 0x00: - conn->handle = __le16_to_cpu(ev->handle); - conn->state = BT_CONNECTED; - - hci_conn_hold_device(conn); - hci_conn_add_sysfs(conn); - break; - - case 0x11: /* Unsupported Feature or Parameter Value */ - case 0x1c: /* SCO interval rejected */ - case 0x1a: /* Unsupported Remote Feature */ - case 0x1f: /* Unspecified error */ - if (conn->out && conn->attempt < 2) { - conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) | - (hdev->esco_type & EDR_ESCO_MASK); - hci_setup_sync(conn, conn->link->handle); - goto unlock; - } - /* fall through */ - - default: - conn->state = BT_CLOSED; - break; - } - - hci_proto_connect_cfm(conn, ev->status); - if (ev->status) - hci_conn_del(conn); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_sniff_subrate *ev = (void *) skb->data; - - BT_DBG("%s status %d", hdev->name, ev->status); -} - -static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - struct extended_inquiry_info *info = (void *) (skb->data + 1); - int num_rsp = *((__u8 *) skb->data); - size_t eir_len; - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; - - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = 0x00; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - data.ssp_mode = 0x01; - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - name_known = eir_has_data_type(info->data, - sizeof(info->data), - EIR_NAME_COMPLETE); - else - name_known = true; - - name_known = hci_inquiry_cache_update(hdev, &data, name_known, - &ssp); - eir_len = eir_get_length(info->data, sizeof(info->data)); - mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, !name_known, - ssp, info->data, eir_len); - } - - hci_dev_unlock(hdev); -} - -static inline u8 hci_get_auth_req(struct hci_conn *conn) -{ - /* If remote requests dedicated bonding follow that lead */ - if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) { - /* If both remote and local IO capabilities allow MITM - * protection then require it, otherwise don't */ - if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) - return 0x02; - else - return 0x03; - } - - /* If remote requests no-bonding follow that lead */ - if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01) - return conn->remote_auth | (conn->auth_type & 0x01); - - return conn->auth_type; -} - -static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_io_capa_request *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - hci_conn_hold(conn); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto unlock; - - if (test_bit(HCI_PAIRABLE, &hdev->dev_flags) || - (conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) { - struct hci_cp_io_capability_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - /* Change the IO capability from KeyboardDisplay - * to DisplayYesNo as it is not supported by BT spec. */ - cp.capability = (conn->io_capability == 0x04) ? - 0x01 : conn->io_capability; - conn->auth_type = hci_get_auth_req(conn); - cp.authentication = conn->auth_type; - - if ((conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)) && - hci_find_remote_oob_data(hdev, &conn->dst)) - cp.oob_data = 0x01; - else - cp.oob_data = 0x00; - - hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY, - sizeof(cp), &cp); - } else { - struct hci_cp_io_capability_neg_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED; - - hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, - sizeof(cp), &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_io_capa_reply *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - conn->remote_cap = ev->capability; - conn->remote_auth = ev->authentication; - if (ev->oob_data) - set_bit(HCI_CONN_REMOTE_OOB, &conn->flags); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_user_confirm_req *ev = (void *) skb->data; - int loc_mitm, rem_mitm, confirm_hint = 0; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto unlock; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - loc_mitm = (conn->auth_type & 0x01); - rem_mitm = (conn->remote_auth & 0x01); - - /* If we require MITM but the remote device can't provide that - * (it has NoInputNoOutput) then reject the confirmation - * request. The only exception is when we're dedicated bonding - * initiators (connect_cfm_cb set) since then we always have the MITM - * bit set. */ - if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) { - BT_DBG("Rejecting request: remote device can't provide MITM"); - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - goto unlock; - } - - /* If no side requires MITM protection; auto-accept */ - if ((!loc_mitm || conn->remote_cap == 0x03) && - (!rem_mitm || conn->io_capability == 0x03)) { - - /* If we're not the initiators request authorization to - * proceed from user space (mgmt_user_confirm with - * confirm_hint set to 1). */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - BT_DBG("Confirming auto-accept as acceptor"); - confirm_hint = 1; - goto confirm; - } - - BT_DBG("Auto-accept of user confirmation with %ums delay", - hdev->auto_accept_delay); - - if (hdev->auto_accept_delay > 0) { - int delay = msecs_to_jiffies(hdev->auto_accept_delay); - mod_timer(&conn->auto_accept_timer, jiffies + delay); - goto unlock; - } - - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, - sizeof(ev->bdaddr), &ev->bdaddr); - goto unlock; - } - -confirm: - mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey, - confirm_hint); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_user_passkey_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_user_passkey_req *ev = (void *) skb->data; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0); - - hci_dev_unlock(hdev); -} - -static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_simple_pair_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (!conn) - goto unlock; - - /* To avoid duplicate auth_failed events to user space we check - * the HCI_CONN_AUTH_PEND flag which will be set if we - * initiated the authentication. A traditional auth_complete - * event gets always produced as initiator and is also mapped to - * the mgmt_auth_failed event */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0) - mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, - ev->status); - - hci_conn_put(conn); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_host_features *ev = (void *) skb->data; - struct inquiry_entry *ie; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) - ie->data.ssp_mode = (ev->features[0] & LMP_HOST_SSP); - - hci_dev_unlock(hdev); -} - -static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_remote_oob_data_request *ev = (void *) skb->data; - struct oob_data *data; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - goto unlock; - - data = hci_find_remote_oob_data(hdev, &ev->bdaddr); - if (data) { - struct hci_cp_remote_oob_data_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - memcpy(cp.hash, data->hash, sizeof(cp.hash)); - memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer)); - - hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp), - &cp); - } else { - struct hci_cp_remote_oob_data_neg_reply cp; - - bacpy(&cp.bdaddr, &ev->bdaddr); - hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp), - &cp); - } - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_le_conn_complete *ev = (void *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); - if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); - if (!conn) { - BT_ERR("No memory for new connection"); - hci_dev_unlock(hdev); - return; - } - - conn->dst_type = ev->bdaddr_type; - } - - if (ev->status) { - mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, - conn->dst_type, ev->status); - hci_proto_connect_cfm(conn, ev->status); - conn->state = BT_CLOSED; - hci_conn_del(conn); - goto unlock; - } - - if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) - mgmt_device_connected(hdev, &ev->bdaddr, conn->type, - conn->dst_type, 0, NULL, 0, NULL); - - conn->sec_level = BT_SECURITY_LOW; - conn->handle = __le16_to_cpu(ev->handle); - conn->state = BT_CONNECTED; - - hci_conn_hold_device(conn); - hci_conn_add_sysfs(conn); - - hci_proto_connect_cfm(conn, ev->status); - -unlock: - hci_dev_unlock(hdev); -} - -static inline void hci_le_adv_report_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - u8 num_reports = skb->data[0]; - void *ptr = &skb->data[1]; - s8 rssi; - - hci_dev_lock(hdev); - - while (num_reports--) { - struct hci_ev_le_advertising_info *ev = ptr; - - hci_add_adv_entry(hdev, ev); - - rssi = ev->data[ev->length]; - mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type, - NULL, rssi, 0, 1, ev->data, ev->length); - - ptr += sizeof(*ev) + ev->length + 1; - } - - hci_dev_unlock(hdev); -} - -static inline void hci_le_ltk_request_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - struct hci_ev_le_ltk_req *ev = (void *) skb->data; - struct hci_cp_le_ltk_reply cp; - struct hci_cp_le_ltk_neg_reply neg; - struct hci_conn *conn; - struct smp_ltk *ltk; - - BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle)); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn == NULL) - goto not_found; - - ltk = hci_find_ltk(hdev, ev->ediv, ev->random); - if (ltk == NULL) - goto not_found; - - memcpy(cp.ltk, ltk->val, sizeof(ltk->val)); - cp.handle = cpu_to_le16(conn->handle); - - if (ltk->authenticated) - conn->sec_level = BT_SECURITY_HIGH; - - hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp); - - if (ltk->type & HCI_SMP_STK) { - list_del(<k->list); - kfree(ltk); - } - - hci_dev_unlock(hdev); - - return; - -not_found: - neg.handle = ev->handle; - hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg); - hci_dev_unlock(hdev); -} - -static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_le_meta *le_ev = (void *) skb->data; - - skb_pull(skb, sizeof(*le_ev)); - - switch (le_ev->subevent) { - case HCI_EV_LE_CONN_COMPLETE: - hci_le_conn_complete_evt(hdev, skb); - break; - - case HCI_EV_LE_ADVERTISING_REPORT: - hci_le_adv_report_evt(hdev, skb); - break; - - case HCI_EV_LE_LTK_REQ: - hci_le_ltk_request_evt(hdev, skb); - break; - - default: - break; - } -} - -void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_event_hdr *hdr = (void *) skb->data; - __u8 event = hdr->evt; - - skb_pull(skb, HCI_EVENT_HDR_SIZE); - - switch (event) { - case HCI_EV_INQUIRY_COMPLETE: - hci_inquiry_complete_evt(hdev, skb); - break; - - case HCI_EV_INQUIRY_RESULT: - hci_inquiry_result_evt(hdev, skb); - break; - - case HCI_EV_CONN_COMPLETE: - hci_conn_complete_evt(hdev, skb); - break; - - case HCI_EV_CONN_REQUEST: - hci_conn_request_evt(hdev, skb); - break; - - case HCI_EV_DISCONN_COMPLETE: - hci_disconn_complete_evt(hdev, skb); - break; - - case HCI_EV_AUTH_COMPLETE: - hci_auth_complete_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_NAME: - hci_remote_name_evt(hdev, skb); - break; - - case HCI_EV_ENCRYPT_CHANGE: - hci_encrypt_change_evt(hdev, skb); - break; - - case HCI_EV_CHANGE_LINK_KEY_COMPLETE: - hci_change_link_key_complete_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_FEATURES: - hci_remote_features_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_VERSION: - hci_remote_version_evt(hdev, skb); - break; - - case HCI_EV_QOS_SETUP_COMPLETE: - hci_qos_setup_complete_evt(hdev, skb); - break; - - case HCI_EV_CMD_COMPLETE: - hci_cmd_complete_evt(hdev, skb); - break; - - case HCI_EV_CMD_STATUS: - hci_cmd_status_evt(hdev, skb); - break; - - case HCI_EV_ROLE_CHANGE: - hci_role_change_evt(hdev, skb); - break; - - case HCI_EV_NUM_COMP_PKTS: - hci_num_comp_pkts_evt(hdev, skb); - break; - - case HCI_EV_MODE_CHANGE: - hci_mode_change_evt(hdev, skb); - break; - - case HCI_EV_PIN_CODE_REQ: - hci_pin_code_request_evt(hdev, skb); - break; - - case HCI_EV_LINK_KEY_REQ: - hci_link_key_request_evt(hdev, skb); - break; - - case HCI_EV_LINK_KEY_NOTIFY: - hci_link_key_notify_evt(hdev, skb); - break; - - case HCI_EV_CLOCK_OFFSET: - hci_clock_offset_evt(hdev, skb); - break; - - case HCI_EV_PKT_TYPE_CHANGE: - hci_pkt_type_change_evt(hdev, skb); - break; - - case HCI_EV_PSCAN_REP_MODE: - hci_pscan_rep_mode_evt(hdev, skb); - break; - - case HCI_EV_INQUIRY_RESULT_WITH_RSSI: - hci_inquiry_result_with_rssi_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_EXT_FEATURES: - hci_remote_ext_features_evt(hdev, skb); - break; - - case HCI_EV_SYNC_CONN_COMPLETE: - hci_sync_conn_complete_evt(hdev, skb); - break; - - case HCI_EV_SYNC_CONN_CHANGED: - hci_sync_conn_changed_evt(hdev, skb); - break; - - case HCI_EV_SNIFF_SUBRATE: - hci_sniff_subrate_evt(hdev, skb); - break; - - case HCI_EV_EXTENDED_INQUIRY_RESULT: - hci_extended_inquiry_result_evt(hdev, skb); - break; - - case HCI_EV_IO_CAPA_REQUEST: - hci_io_capa_request_evt(hdev, skb); - break; - - case HCI_EV_IO_CAPA_REPLY: - hci_io_capa_reply_evt(hdev, skb); - break; - - case HCI_EV_USER_CONFIRM_REQUEST: - hci_user_confirm_request_evt(hdev, skb); - break; - - case HCI_EV_USER_PASSKEY_REQUEST: - hci_user_passkey_request_evt(hdev, skb); - break; - - case HCI_EV_SIMPLE_PAIR_COMPLETE: - hci_simple_pair_complete_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_HOST_FEATURES: - hci_remote_host_features_evt(hdev, skb); - break; - - case HCI_EV_LE_META: - hci_le_meta_evt(hdev, skb); - break; - - case HCI_EV_REMOTE_OOB_DATA_REQUEST: - hci_remote_oob_data_request_evt(hdev, skb); - break; - - case HCI_EV_NUM_COMP_BLOCKS: - hci_num_comp_blocks_evt(hdev, skb); - break; - - default: - BT_DBG("%s event 0x%x", hdev->name, event); - break; - } - - kfree_skb(skb); - hdev->stat.evt_rx++; -} - -/* Generate internal stack event */ -void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) -{ - struct hci_event_hdr *hdr; - struct hci_ev_stack_internal *ev; - struct sk_buff *skb; - - skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC); - if (!skb) - return; - - hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE); - hdr->evt = HCI_EV_STACK_INTERNAL; - hdr->plen = sizeof(*ev) + dlen; - - ev = (void *) skb_put(skb, sizeof(*ev) + dlen); - ev->type = type; - memcpy(ev->data, data, dlen); - - bt_cb(skb)->incoming = 1; - __net_timestamp(skb); - - bt_cb(skb)->pkt_type = HCI_EVENT_PKT; - skb->dev = (void *) hdev; - hci_send_to_sock(hdev, skb); - kfree_skb(skb); -} diff --git a/net/bluetooth_tizen/hci_sock.c b/net/bluetooth_tizen/hci_sock.c deleted file mode 100644 index 63c1516..0000000 --- a/net/bluetooth_tizen/hci_sock.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI sockets. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/skbuff.h> -#include <linux/workqueue.h> -#include <linux/interrupt.h> -#include <linux/compat.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/hci_mon.h> - -static atomic_t monitor_promisc = ATOMIC_INIT(0); - -/* ----- HCI socket interface ----- */ - -static inline int hci_test_bit(int nr, void *addr) -{ - return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); -} - -/* Security filter */ -static struct hci_sec_filter hci_sec_filter = { - /* Packet types */ - 0x10, - /* Events */ - { 0x1000d9fe, 0x0000b00c }, - /* Commands */ - { - { 0x0 }, - /* OGF_LINK_CTL */ - { 0xbe000006, 0x00000001, 0x00000000, 0x00 }, - /* OGF_LINK_POLICY */ - { 0x00005200, 0x00000000, 0x00000000, 0x00 }, - /* OGF_HOST_CTL */ - { 0xaab00200, 0x2b402aaa, 0x05220154, 0x00 }, - /* OGF_INFO_PARAM */ - { 0x000002be, 0x00000000, 0x00000000, 0x00 }, - /* OGF_STATUS_PARAM */ - { 0x000000ea, 0x00000000, 0x00000000, 0x00 } - } -}; - -static struct bt_sock_list hci_sk_list = { - .lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock) -}; - -/* Send frame to RAW socket */ -void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct sock *sk; - struct hlist_node *node; - struct sk_buff *skb_copy = NULL; - - BT_DBG("hdev %p len %d", hdev, skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct hci_filter *flt; - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) - continue; - - /* Don't send frame to the socket it came from */ - if (skb->sk == sk) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) - continue; - - /* Apply filter */ - flt = &hci_pi(sk)->filter; - - if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ? - 0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) - continue; - - if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) { - register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); - - if (!hci_test_bit(evt, &flt->event_mask)) - continue; - - if (flt->opcode && - ((evt == HCI_EV_CMD_COMPLETE && - flt->opcode != - get_unaligned((__le16 *)(skb->data + 3))) || - (evt == HCI_EV_CMD_STATUS && - flt->opcode != - get_unaligned((__le16 *)(skb->data + 4))))) - continue; - } - - if (!skb_copy) { - /* Create a private copy with headroom */ - skb_copy = __pskb_copy(skb, 1, GFP_ATOMIC); - if (!skb_copy) - continue; - - /* Put type byte before the data */ - memcpy(skb_push(skb_copy, 1), &bt_cb(skb)->pkt_type, 1); - } - - nskb = skb_clone(skb_copy, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); - - kfree_skb(skb_copy); -} - -/* Send frame to control socket */ -void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk) -{ - struct sock *sk; - struct hlist_node *node; - - BT_DBG("len %d", skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct sk_buff *nskb; - - /* Skip the original socket */ - if (sk == skip_sk) - continue; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - -/* Send frame to monitor socket */ -void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct sock *sk; - struct hlist_node *node; - struct sk_buff *skb_copy = NULL; - __le16 opcode; - - if (!atomic_read(&monitor_promisc)) - return; - - BT_DBG("hdev %p len %d", hdev, skb->len); - - switch (bt_cb(skb)->pkt_type) { - case HCI_COMMAND_PKT: - opcode = __constant_cpu_to_le16(HCI_MON_COMMAND_PKT); - break; - case HCI_EVENT_PKT: - opcode = __constant_cpu_to_le16(HCI_MON_EVENT_PKT); - break; - case HCI_ACLDATA_PKT: - if (bt_cb(skb)->incoming) - opcode = __constant_cpu_to_le16(HCI_MON_ACL_RX_PKT); - else - opcode = __constant_cpu_to_le16(HCI_MON_ACL_TX_PKT); - break; - case HCI_SCODATA_PKT: - if (bt_cb(skb)->incoming) - opcode = __constant_cpu_to_le16(HCI_MON_SCO_RX_PKT); - else - opcode = __constant_cpu_to_le16(HCI_MON_SCO_TX_PKT); - break; - default: - return; - } - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) - continue; - - if (!skb_copy) { - struct hci_mon_hdr *hdr; - - /* Create a private copy with headroom */ - skb_copy = __pskb_copy(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC); - if (!skb_copy) - continue; - - /* Put header before the data */ - hdr = (void *) skb_push(skb_copy, HCI_MON_HDR_SIZE); - hdr->opcode = opcode; - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(skb->len); - } - - nskb = skb_clone(skb_copy, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); - - kfree_skb(skb_copy); -} - -static void send_monitor_event(struct sk_buff *skb) -{ - struct sock *sk; - struct hlist_node *node; - - BT_DBG("len %d", skb->len); - - read_lock(&hci_sk_list.lock); - - sk_for_each(sk, node, &hci_sk_list.head) { - struct sk_buff *nskb; - - if (sk->sk_state != BT_BOUND) - continue; - - if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR) - continue; - - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (sock_queue_rcv_skb(sk, nskb)) - kfree_skb(nskb); - } - - read_unlock(&hci_sk_list.lock); -} - -static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event) -{ - struct hci_mon_hdr *hdr; - struct hci_mon_new_index *ni; - struct sk_buff *skb; - __le16 opcode; - - switch (event) { - case HCI_DEV_REG: - skb = bt_skb_alloc(HCI_MON_NEW_INDEX_SIZE, GFP_ATOMIC); - if (!skb) - return NULL; - - ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE); - ni->type = hdev->dev_type; - ni->bus = hdev->bus; - bacpy(&ni->bdaddr, &hdev->bdaddr); - memcpy(ni->name, hdev->name, 8); - - opcode = __constant_cpu_to_le16(HCI_MON_NEW_INDEX); - break; - - case HCI_DEV_UNREG: - skb = bt_skb_alloc(0, GFP_ATOMIC); - if (!skb) - return NULL; - - opcode = __constant_cpu_to_le16(HCI_MON_DEL_INDEX); - break; - - default: - return NULL; - } - - __net_timestamp(skb); - - hdr = (void *) skb_push(skb, HCI_MON_HDR_SIZE); - hdr->opcode = opcode; - hdr->index = cpu_to_le16(hdev->id); - hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); - - return skb; -} - -static void send_monitor_replay(struct sock *sk) -{ - struct hci_dev *hdev; - - read_lock(&hci_dev_list_lock); - - list_for_each_entry(hdev, &hci_dev_list, list) { - struct sk_buff *skb; - - skb = create_monitor_event(hdev, HCI_DEV_REG); - if (!skb) - continue; - - if (sock_queue_rcv_skb(sk, skb)) - kfree_skb(skb); - } - - read_unlock(&hci_dev_list_lock); -} - -static int hci_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct hci_dev *hdev; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - hdev = hci_pi(sk)->hdev; - - if (hci_pi(sk)->channel == HCI_CHANNEL_MONITOR) - atomic_dec(&monitor_promisc); - - bt_sock_unlink(&hci_sk_list, sk); - - if (hdev) { - atomic_dec(&hdev->promisc); - hci_dev_put(hdev); - } - - sock_orphan(sk); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - - sock_put(sk); - return 0; -} - -static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) -{ - bdaddr_t bdaddr; - int err; - - if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) - return -EFAULT; - - hci_dev_lock(hdev); - - err = hci_blacklist_add(hdev, &bdaddr, 0); - - hci_dev_unlock(hdev); - - return err; -} - -static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) -{ - bdaddr_t bdaddr; - int err; - - if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) - return -EFAULT; - - hci_dev_lock(hdev); - - err = hci_blacklist_del(hdev, &bdaddr, 0); - - hci_dev_unlock(hdev); - - return err; -} - -/* Ioctls that require bound socket */ -static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) -{ - struct hci_dev *hdev = hci_pi(sk)->hdev; - - if (!hdev) - return -EBADFD; - - switch (cmd) { - case HCISETRAW: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - return -EPERM; - - if (arg) - set_bit(HCI_RAW, &hdev->flags); - else - clear_bit(HCI_RAW, &hdev->flags); - - return 0; - - case HCIGETCONNINFO: - return hci_get_conn_info(hdev, (void __user *) arg); - - case HCIGETAUTHINFO: - return hci_get_auth_info(hdev, (void __user *) arg); - - case HCIBLOCKADDR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_sock_blacklist_add(hdev, (void __user *) arg); - - case HCIUNBLOCKADDR: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_sock_blacklist_del(hdev, (void __user *) arg); - - default: - if (hdev->ioctl) - return hdev->ioctl(hdev, cmd, arg); - return -EINVAL; - } -} - -static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - void __user *argp = (void __user *) arg; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case HCIGETDEVLIST: - return hci_get_dev_list(argp); - - case HCIGETDEVINFO: - return hci_get_dev_info(argp); - - case HCIGETCONNLIST: - return hci_get_conn_list(argp); - - case HCIDEVUP: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_open(arg); - - case HCIDEVDOWN: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_close(arg); - - case HCIDEVRESET: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_reset(arg); - - case HCIDEVRESTAT: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_reset_stat(arg); - - case HCISETSCAN: - case HCISETAUTH: - case HCISETENCRYPT: - case HCISETPTYPE: - case HCISETLINKPOL: - case HCISETLINKMODE: - case HCISETACLMTU: - case HCISETSCOMTU: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - return hci_dev_cmd(cmd, argp); - - case HCIINQUIRY: - return hci_inquiry(argp); - - default: - lock_sock(sk); - err = hci_sock_bound_ioctl(sk, cmd, arg); - release_sock(sk); - return err; - } -} - -static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -{ - struct sockaddr_hci haddr; - struct sock *sk = sock->sk; - struct hci_dev *hdev = NULL; - int len, err = 0; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!addr) - return -EINVAL; - - memset(&haddr, 0, sizeof(haddr)); - len = min_t(unsigned int, sizeof(haddr), addr_len); - memcpy(&haddr, addr, len); - - if (haddr.hci_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state == BT_BOUND) { - err = -EALREADY; - goto done; - } - - switch (haddr.hci_channel) { - case HCI_CHANNEL_RAW: - if (hci_pi(sk)->hdev) { - err = -EALREADY; - goto done; - } - - if (haddr.hci_dev != HCI_DEV_NONE) { - hdev = hci_dev_get(haddr.hci_dev); - if (!hdev) { - err = -ENODEV; - goto done; - } - - atomic_inc(&hdev->promisc); - } - - hci_pi(sk)->hdev = hdev; - break; - - case HCI_CHANNEL_CONTROL: - if (haddr.hci_dev != HCI_DEV_NONE) { - err = -EINVAL; - goto done; - } - - if (!capable(CAP_NET_ADMIN)) { - err = -EPERM; - goto done; - } - - break; - - case HCI_CHANNEL_MONITOR: - if (haddr.hci_dev != HCI_DEV_NONE) { - err = -EINVAL; - goto done; - } - - if (!capable(CAP_NET_RAW)) { - err = -EPERM; - goto done; - } - - send_monitor_replay(sk); - - atomic_inc(&monitor_promisc); - break; - - default: - err = -EINVAL; - goto done; - } - - - hci_pi(sk)->channel = haddr.hci_channel; - sk->sk_state = BT_BOUND; - -done: - release_sock(sk); - return err; -} - -static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) -{ - struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; - struct sock *sk = sock->sk; - struct hci_dev *hdev = hci_pi(sk)->hdev; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!hdev) - return -EBADFD; - - lock_sock(sk); - - *addr_len = sizeof(*haddr); - haddr->hci_family = AF_BLUETOOTH; - haddr->hci_dev = hdev->id; - - release_sock(sk); - return 0; -} - -static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) -{ - __u32 mask = hci_pi(sk)->cmsg_mask; - - if (mask & HCI_CMSG_DIR) { - int incoming = bt_cb(skb)->incoming; - put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(incoming), &incoming); - } - - if (mask & HCI_CMSG_TSTAMP) { -#ifdef CONFIG_COMPAT - struct compat_timeval ctv; -#endif - struct timeval tv; - void *data; - int len; - - skb_get_timestamp(skb, &tv); - - data = &tv; - len = sizeof(tv); -#ifdef CONFIG_COMPAT - if (msg->msg_flags & MSG_CMSG_COMPAT) { - ctv.tv_sec = tv.tv_sec; - ctv.tv_usec = tv.tv_usec; - data = &ctv; - len = sizeof(ctv); - } -#endif - - put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, len, data); - } -} - -static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len, int flags) -{ - int noblock = flags & MSG_DONTWAIT; - struct sock *sk = sock->sk; - struct sk_buff *skb; - int copied, err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (flags & (MSG_OOB)) - return -EOPNOTSUPP; - - if (sk->sk_state == BT_CLOSED) - return 0; - - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - return err; - - msg->msg_namelen = 0; - - copied = skb->len; - if (len < copied) { - msg->msg_flags |= MSG_TRUNC; - copied = len; - } - - skb_reset_transport_header(skb); - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - - switch (hci_pi(sk)->channel) { - case HCI_CHANNEL_RAW: - hci_sock_cmsg(sk, msg, skb); - break; - case HCI_CHANNEL_CONTROL: - case HCI_CHANNEL_MONITOR: - sock_recv_timestamp(msg, sk, skb); - break; - } - - skb_free_datagram(sk, skb); - - return err ? : copied; -} - -static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct hci_dev *hdev; - struct sk_buff *skb; - int err; - - BT_DBG("sock %p sk %p", sock, sk); - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) - return -EINVAL; - - if (len < 4 || len > HCI_MAX_FRAME_SIZE) - return -EINVAL; - - lock_sock(sk); - - switch (hci_pi(sk)->channel) { - case HCI_CHANNEL_RAW: - break; - case HCI_CHANNEL_CONTROL: - err = mgmt_control(sk, msg, len); - goto done; - case HCI_CHANNEL_MONITOR: - err = -EOPNOTSUPP; - goto done; - default: - err = -EINVAL; - goto done; - } - - hdev = hci_pi(sk)->hdev; - if (!hdev) { - err = -EBADFD; - goto done; - } - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = -ENETDOWN; - goto done; - } - - skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - goto done; - - if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { - err = -EFAULT; - goto drop; - } - - bt_cb(skb)->pkt_type = *((unsigned char *) skb->data); - skb_pull(skb, 1); - skb->dev = (void *) hdev; - - if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { - u16 opcode = get_unaligned_le16(skb->data); - u16 ogf = hci_opcode_ogf(opcode); - u16 ocf = hci_opcode_ocf(opcode); - - if (((ogf > HCI_SFLT_MAX_OGF) || - !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && - !capable(CAP_NET_RAW)) { - err = -EPERM; - goto drop; - } - - if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) { - skb_queue_tail(&hdev->raw_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); - } else { - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - } else { - if (!capable(CAP_NET_RAW)) { - err = -EPERM; - goto drop; - } - - skb_queue_tail(&hdev->raw_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); - } - - err = len; - -done: - release_sock(sk); - return err; - -drop: - kfree_skb(skb); - goto done; -} - -static int hci_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int len) -{ - struct hci_ufilter uf = { .opcode = 0 }; - struct sock *sk = sock->sk; - int err = 0, opt = 0; - - BT_DBG("sk %p, opt %d", sk, optname); - - lock_sock(sk); - - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { - err = -EINVAL; - goto done; - } - - switch (optname) { - case HCI_DATA_DIR: - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - break; - } - - if (opt) - hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR; - else - hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR; - break; - - case HCI_TIME_STAMP: - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - break; - } - - if (opt) - hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP; - else - hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP; - break; - - case HCI_FILTER: - { - struct hci_filter *f = &hci_pi(sk)->filter; - - uf.type_mask = f->type_mask; - uf.opcode = f->opcode; - uf.event_mask[0] = *((u32 *) f->event_mask + 0); - uf.event_mask[1] = *((u32 *) f->event_mask + 1); - } - - len = min_t(unsigned int, len, sizeof(uf)); - if (copy_from_user(&uf, optval, len)) { - err = -EFAULT; - break; - } - - if (!capable(CAP_NET_RAW)) { - uf.type_mask &= hci_sec_filter.type_mask; - uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0); - uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1); - } - - { - struct hci_filter *f = &hci_pi(sk)->filter; - - f->type_mask = uf.type_mask; - f->opcode = uf.opcode; - *((u32 *) f->event_mask + 0) = uf.event_mask[0]; - *((u32 *) f->event_mask + 1) = uf.event_mask[1]; - } - break; - - default: - err = -ENOPROTOOPT; - break; - } - -done: - release_sock(sk); - return err; -} - -static int hci_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct hci_ufilter uf; - struct sock *sk = sock->sk; - int len, opt, err = 0; - - BT_DBG("sk %p, opt %d", sk, optname); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { - err = -EINVAL; - goto done; - } - - switch (optname) { - case HCI_DATA_DIR: - if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR) - opt = 1; - else - opt = 0; - - if (put_user(opt, optval)) - err = -EFAULT; - break; - - case HCI_TIME_STAMP: - if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP) - opt = 1; - else - opt = 0; - - if (put_user(opt, optval)) - err = -EFAULT; - break; - - case HCI_FILTER: - { - struct hci_filter *f = &hci_pi(sk)->filter; - - uf.type_mask = f->type_mask; - uf.opcode = f->opcode; - uf.event_mask[0] = *((u32 *) f->event_mask + 0); - uf.event_mask[1] = *((u32 *) f->event_mask + 1); - } - - len = min_t(unsigned int, len, sizeof(uf)); - if (copy_to_user(optval, &uf, len)) - err = -EFAULT; - break; - - default: - err = -ENOPROTOOPT; - break; - } - -done: - release_sock(sk); - return err; -} - -static const struct proto_ops hci_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = hci_sock_release, - .bind = hci_sock_bind, - .getname = hci_sock_getname, - .sendmsg = hci_sock_sendmsg, - .recvmsg = hci_sock_recvmsg, - .ioctl = hci_sock_ioctl, - .poll = datagram_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = hci_sock_setsockopt, - .getsockopt = hci_sock_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto hci_sk_proto = { - .name = "HCI", - .owner = THIS_MODULE, - .obj_size = sizeof(struct hci_pinfo) -}; - -static int hci_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sock->ops = &hci_sock_ops; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - - sock->state = SS_UNCONNECTED; - sk->sk_state = BT_OPEN; - - bt_sock_link(&hci_sk_list, sk); - return 0; -} - -static int hci_sock_dev_event(struct notifier_block *this, - unsigned long event, void *ptr) -{ - struct hci_dev *hdev = (struct hci_dev *) ptr; - struct hci_ev_si_device ev; - - BT_DBG("hdev %s event %ld", hdev->name, event); - - /* Send event to sockets */ - ev.event = event; - ev.dev_id = hdev->id; - hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev); - - if (event == HCI_DEV_UNREG) { - struct sock *sk; - struct hlist_node *node; - - /* Detach sockets from device */ - read_lock(&hci_sk_list.lock); - sk_for_each(sk, node, &hci_sk_list.head) { - bh_lock_sock_nested(sk); - if (hci_pi(sk)->hdev == hdev) { - hci_pi(sk)->hdev = NULL; - sk->sk_err = EPIPE; - sk->sk_state = BT_OPEN; - sk->sk_state_change(sk); - - hci_dev_put(hdev); - } - bh_unlock_sock(sk); - } - read_unlock(&hci_sk_list.lock); - } - - return NOTIFY_DONE; -} - -static const struct net_proto_family hci_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = hci_sock_create, -}; - -static struct notifier_block hci_sock_nblock = { - .notifier_call = hci_sock_dev_event -}; - -int __init hci_sock_init(void) -{ - int err; - - err = proto_register(&hci_sk_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_HCI, &hci_sock_family_ops); - if (err < 0) - goto error; - - hci_register_notifier(&hci_sock_nblock); - - BT_INFO("HCI socket layer initialized"); - - return 0; - -error: - BT_ERR("HCI socket registration failed"); - proto_unregister(&hci_sk_proto); - return err; -} - -void hci_sock_cleanup(void) -{ - if (bt_sock_unregister(BTPROTO_HCI) < 0) - BT_ERR("HCI socket unregistration failed"); - - hci_unregister_notifier(&hci_sock_nblock); - - proto_unregister(&hci_sk_proto); -} diff --git a/net/bluetooth_tizen/hci_sysfs.c b/net/bluetooth_tizen/hci_sysfs.c deleted file mode 100644 index bc15429..0000000 --- a/net/bluetooth_tizen/hci_sysfs.c +++ /dev/null @@ -1,588 +0,0 @@ -/* Bluetooth HCI driver model support. */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/module.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> - -static struct class *bt_class; - -struct dentry *bt_debugfs; -EXPORT_SYMBOL_GPL(bt_debugfs); - -static inline char *link_typetostr(int type) -{ - switch (type) { - case ACL_LINK: - return "ACL"; - case SCO_LINK: - return "SCO"; - case ESCO_LINK: - return "eSCO"; - case LE_LINK: - return "LE"; - default: - return "UNKNOWN"; - } -} - -static ssize_t show_link_type(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_conn *conn = to_hci_conn(dev); - return sprintf(buf, "%s\n", link_typetostr(conn->type)); -} - -static ssize_t show_link_address(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_conn *conn = to_hci_conn(dev); - return sprintf(buf, "%s\n", batostr(&conn->dst)); -} - -static ssize_t show_link_features(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_conn *conn = to_hci_conn(dev); - - return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", - conn->features[0], conn->features[1], - conn->features[2], conn->features[3], - conn->features[4], conn->features[5], - conn->features[6], conn->features[7]); -} - -#define LINK_ATTR(_name, _mode, _show, _store) \ -struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store) - -static LINK_ATTR(type, S_IRUGO, show_link_type, NULL); -static LINK_ATTR(address, S_IRUGO, show_link_address, NULL); -static LINK_ATTR(features, S_IRUGO, show_link_features, NULL); - -static struct attribute *bt_link_attrs[] = { - &link_attr_type.attr, - &link_attr_address.attr, - &link_attr_features.attr, - NULL -}; - -static struct attribute_group bt_link_group = { - .attrs = bt_link_attrs, -}; - -static const struct attribute_group *bt_link_groups[] = { - &bt_link_group, - NULL -}; - -static void bt_link_release(struct device *dev) -{ - struct hci_conn *conn = to_hci_conn(dev); - kfree(conn); -} - -static struct device_type bt_link = { - .name = "link", - .groups = bt_link_groups, - .release = bt_link_release, -}; - -/* - * The rfcomm tty device will possibly retain even when conn - * is down, and sysfs doesn't support move zombie device, - * so we should move the device before conn device is destroyed. - */ -static int __match_tty(struct device *dev, void *data) -{ - return !strncmp(dev_name(dev), "rfcomm", 6); -} - -void hci_conn_init_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p", conn); - - conn->dev.type = &bt_link; - conn->dev.class = bt_class; - conn->dev.parent = &hdev->dev; - - device_initialize(&conn->dev); -} - -void hci_conn_add_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p", conn); - - dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); - - if (device_add(&conn->dev) < 0) { - BT_ERR("Failed to register connection device"); - return; - } - - hci_dev_hold(hdev); -} - -void hci_conn_del_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - if (!device_is_registered(&conn->dev)) - return; - - while (1) { - struct device *dev; - - dev = device_find_child(&conn->dev, NULL, __match_tty); - if (!dev) - break; - device_move(dev, NULL, DPM_ORDER_DEV_LAST); - put_device(dev); - } - - device_del(&conn->dev); - put_device(&conn->dev); - - hci_dev_put(hdev); -} - -static inline char *host_bustostr(int bus) -{ - switch (bus) { - case HCI_VIRTUAL: - return "VIRTUAL"; - case HCI_USB: - return "USB"; - case HCI_PCCARD: - return "PCCARD"; - case HCI_UART: - return "UART"; - case HCI_RS232: - return "RS232"; - case HCI_PCI: - return "PCI"; - case HCI_SDIO: - return "SDIO"; - default: - return "UNKNOWN"; - } -} - -static inline char *host_typetostr(int type) -{ - switch (type) { - case HCI_BREDR: - return "BR/EDR"; - case HCI_AMP: - return "AMP"; - default: - return "UNKNOWN"; - } -} - -static ssize_t show_bus(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", host_bustostr(hdev->bus)); -} - -static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", host_typetostr(hdev->dev_type)); -} - -static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - char name[HCI_MAX_NAME_LENGTH + 1]; - int i; - - for (i = 0; i < HCI_MAX_NAME_LENGTH; i++) - name[i] = hdev->dev_name[i]; - - name[HCI_MAX_NAME_LENGTH] = '\0'; - return sprintf(buf, "%s\n", name); -} - -static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "0x%.2x%.2x%.2x\n", - hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); -} - -static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", batostr(&hdev->bdaddr)); -} - -static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - - return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", - hdev->features[0], hdev->features[1], - hdev->features[2], hdev->features[3], - hdev->features[4], hdev->features[5], - hdev->features[6], hdev->features[7]); -} - -static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->manufacturer); -} - -static ssize_t show_hci_version(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->hci_ver); -} - -static ssize_t show_hci_revision(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->hci_rev); -} - -static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->idle_timeout); -} - -static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - unsigned int val; - int rv; - - rv = kstrtouint(buf, 0, &val); - if (rv < 0) - return rv; - - if (val != 0 && (val < 500 || val > 3600000)) - return -EINVAL; - - hdev->idle_timeout = val; - - return count; -} - -static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->sniff_max_interval); -} - -static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - u16 val; - int rv; - - rv = kstrtou16(buf, 0, &val); - if (rv < 0) - return rv; - - if (val == 0 || val % 2 || val < hdev->sniff_min_interval) - return -EINVAL; - - hdev->sniff_max_interval = val; - - return count; -} - -static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->sniff_min_interval); -} - -static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - u16 val; - int rv; - - rv = kstrtou16(buf, 0, &val); - if (rv < 0) - return rv; - - if (val == 0 || val % 2 || val > hdev->sniff_max_interval) - return -EINVAL; - - hdev->sniff_min_interval = val; - - return count; -} - -static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL); -static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); -static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static DEVICE_ATTR(class, S_IRUGO, show_class, NULL); -static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); -static DEVICE_ATTR(features, S_IRUGO, show_features, NULL); -static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL); -static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL); -static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL); - -static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, - show_idle_timeout, store_idle_timeout); -static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, - show_sniff_max_interval, store_sniff_max_interval); -static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, - show_sniff_min_interval, store_sniff_min_interval); - -static struct attribute *bt_host_attrs[] = { - &dev_attr_bus.attr, - &dev_attr_type.attr, - &dev_attr_name.attr, - &dev_attr_class.attr, - &dev_attr_address.attr, - &dev_attr_features.attr, - &dev_attr_manufacturer.attr, - &dev_attr_hci_version.attr, - &dev_attr_hci_revision.attr, - &dev_attr_idle_timeout.attr, - &dev_attr_sniff_max_interval.attr, - &dev_attr_sniff_min_interval.attr, - NULL -}; - -static struct attribute_group bt_host_group = { - .attrs = bt_host_attrs, -}; - -static const struct attribute_group *bt_host_groups[] = { - &bt_host_group, - NULL -}; - -static void bt_host_release(struct device *dev) -{ - struct hci_dev *hdev = to_hci_dev(dev); - kfree(hdev); - module_put(THIS_MODULE); -} - -static struct device_type bt_host = { - .name = "host", - .groups = bt_host_groups, - .release = bt_host_release, -}; - -static int inquiry_cache_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - hci_dev_lock(hdev); - - list_for_each_entry(e, &cache->all, all) { - struct inquiry_data *data = &e->data; - seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", - batostr(&data->bdaddr), - data->pscan_rep_mode, data->pscan_period_mode, - data->pscan_mode, data->dev_class[2], - data->dev_class[1], data->dev_class[0], - __le16_to_cpu(data->clock_offset), - data->rssi, data->ssp_mode, e->timestamp); - } - - hci_dev_unlock(hdev); - - return 0; -} - -static int inquiry_cache_open(struct inode *inode, struct file *file) -{ - return single_open(file, inquiry_cache_show, inode->i_private); -} - -static const struct file_operations inquiry_cache_fops = { - .open = inquiry_cache_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int blacklist_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->blacklist, list) - seq_printf(f, "%s\n", batostr(&b->bdaddr)); - - hci_dev_unlock(hdev); - - return 0; -} - -static int blacklist_open(struct inode *inode, struct file *file) -{ - return single_open(file, blacklist_show, inode->i_private); -} - -static const struct file_operations blacklist_fops = { - .open = blacklist_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void print_bt_uuid(struct seq_file *f, u8 *uuid) -{ - u32 data0, data4; - u16 data1, data2, data3, data5; - - memcpy(&data0, &uuid[0], 4); - memcpy(&data1, &uuid[4], 2); - memcpy(&data2, &uuid[6], 2); - memcpy(&data3, &uuid[8], 2); - memcpy(&data4, &uuid[10], 4); - memcpy(&data5, &uuid[14], 2); - - seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x\n", - ntohl(data0), ntohs(data1), ntohs(data2), - ntohs(data3), ntohl(data4), ntohs(data5)); -} - -static int uuids_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bt_uuid *uuid; - - hci_dev_lock(hdev); - - list_for_each_entry(uuid, &hdev->uuids, list) - print_bt_uuid(f, uuid->uuid); - - hci_dev_unlock(hdev); - - return 0; -} - -static int uuids_open(struct inode *inode, struct file *file) -{ - return single_open(file, uuids_show, inode->i_private); -} - -static const struct file_operations uuids_fops = { - .open = uuids_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int auto_accept_delay_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - - hdev->auto_accept_delay = val; - - hci_dev_unlock(hdev); - - return 0; -} - -static int auto_accept_delay_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - - *val = hdev->auto_accept_delay; - - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, - auto_accept_delay_set, "%llu\n"); - -void hci_init_sysfs(struct hci_dev *hdev) -{ - struct device *dev = &hdev->dev; - - dev->type = &bt_host; - dev->class = bt_class; - - __module_get(THIS_MODULE); - device_initialize(dev); -} - -int hci_add_sysfs(struct hci_dev *hdev) -{ - struct device *dev = &hdev->dev; - int err; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - dev->parent = hdev->parent; - dev_set_name(dev, "%s", hdev->name); - - err = device_add(dev); - if (err < 0) - return err; - - if (!bt_debugfs) - return 0; - - hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); - if (!hdev->debugfs) - return 0; - - debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, - hdev, &inquiry_cache_fops); - - debugfs_create_file("blacklist", 0444, hdev->debugfs, - hdev, &blacklist_fops); - - debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); - - debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev, - &auto_accept_delay_fops); - return 0; -} - -void hci_del_sysfs(struct hci_dev *hdev) -{ - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - debugfs_remove_recursive(hdev->debugfs); - - device_del(&hdev->dev); -} - -int __init bt_sysfs_init(void) -{ - bt_debugfs = debugfs_create_dir("bluetooth", NULL); - - bt_class = class_create(THIS_MODULE, "bluetooth"); - if (IS_ERR(bt_class)) - return PTR_ERR(bt_class); - - return 0; -} - -void bt_sysfs_cleanup(void) -{ - class_destroy(bt_class); - - debugfs_remove_recursive(bt_debugfs); -} diff --git a/net/bluetooth_tizen/hidp/Kconfig b/net/bluetooth_tizen/hidp/Kconfig deleted file mode 100644 index 4deaca7..0000000 --- a/net/bluetooth_tizen/hidp/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config BT_HIDP - tristate "HIDP protocol support" - depends on BT && INPUT && HID_SUPPORT - select HID - help - HIDP (Human Interface Device Protocol) is a transport layer - for HID reports. HIDP is required for the Bluetooth Human - Interface Device Profile. - - Say Y here to compile HIDP support into the kernel or say M to - compile it as module (hidp). - diff --git a/net/bluetooth_tizen/hidp/Makefile b/net/bluetooth_tizen/hidp/Makefile deleted file mode 100644 index a9ee115..0000000 --- a/net/bluetooth_tizen/hidp/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux Bluetooth HIDP layer -# - -obj-$(CONFIG_BT_HIDP) += hidp.o - -hidp-objs := core.o sock.o diff --git a/net/bluetooth_tizen/hidp/core.c b/net/bluetooth_tizen/hidp/core.c deleted file mode 100644 index d478be1..0000000 --- a/net/bluetooth_tizen/hidp/core.c +++ /dev/null @@ -1,1242 +0,0 @@ -/* - HIDP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/freezer.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/mutex.h> -#include <linux/kthread.h> -#include <net/sock.h> - -#include <linux/input.h> -#include <linux/hid.h> -#include <linux/hidraw.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> - -#include "hidp.h" - -#define VERSION "1.2" - -static DECLARE_RWSEM(hidp_session_sem); -static LIST_HEAD(hidp_session_list); - -static unsigned char hidp_keycode[256] = { - 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, - 37, 38, 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, - 21, 44, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, - 14, 15, 57, 12, 13, 26, 27, 43, 43, 39, 40, 41, 51, 52, - 53, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 87, 88, - 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105, 108, 103, 69, - 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72, 73, - 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190, - 191, 192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, - 136, 113, 115, 114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, - 95, 0, 0, 0, 122, 123, 90, 91, 85, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 42, 56, 125, 97, 54, 100, 126, 164, 166, 165, 163, 161, 115, - 114, 113, 150, 158, 159, 128, 136, 177, 178, 176, 142, 152, 173, 140 -}; - -static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - -static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) -{ - struct hidp_session *session; - - BT_DBG(""); - - list_for_each_entry(session, &hidp_session_list, list) { - if (!bacmp(bdaddr, &session->bdaddr)) - return session; - } - - return NULL; -} - -static void __hidp_link_session(struct hidp_session *session) -{ - list_add(&session->list, &hidp_session_list); -} - -static void __hidp_unlink_session(struct hidp_session *session) -{ - hci_conn_put_device(session->conn); - - list_del(&session->list); -} - -static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) -{ - memset(ci, 0, sizeof(*ci)); - bacpy(&ci->bdaddr, &session->bdaddr); - - ci->flags = session->flags; - ci->state = session->state; - - ci->vendor = 0x0000; - ci->product = 0x0000; - ci->version = 0x0000; - - if (session->input) { - ci->vendor = session->input->id.vendor; - ci->product = session->input->id.product; - ci->version = session->input->id.version; - if (session->input->name) - strncpy(ci->name, session->input->name, 128); - else - strncpy(ci->name, "HID Boot Device", 128); - } - - if (session->hid) { - ci->vendor = session->hid->vendor; - ci->product = session->hid->product; - ci->version = session->hid->version; - strncpy(ci->name, session->hid->name, 128); - } -} - -static int hidp_queue_event(struct hidp_session *session, struct input_dev *dev, - unsigned int type, unsigned int code, int value) -{ - unsigned char newleds; - struct sk_buff *skb; - - BT_DBG("session %p type %d code %d value %d", session, type, code, value); - - if (type != EV_LED) - return -1; - - newleds = (!!test_bit(LED_KANA, dev->led) << 3) | - (!!test_bit(LED_COMPOSE, dev->led) << 3) | - (!!test_bit(LED_SCROLLL, dev->led) << 2) | - (!!test_bit(LED_CAPSL, dev->led) << 1) | - (!!test_bit(LED_NUML, dev->led)); - - if (session->leds == newleds) - return 0; - - session->leds = newleds; - - skb = alloc_skb(3, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for new frame"); - return -ENOMEM; - } - - *skb_put(skb, 1) = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - *skb_put(skb, 1) = 0x01; - *skb_put(skb, 1) = newleds; - - skb_queue_tail(&session->intr_transmit, skb); - - hidp_schedule(session); - - return 0; -} - -static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hidp_session *session = hid->driver_data; - - return hidp_queue_event(session, dev, type, code, value); -} - -static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) -{ - struct hidp_session *session = input_get_drvdata(dev); - - return hidp_queue_event(session, dev, type, code, value); -} - -static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) -{ - struct input_dev *dev = session->input; - unsigned char *keys = session->keys; - unsigned char *udata = skb->data + 1; - signed char *sdata = skb->data + 1; - int i, size = skb->len - 1; - - switch (skb->data[0]) { - case 0x01: /* Keyboard report */ - for (i = 0; i < 8; i++) - input_report_key(dev, hidp_keycode[i + 224], (udata[0] >> i) & 1); - - /* If all the key codes have been set to 0x01, it means - * too many keys were pressed at the same time. */ - if (!memcmp(udata + 2, hidp_mkeyspat, 6)) - break; - - for (i = 2; i < 8; i++) { - if (keys[i] > 3 && memscan(udata + 2, keys[i], 6) == udata + 8) { - if (hidp_keycode[keys[i]]) - input_report_key(dev, hidp_keycode[keys[i]], 0); - else - BT_ERR("Unknown key (scancode %#x) released.", keys[i]); - } - - if (udata[i] > 3 && memscan(keys + 2, udata[i], 6) == keys + 8) { - if (hidp_keycode[udata[i]]) - input_report_key(dev, hidp_keycode[udata[i]], 1); - else - BT_ERR("Unknown key (scancode %#x) pressed.", udata[i]); - } - } - - memcpy(keys, udata, 8); - break; - - case 0x02: /* Mouse report */ - input_report_key(dev, BTN_LEFT, sdata[0] & 0x01); - input_report_key(dev, BTN_RIGHT, sdata[0] & 0x02); - input_report_key(dev, BTN_MIDDLE, sdata[0] & 0x04); - input_report_key(dev, BTN_SIDE, sdata[0] & 0x08); - input_report_key(dev, BTN_EXTRA, sdata[0] & 0x10); - - input_report_rel(dev, REL_X, sdata[1]); - input_report_rel(dev, REL_Y, sdata[2]); - - if (size > 3) - input_report_rel(dev, REL_WHEEL, sdata[3]); - break; - } - - input_sync(dev); -} - -static int __hidp_send_ctrl_message(struct hidp_session *session, - unsigned char hdr, unsigned char *data, int size) -{ - struct sk_buff *skb; - - BT_DBG("session %p data %p size %d", session, data, size); - - if (atomic_read(&session->terminate)) - return -EIO; - - skb = alloc_skb(size + 1, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for new frame"); - return -ENOMEM; - } - - *skb_put(skb, 1) = hdr; - if (data && size > 0) - memcpy(skb_put(skb, size), data, size); - - skb_queue_tail(&session->ctrl_transmit, skb); - - return 0; -} - -static inline int hidp_send_ctrl_message(struct hidp_session *session, - unsigned char hdr, unsigned char *data, int size) -{ - int err; - - err = __hidp_send_ctrl_message(session, hdr, data, size); - - hidp_schedule(session); - - return err; -} - -static int hidp_queue_report(struct hidp_session *session, - unsigned char *data, int size) -{ - struct sk_buff *skb; - - BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); - - skb = alloc_skb(size + 1, GFP_ATOMIC); - if (!skb) { - BT_ERR("Can't allocate memory for new frame"); - return -ENOMEM; - } - - *skb_put(skb, 1) = 0xa2; - if (size > 0) - memcpy(skb_put(skb, size), data, size); - - skb_queue_tail(&session->intr_transmit, skb); - - hidp_schedule(session); - - return 0; -} - -static int hidp_send_report(struct hidp_session *session, struct hid_report *report) -{ - unsigned char buf[32]; - int rsize; - - rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (rsize > sizeof(buf)) - return -EIO; - - hid_output_report(report, buf); - - return hidp_queue_report(session, buf, rsize); -} - -static int hidp_get_raw_report(struct hid_device *hid, - unsigned char report_number, - unsigned char *data, size_t count, - unsigned char report_type) -{ - struct hidp_session *session = hid->driver_data; - struct sk_buff *skb; - size_t len; - int numbered_reports = hid->report_enum[report_type].numbered; - int ret; - - switch (report_type) { - case HID_FEATURE_REPORT: - report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; - break; - case HID_INPUT_REPORT: - report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_INPUT; - break; - case HID_OUTPUT_REPORT: - report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_OUPUT; - break; - default: - return -EINVAL; - } - - if (mutex_lock_interruptible(&session->report_mutex)) - return -ERESTARTSYS; - - /* Set up our wait, and send the report request to the device. */ - session->waiting_report_type = report_type & HIDP_DATA_RTYPE_MASK; - session->waiting_report_number = numbered_reports ? report_number : -1; - set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - data[0] = report_number; - ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1); - if (ret) - goto err; - - /* Wait for the return of the report. The returned report - gets put in session->report_return. */ - while (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { - int res; - - res = wait_event_interruptible_timeout(session->report_queue, - !test_bit(HIDP_WAITING_FOR_RETURN, &session->flags), - 5*HZ); - if (res == 0) { - /* timeout */ - ret = -EIO; - goto err; - } - if (res < 0) { - /* signal */ - ret = -ERESTARTSYS; - goto err; - } - } - - skb = session->report_return; - if (skb) { - len = skb->len < count ? skb->len : count; - memcpy(data, skb->data, len); - - kfree_skb(skb); - session->report_return = NULL; - } else { - /* Device returned a HANDSHAKE, indicating protocol error. */ - len = -EIO; - } - - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - mutex_unlock(&session->report_mutex); - - return len; - -err: - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - mutex_unlock(&session->report_mutex); - return ret; -} - -static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, - unsigned char report_type) -{ - struct hidp_session *session = hid->driver_data; - int ret; - - switch (report_type) { - case HID_FEATURE_REPORT: - report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; - break; - case HID_OUTPUT_REPORT: - report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; - break; - default: - return -EINVAL; - } - - if (mutex_lock_interruptible(&session->report_mutex)) - return -ERESTARTSYS; - - /* Set up our wait, and send the report request to the device. */ - set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, - count); - if (ret) - goto err; - - /* Wait for the ACK from the device. */ - while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { - int res; - - res = wait_event_interruptible_timeout(session->report_queue, - !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags), - 10*HZ); - if (res == 0) { - /* timeout */ - ret = -EIO; - goto err; - } - if (res < 0) { - /* signal */ - ret = -ERESTARTSYS; - goto err; - } - } - - if (!session->output_report_success) { - ret = -EIO; - goto err; - } - - ret = count; - -err: - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - mutex_unlock(&session->report_mutex); - return ret; -} - -static void hidp_idle_timeout(unsigned long arg) -{ - struct hidp_session *session = (struct hidp_session *) arg; - - atomic_inc(&session->terminate); - wake_up_process(session->task); -} - -static void hidp_set_timer(struct hidp_session *session) -{ - if (session->idle_to > 0) - mod_timer(&session->timer, jiffies + HZ * session->idle_to); -} - -static inline void hidp_del_timer(struct hidp_session *session) -{ - if (session->idle_to > 0) - del_timer(&session->timer); -} - -static void hidp_process_handshake(struct hidp_session *session, - unsigned char param) -{ - BT_DBG("session %p param 0x%02x", session, param); - session->output_report_success = 0; /* default condition */ - - switch (param) { - case HIDP_HSHK_SUCCESSFUL: - /* FIXME: Call into SET_ GET_ handlers here */ - session->output_report_success = 1; - break; - - case HIDP_HSHK_NOT_READY: - case HIDP_HSHK_ERR_INVALID_REPORT_ID: - case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: - case HIDP_HSHK_ERR_INVALID_PARAMETER: - if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) - wake_up_interruptible(&session->report_queue); - - /* FIXME: Call into SET_ GET_ handlers here */ - break; - - case HIDP_HSHK_ERR_UNKNOWN: - break; - - case HIDP_HSHK_ERR_FATAL: - /* Device requests a reboot, as this is the only way this error - * can be recovered. */ - __hidp_send_ctrl_message(session, - HIDP_TRANS_HID_CONTROL | HIDP_CTRL_SOFT_RESET, NULL, 0); - break; - - default: - __hidp_send_ctrl_message(session, - HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); - break; - } - - /* Wake up the waiting thread. */ - if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) - wake_up_interruptible(&session->report_queue); -} - -static void hidp_process_hid_control(struct hidp_session *session, - unsigned char param) -{ - BT_DBG("session %p param 0x%02x", session, param); - - if (param == HIDP_CTRL_VIRTUAL_CABLE_UNPLUG) { - /* Flush the transmit queues */ - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - - atomic_inc(&session->terminate); - wake_up_process(current); - } -} - -/* Returns true if the passed-in skb should be freed by the caller. */ -static int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, - unsigned char param) -{ - int done_with_skb = 1; - BT_DBG("session %p skb %p len %d param 0x%02x", session, skb, skb->len, param); - - switch (param) { - case HIDP_DATA_RTYPE_INPUT: - hidp_set_timer(session); - - if (session->input) - hidp_input_report(session, skb); - - if (session->hid) - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 0); - break; - - case HIDP_DATA_RTYPE_OTHER: - case HIDP_DATA_RTYPE_OUPUT: - case HIDP_DATA_RTYPE_FEATURE: - break; - - default: - __hidp_send_ctrl_message(session, - HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_INVALID_PARAMETER, NULL, 0); - } - - if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags) && - param == session->waiting_report_type) { - if (session->waiting_report_number < 0 || - session->waiting_report_number == skb->data[0]) { - /* hidp_get_raw_report() is waiting on this report. */ - session->report_return = skb; - done_with_skb = 0; - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - wake_up_interruptible(&session->report_queue); - } - } - - return done_with_skb; -} - -static void hidp_recv_ctrl_frame(struct hidp_session *session, - struct sk_buff *skb) -{ - unsigned char hdr, type, param; - int free_skb = 1; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - hdr = skb->data[0]; - skb_pull(skb, 1); - - type = hdr & HIDP_HEADER_TRANS_MASK; - param = hdr & HIDP_HEADER_PARAM_MASK; - - switch (type) { - case HIDP_TRANS_HANDSHAKE: - hidp_process_handshake(session, param); - break; - - case HIDP_TRANS_HID_CONTROL: - hidp_process_hid_control(session, param); - break; - - case HIDP_TRANS_DATA: - free_skb = hidp_process_data(session, skb, param); - break; - - default: - __hidp_send_ctrl_message(session, - HIDP_TRANS_HANDSHAKE | HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, NULL, 0); - break; - } - - if (free_skb) - kfree_skb(skb); -} - -static void hidp_recv_intr_frame(struct hidp_session *session, - struct sk_buff *skb) -{ - unsigned char hdr; - - BT_DBG("session %p skb %p len %d", session, skb, skb->len); - - hdr = skb->data[0]; - skb_pull(skb, 1); - - if (hdr == (HIDP_TRANS_DATA | HIDP_DATA_RTYPE_INPUT)) { - hidp_set_timer(session); - - if (session->input) - hidp_input_report(session, skb); - - if (session->hid) { - hid_input_report(session->hid, HID_INPUT_REPORT, skb->data, skb->len, 1); - BT_DBG("report len %d", skb->len); - } - } else { - BT_DBG("Unsupported protocol header 0x%02x", hdr); - } - - kfree_skb(skb); -} - -static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) -{ - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("sock %p data %p len %d", sock, data, len); - - if (!len) - return 0; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, &iv, 1, len); -} - -static void hidp_process_intr_transmit(struct hidp_session *session) -{ - struct sk_buff *skb; - - BT_DBG("session %p", session); - - while ((skb = skb_dequeue(&session->intr_transmit))) { - if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->intr_transmit, skb); - break; - } - - hidp_set_timer(session); - kfree_skb(skb); - } -} - -static void hidp_process_ctrl_transmit(struct hidp_session *session) -{ - struct sk_buff *skb; - - BT_DBG("session %p", session); - - while ((skb = skb_dequeue(&session->ctrl_transmit))) { - if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->ctrl_transmit, skb); - break; - } - - hidp_set_timer(session); - kfree_skb(skb); - } -} - -static int hidp_session(void *arg) -{ - struct hidp_session *session = arg; - struct sock *ctrl_sk = session->ctrl_sock->sk; - struct sock *intr_sk = session->intr_sock->sk; - struct sk_buff *skb; - wait_queue_t ctrl_wait, intr_wait; - - BT_DBG("session %p", session); - - __module_get(THIS_MODULE); - set_user_nice(current, -15); - - init_waitqueue_entry(&ctrl_wait, current); - init_waitqueue_entry(&intr_wait, current); - add_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); - add_wait_queue(sk_sleep(intr_sk), &intr_wait); - session->waiting_for_startup = 0; - wake_up_interruptible(&session->startup_queue); - set_current_state(TASK_INTERRUPTIBLE); - while (!atomic_read(&session->terminate)) { - if (ctrl_sk->sk_state != BT_CONNECTED || - intr_sk->sk_state != BT_CONNECTED) - break; - - while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - hidp_recv_intr_frame(session, skb); - else - kfree_skb(skb); - } - - hidp_process_intr_transmit(session); - - while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - hidp_recv_ctrl_frame(session, skb); - else - kfree_skb(skb); - } - - hidp_process_ctrl_transmit(session); - - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(intr_sk), &intr_wait); - remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); - - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - wake_up_interruptible(&session->report_queue); - - down_write(&hidp_session_sem); - - hidp_del_timer(session); - - if (session->input) { - input_unregister_device(session->input); - session->input = NULL; - } - - if (session->hid) { - hid_destroy_device(session->hid); - session->hid = NULL; - } - - /* Wakeup user-space polling for socket errors */ - session->intr_sock->sk->sk_err = EUNATCH; - session->ctrl_sock->sk->sk_err = EUNATCH; - - hidp_schedule(session); - - fput(session->intr_sock->file); - - wait_event_timeout(*(sk_sleep(ctrl_sk)), - (ctrl_sk->sk_state == BT_CLOSED), msecs_to_jiffies(500)); - - fput(session->ctrl_sock->file); - - __hidp_unlink_session(session); - - up_write(&hidp_session_sem); - - kfree(session->rd_data); - kfree(session); - module_put_and_exit(0); - return 0; -} - -static struct hci_conn *hidp_get_connection(struct hidp_session *session) -{ - bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; - bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; - struct hci_conn *conn; - struct hci_dev *hdev; - - hdev = hci_get_route(dst, src); - if (!hdev) - return NULL; - - hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (conn) - hci_conn_hold_device(conn); - hci_dev_unlock(hdev); - - hci_dev_put(hdev); - - return conn; -} - -static int hidp_setup_input(struct hidp_session *session, - struct hidp_connadd_req *req) -{ - struct input_dev *input; - int i; - - input = input_allocate_device(); - if (!input) - return -ENOMEM; - - session->input = input; - - input_set_drvdata(input, session); - - input->name = "Bluetooth HID Boot Protocol Device"; - - input->id.bustype = BUS_BLUETOOTH; - input->id.vendor = req->vendor; - input->id.product = req->product; - input->id.version = req->version; - - if (req->subclass & 0x40) { - set_bit(EV_KEY, input->evbit); - set_bit(EV_LED, input->evbit); - set_bit(EV_REP, input->evbit); - - set_bit(LED_NUML, input->ledbit); - set_bit(LED_CAPSL, input->ledbit); - set_bit(LED_SCROLLL, input->ledbit); - set_bit(LED_COMPOSE, input->ledbit); - set_bit(LED_KANA, input->ledbit); - - for (i = 0; i < sizeof(hidp_keycode); i++) - set_bit(hidp_keycode[i], input->keybit); - clear_bit(0, input->keybit); - } - - if (req->subclass & 0x80) { - input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); - input->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) | - BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE); - input->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); - input->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) | - BIT_MASK(BTN_EXTRA); - input->relbit[0] |= BIT_MASK(REL_WHEEL); - } - - input->dev.parent = &session->conn->dev; - - input->event = hidp_input_event; - - return 0; -} - -static int hidp_open(struct hid_device *hid) -{ - return 0; -} - -static void hidp_close(struct hid_device *hid) -{ -} - -static int hidp_parse(struct hid_device *hid) -{ - struct hidp_session *session = hid->driver_data; - - return hid_parse_report(session->hid, session->rd_data, - session->rd_size); -} - -static int hidp_start(struct hid_device *hid) -{ - struct hidp_session *session = hid->driver_data; - struct hid_report *report; - - if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) - return 0; - - list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. - report_list, list) - hidp_send_report(session, report); - - list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT]. - report_list, list) - hidp_send_report(session, report); - - return 0; -} - -static void hidp_stop(struct hid_device *hid) -{ - struct hidp_session *session = hid->driver_data; - - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - - hid->claimed = 0; -} - -static struct hid_ll_driver hidp_hid_driver = { - .parse = hidp_parse, - .start = hidp_start, - .stop = hidp_stop, - .open = hidp_open, - .close = hidp_close, - .hidinput_input_event = hidp_hidinput_event, -}; - -/* This function sets up the hid device. It does not add it - to the HID system. That is done in hidp_add_connection(). */ -static int hidp_setup_hid(struct hidp_session *session, - struct hidp_connadd_req *req) -{ - struct hid_device *hid; - int err; - - session->rd_data = kzalloc(req->rd_size, GFP_KERNEL); - if (!session->rd_data) - return -ENOMEM; - - 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(); - if (IS_ERR(hid)) { - err = PTR_ERR(hid); - goto fault; - } - - session->hid = hid; - - hid->driver_data = session; - - hid->bus = BUS_BLUETOOTH; - hid->vendor = req->vendor; - hid->product = req->product; - hid->version = req->version; - hid->country = req->country; - - strncpy(hid->name, req->name, 128); - strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); - strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); - - hid->dev.parent = &session->conn->dev; - hid->ll_driver = &hidp_hid_driver; - - hid->hid_get_raw_report = hidp_get_raw_report; - hid->hid_output_raw_report = hidp_output_raw_report; - - return 0; - -fault: - kfree(session->rd_data); - session->rd_data = NULL; - - return err; -} - -int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock) -{ - struct hidp_session *session, *s; - int vendor, product; - int err; - - BT_DBG(""); - - if (bacmp(&bt_sk(ctrl_sock->sk)->src, &bt_sk(intr_sock->sk)->src) || - bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) - return -ENOTUNIQ; - - BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); - - down_write(&hidp_session_sem); - - s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); - if (s && s->state == BT_CONNECTED) { - up_write(&hidp_session_sem); - return -EEXIST; - } - - session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); - if (!session) { - up_write(&hidp_session_sem); - return -ENOMEM; - } - - bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); - - session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, - l2cap_pi(ctrl_sock->sk)->chan->imtu); - session->intr_mtu = min_t(uint, l2cap_pi(intr_sock->sk)->chan->omtu, - l2cap_pi(intr_sock->sk)->chan->imtu); - - BT_DBG("ctrl mtu %d intr mtu %d", session->ctrl_mtu, session->intr_mtu); - - session->ctrl_sock = ctrl_sock; - session->intr_sock = intr_sock; - session->state = BT_CONNECTED; - - session->conn = hidp_get_connection(session); - if (!session->conn) { - err = -ENOTCONN; - goto failed; - } - - setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); - - skb_queue_head_init(&session->ctrl_transmit); - skb_queue_head_init(&session->intr_transmit); - - mutex_init(&session->report_mutex); - init_waitqueue_head(&session->report_queue); - init_waitqueue_head(&session->startup_queue); - session->waiting_for_startup = 1; - session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); - session->idle_to = req->idle_to; - - __hidp_link_session(session); - - if (req->rd_size > 0) { - err = hidp_setup_hid(session, req); - if (err) - goto purge; - } - - if (!session->hid) { - err = hidp_setup_input(session, req); - if (err < 0) - goto purge; - } - - hidp_set_timer(session); - - if (session->hid) { - vendor = session->hid->vendor; - product = session->hid->product; - } else if (session->input) { - vendor = session->input->id.vendor; - product = session->input->id.product; - } else { - vendor = 0x0000; - product = 0x0000; - } - - session->task = kthread_run(hidp_session, session, "khidpd_%04x%04x", - vendor, product); - if (IS_ERR(session->task)) { - err = PTR_ERR(session->task); - goto unlink; - } - - while (session->waiting_for_startup) { - wait_event_interruptible(session->startup_queue, - !session->waiting_for_startup); - } - - if (session->hid) - err = hid_add_device(session->hid); - else - err = input_register_device(session->input); - - if (err < 0) { - atomic_inc(&session->terminate); - wake_up_process(session->task); - up_write(&hidp_session_sem); - return err; - } - - if (session->input) { - hidp_send_ctrl_message(session, - HIDP_TRANS_SET_PROTOCOL | HIDP_PROTO_BOOT, NULL, 0); - session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); - - session->leds = 0xff; - hidp_input_event(session->input, EV_LED, 0, 0); - } - - up_write(&hidp_session_sem); - return 0; - -unlink: - hidp_del_timer(session); - - if (session->input) { - input_unregister_device(session->input); - session->input = NULL; - } - - if (session->hid) { - hid_destroy_device(session->hid); - session->hid = NULL; - } - - kfree(session->rd_data); - session->rd_data = NULL; - -purge: - __hidp_unlink_session(session); - - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - -failed: - up_write(&hidp_session_sem); - - kfree(session); - return err; -} - -int hidp_del_connection(struct hidp_conndel_req *req) -{ - struct hidp_session *session; - int err = 0; - - BT_DBG(""); - - down_read(&hidp_session_sem); - - session = __hidp_get_session(&req->bdaddr); - if (session) { - if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) { - hidp_send_ctrl_message(session, - HIDP_TRANS_HID_CONTROL | HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, NULL, 0); - } else { - /* Flush the transmit queues */ - skb_queue_purge(&session->ctrl_transmit); - skb_queue_purge(&session->intr_transmit); - - atomic_inc(&session->terminate); - wake_up_process(session->task); - } - } else - err = -ENOENT; - - up_read(&hidp_session_sem); - return err; -} - -int hidp_get_connlist(struct hidp_connlist_req *req) -{ - struct hidp_session *session; - int err = 0, n = 0; - - BT_DBG(""); - - down_read(&hidp_session_sem); - - list_for_each_entry(session, &hidp_session_list, list) { - struct hidp_conninfo ci; - - __hidp_copy_session(session, &ci); - - if (copy_to_user(req->ci, &ci, sizeof(ci))) { - err = -EFAULT; - break; - } - - if (++n >= req->cnum) - break; - - req->ci++; - } - req->cnum = n; - - up_read(&hidp_session_sem); - return err; -} - -int hidp_get_conninfo(struct hidp_conninfo *ci) -{ - struct hidp_session *session; - int err = 0; - - down_read(&hidp_session_sem); - - session = __hidp_get_session(&ci->bdaddr); - if (session) - __hidp_copy_session(session, ci); - else - err = -ENOENT; - - up_read(&hidp_session_sem); - return err; -} - -static const struct hid_device_id hidp_table[] = { - { HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) }, - { } -}; - -static struct hid_driver hidp_driver = { - .name = "generic-bluetooth", - .id_table = hidp_table, -}; - -static int __init hidp_init(void) -{ - int ret; - - BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); - - ret = hid_register_driver(&hidp_driver); - if (ret) - goto err; - - ret = hidp_init_sockets(); - if (ret) - goto err_drv; - - return 0; -err_drv: - hid_unregister_driver(&hidp_driver); -err: - return ret; -} - -static void __exit hidp_exit(void) -{ - hidp_cleanup_sockets(); - hid_unregister_driver(&hidp_driver); -} - -module_init(hidp_init); -module_exit(hidp_exit); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth HIDP ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-6"); diff --git a/net/bluetooth_tizen/hidp/hidp.h b/net/bluetooth_tizen/hidp/hidp.h deleted file mode 100644 index af1bcc8..0000000 --- a/net/bluetooth_tizen/hidp/hidp.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - HIDP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#ifndef __HIDP_H -#define __HIDP_H - -#include <linux/types.h> -#include <net/bluetooth/bluetooth.h> - -/* HIDP header masks */ -#define HIDP_HEADER_TRANS_MASK 0xf0 -#define HIDP_HEADER_PARAM_MASK 0x0f - -/* HIDP transaction types */ -#define HIDP_TRANS_HANDSHAKE 0x00 -#define HIDP_TRANS_HID_CONTROL 0x10 -#define HIDP_TRANS_GET_REPORT 0x40 -#define HIDP_TRANS_SET_REPORT 0x50 -#define HIDP_TRANS_GET_PROTOCOL 0x60 -#define HIDP_TRANS_SET_PROTOCOL 0x70 -#define HIDP_TRANS_GET_IDLE 0x80 -#define HIDP_TRANS_SET_IDLE 0x90 -#define HIDP_TRANS_DATA 0xa0 -#define HIDP_TRANS_DATC 0xb0 - -/* HIDP handshake results */ -#define HIDP_HSHK_SUCCESSFUL 0x00 -#define HIDP_HSHK_NOT_READY 0x01 -#define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 -#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 -#define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 -#define HIDP_HSHK_ERR_UNKNOWN 0x0e -#define HIDP_HSHK_ERR_FATAL 0x0f - -/* HIDP control operation parameters */ -#define HIDP_CTRL_NOP 0x00 -#define HIDP_CTRL_HARD_RESET 0x01 -#define HIDP_CTRL_SOFT_RESET 0x02 -#define HIDP_CTRL_SUSPEND 0x03 -#define HIDP_CTRL_EXIT_SUSPEND 0x04 -#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 - -/* HIDP data transaction headers */ -#define HIDP_DATA_RTYPE_MASK 0x03 -#define HIDP_DATA_RSRVD_MASK 0x0c -#define HIDP_DATA_RTYPE_OTHER 0x00 -#define HIDP_DATA_RTYPE_INPUT 0x01 -#define HIDP_DATA_RTYPE_OUPUT 0x02 -#define HIDP_DATA_RTYPE_FEATURE 0x03 - -/* HIDP protocol header parameters */ -#define HIDP_PROTO_BOOT 0x00 -#define HIDP_PROTO_REPORT 0x01 - -/* HIDP ioctl defines */ -#define HIDPCONNADD _IOW('H', 200, int) -#define HIDPCONNDEL _IOW('H', 201, int) -#define HIDPGETCONNLIST _IOR('H', 210, int) -#define HIDPGETCONNINFO _IOR('H', 211, int) - -#define HIDP_VIRTUAL_CABLE_UNPLUG 0 -#define HIDP_BOOT_PROTOCOL_MODE 1 -#define HIDP_BLUETOOTH_VENDOR_ID 9 -#define HIDP_WAITING_FOR_RETURN 10 -#define HIDP_WAITING_FOR_SEND_ACK 11 - -struct hidp_connadd_req { - int ctrl_sock; /* Connected control socket */ - int intr_sock; /* Connected interrupt socket */ - __u16 parser; - __u16 rd_size; - __u8 __user *rd_data; - __u8 country; - __u8 subclass; - __u16 vendor; - __u16 product; - __u16 version; - __u32 flags; - __u32 idle_to; - char name[128]; -}; - -struct hidp_conndel_req { - bdaddr_t bdaddr; - __u32 flags; -}; - -struct hidp_conninfo { - bdaddr_t bdaddr; - __u32 flags; - __u16 state; - __u16 vendor; - __u16 product; - __u16 version; - char name[128]; -}; - -struct hidp_connlist_req { - __u32 cnum; - struct hidp_conninfo __user *ci; -}; - -int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock); -int hidp_del_connection(struct hidp_conndel_req *req); -int hidp_get_connlist(struct hidp_connlist_req *req); -int hidp_get_conninfo(struct hidp_conninfo *ci); - -/* HIDP session defines */ -struct hidp_session { - struct list_head list; - - struct hci_conn *conn; - - struct socket *ctrl_sock; - struct socket *intr_sock; - - bdaddr_t bdaddr; - - unsigned long state; - unsigned long flags; - unsigned long idle_to; - - uint ctrl_mtu; - uint intr_mtu; - - atomic_t terminate; - struct task_struct *task; - - unsigned char keys[8]; - unsigned char leds; - - struct input_dev *input; - - struct hid_device *hid; - - struct timer_list timer; - - struct sk_buff_head ctrl_transmit; - struct sk_buff_head intr_transmit; - - /* Used in hidp_get_raw_report() */ - int waiting_report_type; /* HIDP_DATA_RTYPE_* */ - int waiting_report_number; /* -1 for not numbered */ - struct mutex report_mutex; - struct sk_buff *report_return; - wait_queue_head_t report_queue; - - /* Used in hidp_output_raw_report() */ - int output_report_success; /* boolean */ - - /* Report descriptor */ - __u8 *rd_data; - uint rd_size; - - wait_queue_head_t startup_queue; - int waiting_for_startup; -}; - -static inline void hidp_schedule(struct hidp_session *session) -{ - struct sock *ctrl_sk = session->ctrl_sock->sk; - struct sock *intr_sk = session->intr_sock->sk; - - wake_up_interruptible(sk_sleep(ctrl_sk)); - wake_up_interruptible(sk_sleep(intr_sk)); -} - -/* HIDP init defines */ -extern int __init hidp_init_sockets(void); -extern void __exit hidp_cleanup_sockets(void); - -#endif /* __HIDP_H */ diff --git a/net/bluetooth_tizen/hidp/sock.c b/net/bluetooth_tizen/hidp/sock.c deleted file mode 100644 index 73a32d7..0000000 --- a/net/bluetooth_tizen/hidp/sock.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - HIDP implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2003-2004 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/skbuff.h> -#include <linux/socket.h> -#include <linux/ioctl.h> -#include <linux/file.h> -#include <linux/init.h> -#include <linux/compat.h> -#include <linux/gfp.h> -#include <net/sock.h> - -#include "hidp.h" - -static int hidp_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - BT_DBG("sock %p sk %p", sock, sk); - - if (!sk) - return 0; - - sock_orphan(sk); - sock_put(sk); - - return 0; -} - -static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - void __user *argp = (void __user *) arg; - struct hidp_connadd_req ca; - struct hidp_conndel_req cd; - struct hidp_connlist_req cl; - struct hidp_conninfo ci; - struct socket *csock; - struct socket *isock; - int err; - - BT_DBG("cmd %x arg %lx", cmd, arg); - - switch (cmd) { - case HIDPCONNADD: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ca, argp, sizeof(ca))) - return -EFAULT; - - csock = sockfd_lookup(ca.ctrl_sock, &err); - if (!csock) - return err; - - isock = sockfd_lookup(ca.intr_sock, &err); - if (!isock) { - sockfd_put(csock); - return err; - } - - if (csock->sk->sk_state != BT_CONNECTED || - isock->sk->sk_state != BT_CONNECTED) { - sockfd_put(csock); - sockfd_put(isock); - return -EBADFD; - } - - err = hidp_add_connection(&ca, csock, isock); - if (!err) { - if (copy_to_user(argp, &ca, sizeof(ca))) - err = -EFAULT; - } else { - sockfd_put(csock); - sockfd_put(isock); - } - - return err; - - case HIDPCONNDEL: - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&cd, argp, sizeof(cd))) - return -EFAULT; - - return hidp_del_connection(&cd); - - case HIDPGETCONNLIST: - if (copy_from_user(&cl, argp, sizeof(cl))) - return -EFAULT; - - if (cl.cnum <= 0) - return -EINVAL; - - err = hidp_get_connlist(&cl); - if (!err && copy_to_user(argp, &cl, sizeof(cl))) - return -EFAULT; - - return err; - - case HIDPGETCONNINFO: - if (copy_from_user(&ci, argp, sizeof(ci))) - return -EFAULT; - - err = hidp_get_conninfo(&ci); - if (!err && copy_to_user(argp, &ci, sizeof(ci))) - return -EFAULT; - - return err; - } - - return -EINVAL; -} - -#ifdef CONFIG_COMPAT -struct compat_hidp_connadd_req { - int ctrl_sock; /* Connected control socket */ - int intr_sock; /* Connected interrupt socket */ - __u16 parser; - __u16 rd_size; - compat_uptr_t rd_data; - __u8 country; - __u8 subclass; - __u16 vendor; - __u16 product; - __u16 version; - __u32 flags; - __u32 idle_to; - char name[128]; -}; - -static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - if (cmd == HIDPGETCONNLIST) { - struct hidp_connlist_req cl; - u32 uci; - int err; - - if (get_user(cl.cnum, (u32 __user *) arg) || - get_user(uci, (u32 __user *) (arg + 4))) - return -EFAULT; - - cl.ci = compat_ptr(uci); - - if (cl.cnum <= 0) - return -EINVAL; - - err = hidp_get_connlist(&cl); - - if (!err && put_user(cl.cnum, (u32 __user *) arg)) - err = -EFAULT; - - return err; - } else if (cmd == HIDPCONNADD) { - struct compat_hidp_connadd_req ca; - struct hidp_connadd_req __user *uca; - - uca = compat_alloc_user_space(sizeof(*uca)); - - if (copy_from_user(&ca, (void __user *) arg, sizeof(ca))) - return -EFAULT; - - if (put_user(ca.ctrl_sock, &uca->ctrl_sock) || - put_user(ca.intr_sock, &uca->intr_sock) || - put_user(ca.parser, &uca->parser) || - put_user(ca.rd_size, &uca->rd_size) || - put_user(compat_ptr(ca.rd_data), &uca->rd_data) || - put_user(ca.country, &uca->country) || - put_user(ca.subclass, &uca->subclass) || - put_user(ca.vendor, &uca->vendor) || - put_user(ca.product, &uca->product) || - put_user(ca.version, &uca->version) || - put_user(ca.flags, &uca->flags) || - put_user(ca.idle_to, &uca->idle_to) || - copy_to_user(&uca->name[0], &ca.name[0], 128)) - return -EFAULT; - - arg = (unsigned long) uca; - - /* Fall through. We don't actually write back any _changes_ - to the structure anyway, so there's no need to copy back - into the original compat version */ - } - - return hidp_sock_ioctl(sock, cmd, arg); -} -#endif - -static const struct proto_ops hidp_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = hidp_sock_release, - .ioctl = hidp_sock_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = hidp_sock_compat_ioctl, -#endif - .bind = sock_no_bind, - .getname = sock_no_getname, - .sendmsg = sock_no_sendmsg, - .recvmsg = sock_no_recvmsg, - .poll = sock_no_poll, - .listen = sock_no_listen, - .shutdown = sock_no_shutdown, - .setsockopt = sock_no_setsockopt, - .getsockopt = sock_no_getsockopt, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .mmap = sock_no_mmap -}; - -static struct proto hidp_proto = { - .name = "HIDP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct bt_sock) -}; - -static int hidp_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - if (sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto); - if (!sk) - return -ENOMEM; - - sock_init_data(sock, sk); - - sock->ops = &hidp_sock_ops; - - sock->state = SS_UNCONNECTED; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = protocol; - sk->sk_state = BT_OPEN; - - return 0; -} - -static const struct net_proto_family hidp_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = hidp_sock_create -}; - -int __init hidp_init_sockets(void) -{ - int err; - - err = proto_register(&hidp_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_HIDP, &hidp_sock_family_ops); - if (err < 0) - goto error; - - return 0; - -error: - BT_ERR("Can't register HIDP socket"); - proto_unregister(&hidp_proto); - return err; -} - -void __exit hidp_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_HIDP) < 0) - BT_ERR("Can't unregister HIDP socket"); - - proto_unregister(&hidp_proto); -} diff --git a/net/bluetooth_tizen/l2cap_core.c b/net/bluetooth_tizen/l2cap_core.c deleted file mode 100644 index 48268ab..0000000 --- a/net/bluetooth_tizen/l2cap_core.c +++ /dev/null @@ -1,4803 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> - Copyright (C) 2010 Google Inc. - Copyright (C) 2011 ProFUSION Embedded Systems - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth L2CAP core. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/capability.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/socket.h> -#include <linux/skbuff.h> -#include <linux/list.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/uaccess.h> -#include <linux/crc16.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/smp.h> - -bool disable_ertm; - -static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; -static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; - -static LIST_HEAD(chan_list); -static DEFINE_RWLOCK(chan_list_lock); - -static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, - u8 code, u8 ident, u16 dlen, void *data); -static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, - void *data); -static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data); -static void l2cap_send_disconn_req(struct l2cap_conn *conn, - struct l2cap_chan *chan, int err); - -/* ---- L2CAP channels ---- */ - -static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &conn->chan_l, list) { - if (c->dcid == cid) - return c; - } - return NULL; -} - -static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &conn->chan_l, list) { - if (c->scid == cid) - return c; - } - return NULL; -} - -/* Find channel with given SCID. - * Returns locked channel. */ -static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) -{ - struct l2cap_chan *c; - - mutex_lock(&conn->chan_lock); - c = __l2cap_get_chan_by_scid(conn, cid); - if (c) - l2cap_chan_lock(c); - mutex_unlock(&conn->chan_lock); - - return c; -} - -static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &conn->chan_l, list) { - if (c->ident == ident) - return c; - } - return NULL; -} - -static inline struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) -{ - struct l2cap_chan *c; - - mutex_lock(&conn->chan_lock); - c = __l2cap_get_chan_by_ident(conn, ident); - mutex_unlock(&conn->chan_lock); - - return c; -} - -static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) -{ - struct l2cap_chan *c; - - list_for_each_entry(c, &chan_list, global_l) { - if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) - return c; - } - return NULL; -} - -int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) -{ - int err; - - write_lock(&chan_list_lock); - - if (psm && __l2cap_global_chan_by_addr(psm, src)) { - err = -EADDRINUSE; - goto done; - } - - if (psm) { - chan->psm = psm; - chan->sport = psm; - err = 0; - } else { - u16 p; - - err = -EINVAL; - for (p = 0x1001; p < 0x1100; p += 2) - if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) { - chan->psm = cpu_to_le16(p); - chan->sport = cpu_to_le16(p); - err = 0; - break; - } - } - -done: - write_unlock(&chan_list_lock); - return err; -} - -int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) -{ - write_lock(&chan_list_lock); - - chan->scid = scid; - - write_unlock(&chan_list_lock); - - return 0; -} - -static u16 l2cap_alloc_cid(struct l2cap_conn *conn) -{ - u16 cid = L2CAP_CID_DYN_START; - - for (; cid < L2CAP_CID_DYN_END; cid++) { - if (!__l2cap_get_chan_by_scid(conn, cid)) - return cid; - } - - return 0; -} - -static void __l2cap_state_change(struct l2cap_chan *chan, int state) -{ - BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state), - state_to_string(state)); - - chan->state = state; - chan->ops->state_change(chan->data, state); -} - -static void l2cap_state_change(struct l2cap_chan *chan, int state) -{ - struct sock *sk = chan->sk; - - lock_sock(sk); - __l2cap_state_change(chan, state); - release_sock(sk); -} - -static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - - sk->sk_err = err; -} - -static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - - lock_sock(sk); - __l2cap_chan_set_err(chan, err); - release_sock(sk); -} - -static void l2cap_chan_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - chan_timer.work); - struct l2cap_conn *conn = chan->conn; - int reason; - - BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); - - mutex_lock(&conn->chan_lock); - l2cap_chan_lock(chan); - - if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG) - reason = ECONNREFUSED; - else if (chan->state == BT_CONNECT && - chan->sec_level != BT_SECURITY_SDP) - reason = ECONNREFUSED; - else - reason = ETIMEDOUT; - - l2cap_chan_close(chan, reason); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - mutex_unlock(&conn->chan_lock); - - l2cap_chan_put(chan); -} - -struct l2cap_chan *l2cap_chan_create(struct sock *sk) -{ - struct l2cap_chan *chan; - - chan = kzalloc(sizeof(*chan), GFP_ATOMIC); - if (!chan) - return NULL; - - mutex_init(&chan->lock); - - chan->sk = sk; - - write_lock(&chan_list_lock); - list_add(&chan->global_l, &chan_list); - write_unlock(&chan_list_lock); - - INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout); - - chan->state = BT_OPEN; - - atomic_set(&chan->refcnt, 1); - - BT_DBG("sk %p chan %p", sk, chan); - - return chan; -} - -void l2cap_chan_destroy(struct l2cap_chan *chan) -{ - write_lock(&chan_list_lock); - list_del(&chan->global_l); - write_unlock(&chan_list_lock); - - l2cap_chan_put(chan); -} - -void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) -{ - BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, - chan->psm, chan->dcid); - - conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - - chan->conn = conn; - - switch (chan->chan_type) { - case L2CAP_CHAN_CONN_ORIENTED: - if (conn->hcon->type == LE_LINK) { - /* LE connection */ - chan->omtu = L2CAP_LE_DEFAULT_MTU; - chan->scid = L2CAP_CID_LE_DATA; - chan->dcid = L2CAP_CID_LE_DATA; - } else { - /* Alloc CID for connection-oriented socket */ - chan->scid = l2cap_alloc_cid(conn); - chan->omtu = L2CAP_DEFAULT_MTU; - } - break; - - case L2CAP_CHAN_CONN_LESS: - /* Connectionless socket */ - chan->scid = L2CAP_CID_CONN_LESS; - chan->dcid = L2CAP_CID_CONN_LESS; - chan->omtu = L2CAP_DEFAULT_MTU; - break; - - default: - /* Raw socket can send/recv signalling messages only */ - chan->scid = L2CAP_CID_SIGNALING; - chan->dcid = L2CAP_CID_SIGNALING; - chan->omtu = L2CAP_DEFAULT_MTU; - } - - chan->local_id = L2CAP_BESTEFFORT_ID; - chan->local_stype = L2CAP_SERV_BESTEFFORT; - chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; - chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; - chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; - chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; - - l2cap_chan_hold(chan); - - list_add(&chan->list, &conn->chan_l); -} - -void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) -{ - mutex_lock(&conn->chan_lock); - __l2cap_chan_add(conn, chan); - mutex_unlock(&conn->chan_lock); -} - -static void l2cap_chan_del(struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - struct l2cap_conn *conn = chan->conn; - struct sock *parent = bt_sk(sk)->parent; - - __clear_chan_timer(chan); - - BT_DBG("chan %p, conn %p, err %d", chan, conn, err); - - if (conn) { - /* Delete from channel list */ - list_del(&chan->list); - - l2cap_chan_put(chan); - - chan->conn = NULL; - hci_conn_put(conn->hcon); - } - - lock_sock(sk); - - __l2cap_state_change(chan, BT_CLOSED); - sock_set_flag(sk, SOCK_ZAPPED); - - if (err) - __l2cap_chan_set_err(chan, err); - - if (parent) { - bt_accept_unlink(sk); - parent->sk_data_ready(parent, 0); - } else - sk->sk_state_change(sk); - - release_sock(sk); - - if (!(test_bit(CONF_OUTPUT_DONE, &chan->conf_state) && - test_bit(CONF_INPUT_DONE, &chan->conf_state))) - return; - - skb_queue_purge(&chan->tx_q); - - if (chan->mode == L2CAP_MODE_ERTM) { - struct srej_list *l, *tmp; - - __clear_retrans_timer(chan); - __clear_monitor_timer(chan); - __clear_ack_timer(chan); - - skb_queue_purge(&chan->srej_q); - - list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { - list_del(&l->list); - kfree(l); - } - } -} - -static void l2cap_chan_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - - l2cap_chan_lock(chan); - __clear_chan_timer(chan); - l2cap_chan_close(chan, ECONNRESET); - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - } -} - -void l2cap_chan_close(struct l2cap_chan *chan, int reason) -{ - struct l2cap_conn *conn = chan->conn; - struct sock *sk = chan->sk; - - BT_DBG("chan %p state %s sk %p", chan, - state_to_string(chan->state), sk); - - switch (chan->state) { - case BT_LISTEN: - lock_sock(sk); - l2cap_chan_cleanup_listen(sk); - - __l2cap_state_change(chan, BT_CLOSED); - sock_set_flag(sk, SOCK_ZAPPED); - release_sock(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { - __clear_chan_timer(chan); - __set_chan_timer(chan, sk->sk_sndtimeo); - l2cap_send_disconn_req(conn, chan, reason); - } else - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT2: - if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && - conn->hcon->type == ACL_LINK) { - struct l2cap_conn_rsp rsp; - __u16 result; - - if (bt_sk(sk)->defer_setup) - result = L2CAP_CR_SEC_BLOCK; - else - result = L2CAP_CR_BAD_PSM; - l2cap_state_change(chan, BT_DISCONN); - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - } - - l2cap_chan_del(chan, reason); - break; - - case BT_CONNECT: - case BT_DISCONN: - l2cap_chan_del(chan, reason); - break; - - default: - lock_sock(sk); - sock_set_flag(sk, SOCK_ZAPPED); - release_sock(sk); - break; - } -} - -static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) -{ - if (chan->chan_type == L2CAP_CHAN_RAW) { - switch (chan->sec_level) { - case BT_SECURITY_HIGH: - return HCI_AT_DEDICATED_BONDING_MITM; - case BT_SECURITY_MEDIUM: - return HCI_AT_DEDICATED_BONDING; - default: - return HCI_AT_NO_BONDING; - } - } else if (chan->psm == cpu_to_le16(0x0001)) { - if (chan->sec_level == BT_SECURITY_LOW) - chan->sec_level = BT_SECURITY_SDP; - - if (chan->sec_level == BT_SECURITY_HIGH) - return HCI_AT_NO_BONDING_MITM; - else - return HCI_AT_NO_BONDING; - } else { - switch (chan->sec_level) { - case BT_SECURITY_HIGH: - return HCI_AT_GENERAL_BONDING_MITM; - case BT_SECURITY_MEDIUM: - return HCI_AT_GENERAL_BONDING; - default: - return HCI_AT_NO_BONDING; - } - } -} - -/* Service level security */ -int l2cap_chan_check_security(struct l2cap_chan *chan) -{ - struct l2cap_conn *conn = chan->conn; - __u8 auth_type; - - auth_type = l2cap_get_auth_type(chan); - - return hci_conn_security(conn->hcon, chan->sec_level, auth_type); -} - -static u8 l2cap_get_ident(struct l2cap_conn *conn) -{ - u8 id; - - /* Get next available identificator. - * 1 - 128 are used by kernel. - * 129 - 199 are reserved. - * 200 - 254 are used by utilities like l2ping, etc. - */ - - spin_lock(&conn->lock); - - if (++conn->tx_ident > 128) - conn->tx_ident = 1; - - id = conn->tx_ident; - - spin_unlock(&conn->lock); - - return id; -} - -static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) -{ - struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); - u8 flags; - - BT_DBG("code 0x%2.2x", code); - - if (!skb) - return; - - if (lmp_no_flush_capable(conn->hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; - skb->priority = HCI_PRIO_MAX; - - hci_send_acl(conn->hchan, skb, flags); -} - -static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) -{ - struct hci_conn *hcon = chan->conn->hcon; - u16 flags; - - BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, - skb->priority); - - if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && - lmp_no_flush_capable(hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); - hci_send_acl(chan->conn->hchan, skb, flags); -} - -static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) -{ - struct sk_buff *skb; - struct l2cap_hdr *lh; - struct l2cap_conn *conn = chan->conn; - int count, hlen; - - if (chan->state != BT_CONNECTED) - return; - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - hlen = L2CAP_EXT_HDR_SIZE; - else - hlen = L2CAP_ENH_HDR_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - hlen += L2CAP_FCS_SIZE; - - BT_DBG("chan %p, control 0x%8.8x", chan, control); - - count = min_t(unsigned int, conn->mtu, hlen); - - control |= __set_sframe(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) - control |= __set_ctrl_poll(chan); - - skb = bt_skb_alloc(count, GFP_ATOMIC); - if (!skb) - return; - - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - - __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); - - if (chan->fcs == L2CAP_FCS_CRC16) { - u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); - } - - skb->priority = HCI_PRIO_MAX; - l2cap_do_send(chan, skb); -} - -static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control) -{ - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); - set_bit(CONN_RNR_SENT, &chan->conn_state); - } else - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - - control |= __set_reqseq(chan, chan->buffer_seq); - - l2cap_send_sframe(chan, control); -} - -static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) -{ - return !test_bit(CONF_CONNECT_PEND, &chan->conf_state); -} - -static void l2cap_send_conn_req(struct l2cap_chan *chan) -{ - struct l2cap_conn *conn = chan->conn; - struct l2cap_conn_req req; - - req.scid = cpu_to_le16(chan->scid); - req.psm = chan->psm; - - chan->ident = l2cap_get_ident(conn); - - set_bit(CONF_CONNECT_PEND, &chan->conf_state); - - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); -} - -static void l2cap_do_start(struct l2cap_chan *chan) -{ - struct l2cap_conn *conn = chan->conn; - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) { - if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) - return; - - if (l2cap_chan_check_security(chan) && - __l2cap_no_conn_pending(chan)) - l2cap_send_conn_req(chan); - } else { - struct l2cap_info_req req; - req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; - conn->info_ident = l2cap_get_ident(conn); - - schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); - } -} - -static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) -{ - u32 local_feat_mask = l2cap_feat_mask; - if (!disable_ertm) - local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING; - - switch (mode) { - case L2CAP_MODE_ERTM: - return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask; - case L2CAP_MODE_STREAMING: - return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask; - default: - return 0x00; - } -} - -static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err) -{ - struct sock *sk = chan->sk; - struct l2cap_disconn_req req; - - if (!conn) - return; - - if (chan->mode == L2CAP_MODE_ERTM) { - __clear_retrans_timer(chan); - __clear_monitor_timer(chan); - __clear_ack_timer(chan); - } - - req.dcid = cpu_to_le16(chan->dcid); - req.scid = cpu_to_le16(chan->scid); - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_DISCONN_REQ, sizeof(req), &req); - - lock_sock(sk); - __l2cap_state_change(chan, BT_DISCONN); - __l2cap_chan_set_err(chan, err); - release_sock(sk); -} - -/* ---- L2CAP connections ---- */ -static void l2cap_conn_start(struct l2cap_conn *conn) -{ - struct l2cap_chan *chan, *tmp; - - BT_DBG("conn %p", conn); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { - struct sock *sk = chan->sk; - - l2cap_chan_lock(chan); - - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - l2cap_chan_unlock(chan); - continue; - } - - if (chan->state == BT_CONNECT) { - if (!l2cap_chan_check_security(chan) || - !__l2cap_no_conn_pending(chan)) { - l2cap_chan_unlock(chan); - continue; - } - - if (!l2cap_mode_supported(chan->mode, conn->feat_mask) - && test_bit(CONF_STATE2_DEVICE, - &chan->conf_state)) { - l2cap_chan_close(chan, ECONNRESET); - l2cap_chan_unlock(chan); - continue; - } - - l2cap_send_conn_req(chan); - - } else if (chan->state == BT_CONNECT2) { - struct l2cap_conn_rsp rsp; - char buf[128]; - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - - if (l2cap_chan_check_security(chan)) { - lock_sock(sk); - if (bt_sk(sk)->defer_setup) { - struct sock *parent = bt_sk(sk)->parent; - rsp.result = cpu_to_le16(L2CAP_CR_PEND); - rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND); - if (parent) - parent->sk_data_ready(parent, 0); - - } else { - __l2cap_state_change(chan, BT_CONFIG); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - } - release_sock(sk); - } else { - rsp.result = cpu_to_le16(L2CAP_CR_PEND); - rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND); - } - - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - - if (test_bit(CONF_REQ_SENT, &chan->conf_state) || - rsp.result != L2CAP_CR_SUCCESS) { - l2cap_chan_unlock(chan); - continue; - } - - set_bit(CONF_REQ_SENT, &chan->conf_state); - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); - chan->num_conf_req++; - } - - l2cap_chan_unlock(chan); - } - - mutex_unlock(&conn->chan_lock); -} - -/* Find socket with cid and source bdaddr. - * Returns closest match, locked. - */ -static struct l2cap_chan *l2cap_global_chan_by_scid(int state, __le16 cid, bdaddr_t *src) -{ - struct l2cap_chan *c, *c1 = NULL; - - read_lock(&chan_list_lock); - - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - if (state && c->state != state) - continue; - - if (c->scid == cid) { - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) { - read_unlock(&chan_list_lock); - return c; - } - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - c1 = c; - } - } - - read_unlock(&chan_list_lock); - - return c1; -} - -static void l2cap_le_conn_ready(struct l2cap_conn *conn) -{ - struct sock *parent, *sk; - struct l2cap_chan *chan, *pchan; - - BT_DBG(""); - - /* Check if we have socket listening on cid */ - pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA, - conn->src); - if (!pchan) - return; - - parent = pchan->sk; - - lock_sock(parent); - - /* Check for backlog size */ - if (sk_acceptq_is_full(parent)) { - BT_DBG("backlog full %d", parent->sk_ack_backlog); - goto clean; - } - - chan = pchan->ops->new_connection(pchan->data); - if (!chan) - goto clean; - - sk = chan->sk; - - hci_conn_hold(conn->hcon); - - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); - - bt_accept_enqueue(parent, sk); - - l2cap_chan_add(conn, chan); - - __set_chan_timer(chan, sk->sk_sndtimeo); - - __l2cap_state_change(chan, BT_CONNECTED); - parent->sk_data_ready(parent, 0); - -clean: - release_sock(parent); -} - -static void l2cap_chan_ready(struct l2cap_chan *chan) -{ - struct sock *sk = chan->sk; - struct sock *parent; - - lock_sock(sk); - - parent = bt_sk(sk)->parent; - - BT_DBG("sk %p, parent %p", sk, parent); - - chan->conf_state = 0; - __clear_chan_timer(chan); - - __l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); - - if (parent) - parent->sk_data_ready(parent, 0); - - release_sock(sk); -} - -static void l2cap_conn_ready(struct l2cap_conn *conn) -{ - struct l2cap_chan *chan; - - BT_DBG("conn %p", conn); - - if (!conn->hcon->out && conn->hcon->type == LE_LINK) - l2cap_le_conn_ready(conn); - - if (conn->hcon->out && conn->hcon->type == LE_LINK) - smp_conn_security(conn, conn->hcon->pending_sec_level); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - - l2cap_chan_lock(chan); - - if (conn->hcon->type == LE_LINK) { - if (smp_conn_security(conn, chan->sec_level)) - l2cap_chan_ready(chan); - - } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - struct sock *sk = chan->sk; - __clear_chan_timer(chan); - lock_sock(sk); - __l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); - release_sock(sk); - - } else if (chan->state == BT_CONNECT) - l2cap_do_start(chan); - - l2cap_chan_unlock(chan); - } - - mutex_unlock(&conn->chan_lock); -} - -/* Notify sockets that we cannot guaranty reliability anymore */ -static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) -{ - struct l2cap_chan *chan; - - BT_DBG("conn %p", conn); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) - __l2cap_chan_set_err(chan, err); - } - - mutex_unlock(&conn->chan_lock); -} - -static void l2cap_info_timeout(struct work_struct *work) -{ - struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - info_timer.work); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); -} - -static void l2cap_conn_del(struct hci_conn *hcon, int err) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct l2cap_chan *chan, *l; - - if (!conn) - return; - - BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - - kfree_skb(conn->rx_skb); - - mutex_lock(&conn->chan_lock); - - /* Kill channels */ - list_for_each_entry_safe(chan, l, &conn->chan_l, list) { - l2cap_chan_lock(chan); - - l2cap_chan_del(chan, err); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - } - - mutex_unlock(&conn->chan_lock); - - hci_chan_del(conn->hchan); - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) - cancel_delayed_work_sync(&conn->info_timer); - - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) { - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } - - hcon->l2cap_data = NULL; - kfree(conn); -} - -static void security_timeout(struct work_struct *work) -{ - struct l2cap_conn *conn = container_of(work, struct l2cap_conn, - security_timer.work); - - l2cap_conn_del(conn->hcon, ETIMEDOUT); -} - -static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct hci_chan *hchan; - - if (conn || status) - return conn; - - hchan = hci_chan_create(hcon); - if (!hchan) - return NULL; - - conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); - if (!conn) { - hci_chan_del(hchan); - return NULL; - } - - hcon->l2cap_data = conn; - conn->hcon = hcon; - conn->hchan = hchan; - - BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); - - if (hcon->hdev->le_mtu && hcon->type == LE_LINK) - conn->mtu = hcon->hdev->le_mtu; - else - conn->mtu = hcon->hdev->acl_mtu; - - conn->src = &hcon->hdev->bdaddr; - conn->dst = &hcon->dst; - - conn->feat_mask = 0; - - spin_lock_init(&conn->lock); - mutex_init(&conn->chan_lock); - - INIT_LIST_HEAD(&conn->chan_l); - - if (hcon->type == LE_LINK) - INIT_DELAYED_WORK(&conn->security_timer, security_timeout); - else - INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); - - conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; - - return conn; -} - -/* ---- Socket interface ---- */ - -/* Find socket with psm and source bdaddr. - * Returns closest match. - */ -static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, bdaddr_t *src) -{ - struct l2cap_chan *c, *c1 = NULL; - - read_lock(&chan_list_lock); - - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - if (state && c->state != state) - continue; - - if (c->psm == psm) { - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) { - read_unlock(&chan_list_lock); - return c; - } - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - c1 = c; - } - } - - read_unlock(&chan_list_lock); - - return c1; -} - -int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst) -{ - struct sock *sk = chan->sk; - bdaddr_t *src = &bt_sk(sk)->src; - struct l2cap_conn *conn; - struct hci_conn *hcon; - struct hci_dev *hdev; - __u8 auth_type; - int err; - - BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), - chan->psm); - - hdev = hci_get_route(dst, src); - if (!hdev) - return -EHOSTUNREACH; - - hci_dev_lock(hdev); - - l2cap_chan_lock(chan); - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - goto done; - } - - if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) { - err = -EINVAL; - goto done; - } - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - lock_sock(sk); - - switch (sk->sk_state) { - case BT_CONNECT: - case BT_CONNECT2: - case BT_CONFIG: - /* Already connecting */ - err = 0; - release_sock(sk); - goto done; - - case BT_CONNECTED: - /* Already connected */ - err = -EISCONN; - release_sock(sk); - goto done; - - case BT_OPEN: - case BT_BOUND: - /* Can connect */ - break; - - default: - err = -EBADFD; - release_sock(sk); - goto done; - } - - /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, dst); - - release_sock(sk); - - chan->psm = psm; - chan->dcid = cid; - - auth_type = l2cap_get_auth_type(chan); - - if (chan->dcid == L2CAP_CID_LE_DATA) - hcon = hci_connect(hdev, LE_LINK, dst, - chan->sec_level, auth_type); - else - hcon = hci_connect(hdev, ACL_LINK, dst, - chan->sec_level, auth_type); - - if (IS_ERR(hcon)) { - err = PTR_ERR(hcon); - goto done; - } - - conn = l2cap_conn_add(hcon, 0); - if (!conn) { - hci_conn_put(hcon); - err = -ENOMEM; - goto done; - } - - /* Update source addr of the socket */ - bacpy(src, conn->src); - - l2cap_chan_unlock(chan); - l2cap_chan_add(conn, chan); - l2cap_chan_lock(chan); - - l2cap_state_change(chan, BT_CONNECT); - __set_chan_timer(chan, sk->sk_sndtimeo); - - if (hcon->state == BT_CONNECTED) { - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - __clear_chan_timer(chan); - if (l2cap_chan_check_security(chan)) - l2cap_state_change(chan, BT_CONNECTED); - } else - l2cap_do_start(chan); - } - - err = 0; - -done: - l2cap_chan_unlock(chan); - hci_dev_unlock(hdev); - hci_dev_put(hdev); - return err; -} - -int __l2cap_wait_ack(struct sock *sk) -{ - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - DECLARE_WAITQUEUE(wait, current); - int err = 0; - int timeo = HZ/5; - - add_wait_queue(sk_sleep(sk), &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (chan->unacked_frames > 0 && chan->conn) { - if (!timeo) - timeo = HZ/5; - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - set_current_state(TASK_INTERRUPTIBLE); - - err = sock_error(sk); - if (err) - break; - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return err; -} - -static void l2cap_monitor_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - monitor_timer.work); - - BT_DBG("chan %p", chan); - - l2cap_chan_lock(chan); - - if (chan->retry_count >= chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - l2cap_chan_unlock(chan); - l2cap_chan_put(chan); - return; - } - - chan->retry_count++; - __set_monitor_timer(chan); - - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); - l2cap_chan_unlock(chan); - l2cap_chan_put(chan); -} - -static void l2cap_retrans_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - retrans_timer.work); - - BT_DBG("chan %p", chan); - - l2cap_chan_lock(chan); - - chan->retry_count = 1; - __set_monitor_timer(chan); - - set_bit(CONN_WAIT_F, &chan->conn_state); - - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_POLL); - - l2cap_chan_unlock(chan); - l2cap_chan_put(chan); -} - -static void l2cap_drop_acked_frames(struct l2cap_chan *chan) -{ - struct sk_buff *skb; - - while ((skb = skb_peek(&chan->tx_q)) && - chan->unacked_frames) { - if (bt_cb(skb)->tx_seq == chan->expected_ack_seq) - break; - - skb = skb_dequeue(&chan->tx_q); - kfree_skb(skb); - - chan->unacked_frames--; - } - - if (!chan->unacked_frames) - __clear_retrans_timer(chan); -} - -static void l2cap_streaming_send(struct l2cap_chan *chan) -{ - struct sk_buff *skb; - u32 control; - u16 fcs; - - while ((skb = skb_dequeue(&chan->tx_q))) { - control = __get_control(chan, skb->data + L2CAP_HDR_SIZE); - control |= __set_txseq(chan, chan->next_tx_seq); - __put_control(chan, control, skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, - skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, - skb->data + skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, skb); - - chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); - } -} - -static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) -{ - struct sk_buff *skb, *tx_skb; - u16 fcs; - u32 control; - - skb = skb_peek(&chan->tx_q); - if (!skb) - return; - - while (bt_cb(skb)->tx_seq != tx_seq) { - if (skb_queue_is_last(&chan->tx_q, skb)) - return; - - skb = skb_queue_next(&chan->tx_q, skb); - } - - if (chan->remote_max_tx && - bt_cb(skb)->retries == chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - return; - } - - tx_skb = skb_clone(skb, GFP_ATOMIC); - bt_cb(skb)->retries++; - - control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); - control &= __get_sar_mask(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - control |= __set_reqseq(chan, chan->buffer_seq); - control |= __set_txseq(chan, tx_seq); - - __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)tx_skb->data, - tx_skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, - tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, tx_skb); -} - -static int l2cap_ertm_send(struct l2cap_chan *chan) -{ - struct sk_buff *skb, *tx_skb; - u16 fcs; - u32 control; - int nsent = 0; - - if (chan->state != BT_CONNECTED) - return -ENOTCONN; - - while ((skb = chan->tx_send_head) && (!l2cap_tx_window_full(chan))) { - - if (chan->remote_max_tx && - bt_cb(skb)->retries == chan->remote_max_tx) { - l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED); - break; - } - - tx_skb = skb_clone(skb, GFP_ATOMIC); - - bt_cb(skb)->retries++; - - control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); - control &= __get_sar_mask(chan); - - if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= __set_ctrl_final(chan); - - control |= __set_reqseq(chan, chan->buffer_seq); - control |= __set_txseq(chan, chan->next_tx_seq); - - __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); - - if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, - tx_skb->len - L2CAP_FCS_SIZE); - put_unaligned_le16(fcs, skb->data + - tx_skb->len - L2CAP_FCS_SIZE); - } - - l2cap_do_send(chan, tx_skb); - - __set_retrans_timer(chan); - - bt_cb(skb)->tx_seq = chan->next_tx_seq; - - chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); - - if (bt_cb(skb)->retries == 1) { - chan->unacked_frames++; - - if (!nsent++) - __clear_ack_timer(chan); - } - - chan->frames_sent++; - - if (skb_queue_is_last(&chan->tx_q, skb)) - chan->tx_send_head = NULL; - else - chan->tx_send_head = skb_queue_next(&chan->tx_q, skb); - } - - return nsent; -} - -static int l2cap_retransmit_frames(struct l2cap_chan *chan) -{ - int ret; - - if (!skb_queue_empty(&chan->tx_q)) - chan->tx_send_head = chan->tx_q.next; - - chan->next_tx_seq = chan->expected_ack_seq; - ret = l2cap_ertm_send(chan); - return ret; -} - -static void __l2cap_send_ack(struct l2cap_chan *chan) -{ - u32 control = 0; - - control |= __set_reqseq(chan, chan->buffer_seq); - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); - set_bit(CONN_RNR_SENT, &chan->conn_state); - l2cap_send_sframe(chan, control); - return; - } - - if (l2cap_ertm_send(chan) > 0) - return; - - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, control); -} - -static void l2cap_send_ack(struct l2cap_chan *chan) -{ - __clear_ack_timer(chan); - __l2cap_send_ack(chan); -} - -static void l2cap_send_srejtail(struct l2cap_chan *chan) -{ - struct srej_list *tail; - u32 control; - - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_ctrl_final(chan); - - tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); - control |= __set_reqseq(chan, tail->tx_seq); - - l2cap_send_sframe(chan, control); -} - -static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, - struct msghdr *msg, int len, - int count, struct sk_buff *skb) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff **frag; - int err, sent = 0; - - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) - return -EFAULT; - - sent += count; - len -= count; - - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len) { - count = min_t(unsigned int, conn->mtu, len); - - *frag = chan->ops->alloc_skb(chan, count, - msg->msg_flags & MSG_DONTWAIT, - &err); - - if (!*frag) - return err; - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) - return -EFAULT; - - (*frag)->priority = skb->priority; - - sent += count; - len -= count; - - frag = &(*frag)->next; - } - - return sent; -} - -static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; - struct l2cap_hdr *lh; - - BT_DBG("chan %p len %d priority %u", chan, (int)len, priority); - - count = min_t(unsigned int, (conn->mtu - hlen), len); - - skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - - if (!skb) - return ERR_PTR(err); - - skb->priority = priority; - - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(chan->psm, skb_put(skb, 2)); - - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - return skb; -} - -static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE; - struct l2cap_hdr *lh; - - BT_DBG("chan %p len %d", chan, (int)len); - - count = min_t(unsigned int, (conn->mtu - hlen), len); - - skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - - if (!skb) - return ERR_PTR(err); - - skb->priority = priority; - - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - return skb; -} - -static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 control, u16 sdulen) -{ - struct l2cap_conn *conn = chan->conn; - struct sk_buff *skb; - int err, count, hlen; - struct l2cap_hdr *lh; - - BT_DBG("chan %p len %d", chan, (int)len); - - if (!conn) - return ERR_PTR(-ENOTCONN); - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - hlen = L2CAP_EXT_HDR_SIZE; - else - hlen = L2CAP_ENH_HDR_SIZE; - - if (sdulen) - hlen += L2CAP_SDULEN_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - hlen += L2CAP_FCS_SIZE; - - count = min_t(unsigned int, (conn->mtu - hlen), len); - - skb = chan->ops->alloc_skb(chan, count + hlen, - msg->msg_flags & MSG_DONTWAIT, &err); - - if (!skb) - return ERR_PTR(err); - - /* Create L2CAP header */ - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(chan->dcid); - lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - - __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); - - if (sdulen) - put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE)); - - err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); - if (unlikely(err < 0)) { - kfree_skb(skb); - return ERR_PTR(err); - } - - if (chan->fcs == L2CAP_FCS_CRC16) - put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE)); - - bt_cb(skb)->retries = 0; - return skb; -} - -static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) -{ - struct sk_buff *skb; - struct sk_buff_head sar_queue; - u32 control; - size_t size = 0; - - skb_queue_head_init(&sar_queue); - control = __set_ctrl_sar(chan, L2CAP_SAR_START); - skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - __skb_queue_tail(&sar_queue, skb); - len -= chan->remote_mps; - size += chan->remote_mps; - - while (len > 0) { - size_t buflen; - - if (len > chan->remote_mps) { - control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE); - buflen = chan->remote_mps; - } else { - control = __set_ctrl_sar(chan, L2CAP_SAR_END); - buflen = len; - } - - skb = l2cap_create_iframe_pdu(chan, msg, buflen, control, 0); - if (IS_ERR(skb)) { - skb_queue_purge(&sar_queue); - return PTR_ERR(skb); - } - - __skb_queue_tail(&sar_queue, skb); - len -= buflen; - size += buflen; - } - skb_queue_splice_tail(&sar_queue, &chan->tx_q); - if (chan->tx_send_head == NULL) - chan->tx_send_head = sar_queue.next; - - return size; -} - -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority) -{ - struct sk_buff *skb; - u32 control; - int err; - - /* Connectionless channel */ - if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { - skb = l2cap_create_connless_pdu(chan, msg, len, priority); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - l2cap_do_send(chan, skb); - return len; - } - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - /* Check outgoing MTU */ - if (len > chan->omtu) - return -EMSGSIZE; - - /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len, priority); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - l2cap_do_send(chan, skb); - err = len; - break; - - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - /* Entire SDU fits into one PDU */ - if (len <= chan->remote_mps) { - control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED); - skb = l2cap_create_iframe_pdu(chan, msg, len, control, - 0); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - __skb_queue_tail(&chan->tx_q, skb); - - if (chan->tx_send_head == NULL) - chan->tx_send_head = skb; - - } else { - /* Segment SDU into multiples PDUs */ - err = l2cap_sar_segment_sdu(chan, msg, len); - if (err < 0) - return err; - } - - if (chan->mode == L2CAP_MODE_STREAMING) { - l2cap_streaming_send(chan); - err = len; - break; - } - - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - err = len; - break; - } - - err = l2cap_ertm_send(chan); - if (err >= 0) - err = len; - - break; - - default: - BT_DBG("bad state %1.1x", chan->mode); - err = -EBADFD; - } - - return err; -} - -/* Copy frame to all raw sockets on that connection */ -static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct sk_buff *nskb; - struct l2cap_chan *chan; - - BT_DBG("conn %p", conn); - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - struct sock *sk = chan->sk; - if (chan->chan_type != L2CAP_CHAN_RAW) - continue; - - /* Don't send frame to the socket it came from */ - if (skb->sk == sk) - continue; - nskb = skb_clone(skb, GFP_ATOMIC); - if (!nskb) - continue; - - if (chan->ops->recv(chan->data, nskb)) - kfree_skb(nskb); - } - - mutex_unlock(&conn->chan_lock); -} - -/* ---- L2CAP signalling commands ---- */ -static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, - u8 code, u8 ident, u16 dlen, void *data) -{ - struct sk_buff *skb, **frag; - struct l2cap_cmd_hdr *cmd; - struct l2cap_hdr *lh; - int len, count; - - BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", - conn, code, ident, dlen); - - len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen; - count = min_t(unsigned int, conn->mtu, len); - - skb = bt_skb_alloc(count, GFP_ATOMIC); - if (!skb) - return NULL; - - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen); - - if (conn->hcon->type == LE_LINK) - lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING); - else - lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING); - - cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE); - cmd->code = code; - cmd->ident = ident; - cmd->len = cpu_to_le16(dlen); - - if (dlen) { - count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE; - memcpy(skb_put(skb, count), data, count); - data += count; - } - - len -= skb->len; - - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len) { - count = min_t(unsigned int, conn->mtu, len); - - *frag = bt_skb_alloc(count, GFP_ATOMIC); - if (!*frag) - goto fail; - - memcpy(skb_put(*frag, count), data, count); - - len -= count; - data += count; - - frag = &(*frag)->next; - } - - return skb; - -fail: - kfree_skb(skb); - return NULL; -} - -static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val) -{ - struct l2cap_conf_opt *opt = *ptr; - int len; - - len = L2CAP_CONF_OPT_SIZE + opt->len; - *ptr += len; - - *type = opt->type; - *olen = opt->len; - - switch (opt->len) { - case 1: - *val = *((u8 *) opt->val); - break; - - case 2: - *val = get_unaligned_le16(opt->val); - break; - - case 4: - *val = get_unaligned_le32(opt->val); - break; - - default: - *val = (unsigned long) opt->val; - break; - } - - BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val); - return len; -} - -static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) -{ - struct l2cap_conf_opt *opt = *ptr; - - BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val); - - opt->type = type; - opt->len = len; - - switch (len) { - case 1: - *((u8 *) opt->val) = val; - break; - - case 2: - put_unaligned_le16(val, opt->val); - break; - - case 4: - put_unaligned_le32(val, opt->val); - break; - - default: - memcpy(opt->val, (void *) val, len); - break; - } - - *ptr += L2CAP_CONF_OPT_SIZE + len; -} - -static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) -{ - struct l2cap_conf_efs efs; - - switch (chan->mode) { - case L2CAP_MODE_ERTM: - efs.id = chan->local_id; - efs.stype = chan->local_stype; - efs.msdu = cpu_to_le16(chan->local_msdu); - efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); - efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); - efs.flush_to = cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); - break; - - case L2CAP_MODE_STREAMING: - efs.id = 1; - efs.stype = L2CAP_SERV_BESTEFFORT; - efs.msdu = cpu_to_le16(chan->local_msdu); - efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); - efs.acc_lat = 0; - efs.flush_to = 0; - break; - - default: - return; - } - - l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), - (unsigned long) &efs); -} - -static void l2cap_ack_timeout(struct work_struct *work) -{ - struct l2cap_chan *chan = container_of(work, struct l2cap_chan, - ack_timer.work); - - BT_DBG("chan %p", chan); - - l2cap_chan_lock(chan); - - __l2cap_send_ack(chan); - - l2cap_chan_unlock(chan); - - l2cap_chan_put(chan); -} - -static inline void l2cap_ertm_init(struct l2cap_chan *chan) -{ - chan->expected_ack_seq = 0; - chan->unacked_frames = 0; - chan->buffer_seq = 0; - chan->num_acked = 0; - chan->frames_sent = 0; - - INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout); - INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout); - INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout); - - skb_queue_head_init(&chan->srej_q); - - INIT_LIST_HEAD(&chan->srej_l); -} - -static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) -{ - switch (mode) { - case L2CAP_MODE_STREAMING: - case L2CAP_MODE_ERTM: - if (l2cap_mode_supported(mode, remote_feat_mask)) - return mode; - /* fall through */ - default: - return L2CAP_MODE_BASIC; - } -} - -static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) -{ - return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; -} - -static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) -{ - return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; -} - -static inline void l2cap_txwin_setup(struct l2cap_chan *chan) -{ - if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __l2cap_ews_supported(chan)) { - /* use extended control field */ - set_bit(FLAG_EXT_CTRL, &chan->flags); - chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; - } else { - chan->tx_win = min_t(u16, chan->tx_win, - L2CAP_DEFAULT_TX_WINDOW); - chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; - } -} - -static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) -{ - struct l2cap_conf_req *req = data; - struct l2cap_conf_rfc rfc = { .mode = chan->mode }; - void *ptr = req->data; - u16 size; - - BT_DBG("chan %p", chan); - - if (chan->num_conf_req || chan->num_conf_rsp) - goto done; - - switch (chan->mode) { - case L2CAP_MODE_STREAMING: - case L2CAP_MODE_ERTM: - if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) - break; - - if (__l2cap_efs_supported(chan)) - set_bit(FLAG_EFS_ENABLE, &chan->flags); - - /* fall through */ - default: - chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); - break; - } - -done: - if (chan->imtu != L2CAP_DEFAULT_MTU) - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && - !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) - break; - - rfc.mode = L2CAP_MODE_BASIC; - rfc.txwin_size = 0; - rfc.max_transmit = 0; - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; - rfc.max_pdu_size = 0; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); - break; - - case L2CAP_MODE_ERTM: - rfc.mode = L2CAP_MODE_ERTM; - rfc.max_transmit = chan->max_tx; - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; - - size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); - rfc.max_pdu_size = cpu_to_le16(size); - - l2cap_txwin_setup(chan); - - rfc.txwin_size = min_t(u16, chan->tx_win, - L2CAP_DEFAULT_TX_WINDOW); - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); - - if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) - l2cap_add_opt_efs(&ptr, chan); - - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { - chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); - } - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, - chan->tx_win); - break; - - case L2CAP_MODE_STREAMING: - rfc.mode = L2CAP_MODE_STREAMING; - rfc.txwin_size = 0; - rfc.max_transmit = 0; - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; - - size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); - rfc.max_pdu_size = cpu_to_le16(size); - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), - (unsigned long) &rfc); - - if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) - l2cap_add_opt_efs(&ptr, chan); - - if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) - break; - - if (chan->fcs == L2CAP_FCS_NONE || - test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) { - chan->fcs = L2CAP_FCS_NONE; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); - } - break; - } - - req->dcid = cpu_to_le16(chan->dcid); - req->flags = cpu_to_le16(0); - - return ptr - data; -} - -static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) -{ - struct l2cap_conf_rsp *rsp = data; - void *ptr = rsp->data; - void *req = chan->conf_req; - int len = chan->conf_len; - int type, hint, olen; - unsigned long val; - struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; - struct l2cap_conf_efs efs; - u8 remote_efs = 0; - u16 mtu = L2CAP_DEFAULT_MTU; - u16 result = L2CAP_CONF_SUCCESS; - u16 size; - - BT_DBG("chan %p", chan); - - while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&req, &type, &olen, &val); - - hint = type & L2CAP_CONF_HINT; - type &= L2CAP_CONF_MASK; - - switch (type) { - case L2CAP_CONF_MTU: - mtu = val; - break; - - case L2CAP_CONF_FLUSH_TO: - chan->flush_to = val; - break; - - case L2CAP_CONF_QOS: - break; - - case L2CAP_CONF_RFC: - if (olen == sizeof(rfc)) - memcpy(&rfc, (void *) val, olen); - break; - - case L2CAP_CONF_FCS: - if (val == L2CAP_FCS_NONE) - set_bit(CONF_NO_FCS_RECV, &chan->conf_state); - break; - - case L2CAP_CONF_EFS: - remote_efs = 1; - if (olen == sizeof(efs)) - memcpy(&efs, (void *) val, olen); - break; - - case L2CAP_CONF_EWS: - if (!enable_hs) - return -ECONNREFUSED; - - set_bit(FLAG_EXT_CTRL, &chan->flags); - set_bit(CONF_EWS_RECV, &chan->conf_state); - chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; - chan->remote_tx_win = val; - break; - - default: - if (hint) - break; - - result = L2CAP_CONF_UNKNOWN; - *((u8 *) ptr++) = type; - break; - } - } - - if (chan->num_conf_rsp || chan->num_conf_req > 1) - goto done; - - switch (chan->mode) { - case L2CAP_MODE_STREAMING: - case L2CAP_MODE_ERTM: - if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) { - chan->mode = l2cap_select_mode(rfc.mode, - chan->conn->feat_mask); - break; - } - - if (remote_efs) { - if (__l2cap_efs_supported(chan)) - set_bit(FLAG_EFS_ENABLE, &chan->flags); - else - return -ECONNREFUSED; - } - - if (chan->mode != rfc.mode) - return -ECONNREFUSED; - - break; - } - -done: - if (chan->mode != rfc.mode) { - result = L2CAP_CONF_UNACCEPT; - rfc.mode = chan->mode; - - if (chan->num_conf_rsp == 1) - return -ECONNREFUSED; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - } - - if (result == L2CAP_CONF_SUCCESS) { - /* Configure output options and let the other side know - * which ones we don't like. */ - - if (mtu < L2CAP_DEFAULT_MIN_MTU) - result = L2CAP_CONF_UNACCEPT; - else { - chan->omtu = mtu; - set_bit(CONF_MTU_DONE, &chan->conf_state); - } - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); - - if (remote_efs) { - if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) { - - result = L2CAP_CONF_UNACCEPT; - - if (chan->num_conf_req >= 1) - return -ECONNREFUSED; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), - (unsigned long) &efs); - } else { - /* Send PENDING Conf Rsp */ - result = L2CAP_CONF_PENDING; - set_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - } - } - - switch (rfc.mode) { - case L2CAP_MODE_BASIC: - chan->fcs = L2CAP_FCS_NONE; - set_bit(CONF_MODE_DONE, &chan->conf_state); - break; - - case L2CAP_MODE_ERTM: - if (!test_bit(CONF_EWS_RECV, &chan->conf_state)) - chan->remote_tx_win = rfc.txwin_size; - else - rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; - - chan->remote_max_tx = rfc.max_transmit; - - size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), - chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); - rfc.max_pdu_size = cpu_to_le16(size); - chan->remote_mps = size; - - rfc.retrans_timeout = - le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = - le16_to_cpu(L2CAP_DEFAULT_MONITOR_TO); - - set_bit(CONF_MODE_DONE, &chan->conf_state); - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - - if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { - chan->remote_id = efs.id; - chan->remote_stype = efs.stype; - chan->remote_msdu = le16_to_cpu(efs.msdu); - chan->remote_flush_to = - le32_to_cpu(efs.flush_to); - chan->remote_acc_lat = - le32_to_cpu(efs.acc_lat); - chan->remote_sdu_itime = - le32_to_cpu(efs.sdu_itime); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), (unsigned long) &efs); - } - break; - - case L2CAP_MODE_STREAMING: - size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), - chan->conn->mtu - - L2CAP_EXT_HDR_SIZE - - L2CAP_SDULEN_SIZE - - L2CAP_FCS_SIZE); - rfc.max_pdu_size = cpu_to_le16(size); - chan->remote_mps = size; - - set_bit(CONF_MODE_DONE, &chan->conf_state); - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - - break; - - default: - result = L2CAP_CONF_UNACCEPT; - - memset(&rfc, 0, sizeof(rfc)); - rfc.mode = chan->mode; - } - - if (result == L2CAP_CONF_SUCCESS) - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - } - rsp->scid = cpu_to_le16(chan->dcid); - rsp->result = cpu_to_le16(result); - rsp->flags = cpu_to_le16(0x0000); - - return ptr - data; -} - -static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result) -{ - struct l2cap_conf_req *req = data; - void *ptr = req->data; - int type, olen; - unsigned long val; - struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; - struct l2cap_conf_efs efs; - - BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); - - while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - - switch (type) { - case L2CAP_CONF_MTU: - if (val < L2CAP_DEFAULT_MIN_MTU) { - *result = L2CAP_CONF_UNACCEPT; - chan->imtu = L2CAP_DEFAULT_MIN_MTU; - } else - chan->imtu = val; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu); - break; - - case L2CAP_CONF_FLUSH_TO: - chan->flush_to = val; - l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, - 2, chan->flush_to); - break; - - case L2CAP_CONF_RFC: - if (olen == sizeof(rfc)) - memcpy(&rfc, (void *)val, olen); - - if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) && - rfc.mode != chan->mode) - return -ECONNREFUSED; - - chan->fcs = 0; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, - sizeof(rfc), (unsigned long) &rfc); - break; - - case L2CAP_CONF_EWS: - chan->tx_win = min_t(u16, val, - L2CAP_DEFAULT_EXT_WINDOW); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, - chan->tx_win); - break; - - case L2CAP_CONF_EFS: - if (olen == sizeof(efs)) - memcpy(&efs, (void *)val, olen); - - if (chan->local_stype != L2CAP_SERV_NOTRAFIC && - efs.stype != L2CAP_SERV_NOTRAFIC && - efs.stype != chan->local_stype) - return -ECONNREFUSED; - - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), (unsigned long) &efs); - break; - } - } - - if (chan->mode == L2CAP_MODE_BASIC && chan->mode != rfc.mode) - return -ECONNREFUSED; - - chan->mode = rfc.mode; - - if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) { - switch (rfc.mode) { - case L2CAP_MODE_ERTM: - chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - chan->mps = le16_to_cpu(rfc.max_pdu_size); - - if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { - chan->local_msdu = le16_to_cpu(efs.msdu); - chan->local_sdu_itime = - le32_to_cpu(efs.sdu_itime); - chan->local_acc_lat = le32_to_cpu(efs.acc_lat); - chan->local_flush_to = - le32_to_cpu(efs.flush_to); - } - break; - - case L2CAP_MODE_STREAMING: - chan->mps = le16_to_cpu(rfc.max_pdu_size); - } - } - - req->dcid = cpu_to_le16(chan->dcid); - req->flags = cpu_to_le16(0x0000); - - return ptr - data; -} - -static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags) -{ - struct l2cap_conf_rsp *rsp = data; - void *ptr = rsp->data; - - BT_DBG("chan %p", chan); - - rsp->scid = cpu_to_le16(chan->dcid); - rsp->result = cpu_to_le16(result); - rsp->flags = cpu_to_le16(flags); - - return ptr - data; -} - -void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) -{ - struct l2cap_conn_rsp rsp; - struct l2cap_conn *conn = chan->conn; - u8 buf[128]; - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); - rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, - L2CAP_CONN_RSP, sizeof(rsp), &rsp); - - if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) - return; - - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); - chan->num_conf_req++; -} - -static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len) -{ - int type, olen; - unsigned long val; - struct l2cap_conf_rfc rfc; - - BT_DBG("chan %p, rsp %p, len %d", chan, rsp, len); - - if ((chan->mode != L2CAP_MODE_ERTM) && (chan->mode != L2CAP_MODE_STREAMING)) - return; - - while (len >= L2CAP_CONF_OPT_SIZE) { - len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val); - - switch (type) { - case L2CAP_CONF_RFC: - if (olen == sizeof(rfc)) - memcpy(&rfc, (void *)val, olen); - goto done; - } - } - - /* Use sane default values in case a misbehaving remote device - * did not send an RFC option. - */ - rfc.mode = chan->mode; - rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); - rfc.max_pdu_size = cpu_to_le16(chan->imtu); - - BT_ERR("Expected RFC option was not found, using defaults"); - -done: - switch (rfc.mode) { - case L2CAP_MODE_ERTM: - chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); - chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); - chan->mps = le16_to_cpu(rfc.max_pdu_size); - break; - case L2CAP_MODE_STREAMING: - chan->mps = le16_to_cpu(rfc.max_pdu_size); - } -} - -static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data; - - if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD) - return 0; - - if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && - cmd->ident == conn->info_ident) { - cancel_delayed_work(&conn->info_timer); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - } - - return 0; -} - -static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; - struct l2cap_conn_rsp rsp; - struct l2cap_chan *chan = NULL, *pchan; - struct sock *parent, *sk = NULL; - int result, status = L2CAP_CS_NO_INFO; - - u16 dcid = 0, scid = __le16_to_cpu(req->scid); - __le16 psm = req->psm; - - BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); - - /* Check if we have socket listening on psm */ - pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src); - if (!pchan) { - result = L2CAP_CR_BAD_PSM; - goto sendresp; - } - - parent = pchan->sk; - - mutex_lock(&conn->chan_lock); - lock_sock(parent); - - /* Check if the ACL is secure enough (if not SDP) */ - if (psm != cpu_to_le16(0x0001) && - !hci_conn_check_link_mode(conn->hcon)) { - conn->disc_reason = HCI_ERROR_AUTH_FAILURE; - result = L2CAP_CR_SEC_BLOCK; - goto response; - } - - result = L2CAP_CR_NO_MEM; - - /* Check for backlog size */ - if (sk_acceptq_is_full(parent)) { - BT_DBG("backlog full %d", parent->sk_ack_backlog); - goto response; - } - - chan = pchan->ops->new_connection(pchan->data); - if (!chan) - goto response; - - sk = chan->sk; - - /* Check if we already have channel with that dcid */ - if (__l2cap_get_chan_by_dcid(conn, scid)) { - sock_set_flag(sk, SOCK_ZAPPED); - chan->ops->close(chan->data); - goto response; - } - - hci_conn_hold(conn->hcon); - - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); - chan->psm = psm; - chan->dcid = scid; - - bt_accept_enqueue(parent, sk); - - __l2cap_chan_add(conn, chan); - - dcid = chan->scid; - - __set_chan_timer(chan, sk->sk_sndtimeo); - - chan->ident = cmd->ident; - - if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { - if (l2cap_chan_check_security(chan)) { - if (bt_sk(sk)->defer_setup) { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - status = L2CAP_CS_AUTHOR_PEND; - parent->sk_data_ready(parent, 0); - } else { - __l2cap_state_change(chan, BT_CONFIG); - result = L2CAP_CR_SUCCESS; - status = L2CAP_CS_NO_INFO; - } - } else { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - status = L2CAP_CS_AUTHEN_PEND; - } - } else { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - status = L2CAP_CS_NO_INFO; - } - -response: - release_sock(parent); - mutex_unlock(&conn->chan_lock); - -sendresp: - rsp.scid = cpu_to_le16(scid); - rsp.dcid = cpu_to_le16(dcid); - rsp.result = cpu_to_le16(result); - rsp.status = cpu_to_le16(status); - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); - - if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) { - struct l2cap_info_req info; - info.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); - - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; - conn->info_ident = l2cap_get_ident(conn); - - schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT); - - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(info), &info); - } - - if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) && - result == L2CAP_CR_SUCCESS) { - u8 buf[128]; - set_bit(CONF_REQ_SENT, &chan->conf_state); - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); - chan->num_conf_req++; - } - - return 0; -} - -static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; - u16 scid, dcid, result, status; - struct l2cap_chan *chan; - u8 req[128]; - int err; - - scid = __le16_to_cpu(rsp->scid); - dcid = __le16_to_cpu(rsp->dcid); - result = __le16_to_cpu(rsp->result); - status = __le16_to_cpu(rsp->status); - - BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", - dcid, scid, result, status); - - mutex_lock(&conn->chan_lock); - - if (scid) { - chan = __l2cap_get_chan_by_scid(conn, scid); - if (!chan) { - err = -EFAULT; - goto unlock; - } - } else { - chan = __l2cap_get_chan_by_ident(conn, cmd->ident); - if (!chan) { - err = -EFAULT; - goto unlock; - } - } - - err = 0; - - l2cap_chan_lock(chan); - - switch (result) { - case L2CAP_CR_SUCCESS: - l2cap_state_change(chan, BT_CONFIG); - chan->ident = 0; - chan->dcid = dcid; - clear_bit(CONF_CONNECT_PEND, &chan->conf_state); - - if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) - break; - - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, req), req); - chan->num_conf_req++; - break; - - case L2CAP_CR_PEND: - set_bit(CONF_CONNECT_PEND, &chan->conf_state); - break; - - default: - l2cap_chan_del(chan, ECONNREFUSED); - break; - } - - l2cap_chan_unlock(chan); - -unlock: - mutex_unlock(&conn->chan_lock); - - return err; -} - -static inline void set_default_fcs(struct l2cap_chan *chan) -{ - /* FCS is enabled only in ERTM or streaming mode, if one or both - * sides request it. - */ - if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING) - chan->fcs = L2CAP_FCS_NONE; - else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) - chan->fcs = L2CAP_FCS_CRC16; -} - -static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) -{ - struct l2cap_conf_req *req = (struct l2cap_conf_req *) data; - u16 dcid, flags; - u8 rsp[64]; - struct l2cap_chan *chan; - int len; - - dcid = __le16_to_cpu(req->dcid); - flags = __le16_to_cpu(req->flags); - - BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); - - chan = l2cap_get_chan_by_scid(conn, dcid); - if (!chan) - return -ENOENT; - - if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) { - struct l2cap_cmd_rej_cid rej; - - rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID); - rej.scid = cpu_to_le16(chan->scid); - rej.dcid = cpu_to_le16(chan->dcid); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); - goto unlock; - } - - /* Reject if config buffer is too small. */ - len = cmd_len - sizeof(*req); - if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) { - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_REJECT, flags), rsp); - goto unlock; - } - - /* Store config. */ - memcpy(chan->conf_req + chan->conf_len, req->data, len); - chan->conf_len += len; - - if (flags & 0x0001) { - /* Incomplete config. Send empty response. */ - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, 0x0001), rsp); - goto unlock; - } - - /* Complete config. */ - len = l2cap_parse_conf_req(chan, rsp); - if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto unlock; - } - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); - chan->num_conf_rsp++; - - /* Reset config buffer. */ - chan->conf_len = 0; - - if (!test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) - goto unlock; - - if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { - set_default_fcs(chan); - - l2cap_state_change(chan, BT_CONNECTED); - - chan->next_tx_seq = 0; - chan->expected_tx_seq = 0; - skb_queue_head_init(&chan->tx_q); - if (chan->mode == L2CAP_MODE_ERTM) - l2cap_ertm_init(chan); - - l2cap_chan_ready(chan); - goto unlock; - } - - if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) { - u8 buf[64]; - l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, - l2cap_build_conf_req(chan, buf), buf); - chan->num_conf_req++; - } - - /* Got Conf Rsp PENDING from remote side and asume 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)) { - - /* check compatibility */ - - clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, - L2CAP_CONF_SUCCESS, 0x0000), rsp); - } - -unlock: - l2cap_chan_unlock(chan); - return 0; -} - -static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data; - u16 scid, flags, result; - struct l2cap_chan *chan; - int len = cmd->len - sizeof(*rsp); - - scid = __le16_to_cpu(rsp->scid); - flags = __le16_to_cpu(rsp->flags); - result = __le16_to_cpu(rsp->result); - - BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", - scid, flags, result); - - chan = l2cap_get_chan_by_scid(conn, scid); - if (!chan) - return 0; - - switch (result) { - case L2CAP_CONF_SUCCESS: - l2cap_conf_rfc_get(chan, rsp->data, len); - clear_bit(CONF_REM_CONF_PEND, &chan->conf_state); - break; - - case L2CAP_CONF_PENDING: - set_bit(CONF_REM_CONF_PEND, &chan->conf_state); - - if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { - char buf[64]; - - len = l2cap_parse_conf_rsp(chan, rsp->data, len, - buf, &result); - if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - /* check compatibility */ - - clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); - set_bit(CONF_OUTPUT_DONE, &chan->conf_state); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, buf, - L2CAP_CONF_SUCCESS, 0x0000), buf); - } - goto done; - - case L2CAP_CONF_UNACCEPT: - if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { - char req[64]; - - if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - /* throw out any old stored conf requests */ - result = L2CAP_CONF_SUCCESS; - len = l2cap_parse_conf_rsp(chan, rsp->data, len, - req, &result); - if (len < 0) { - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - l2cap_send_cmd(conn, l2cap_get_ident(conn), - L2CAP_CONF_REQ, len, req); - chan->num_conf_req++; - if (result != L2CAP_CONF_SUCCESS) - goto done; - break; - } - - default: - l2cap_chan_set_err(chan, ECONNRESET); - - __set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT); - l2cap_send_disconn_req(conn, chan, ECONNRESET); - goto done; - } - - if (flags & 0x01) - goto done; - - set_bit(CONF_INPUT_DONE, &chan->conf_state); - - if (test_bit(CONF_OUTPUT_DONE, &chan->conf_state)) { - set_default_fcs(chan); - - l2cap_state_change(chan, BT_CONNECTED); - chan->next_tx_seq = 0; - chan->expected_tx_seq = 0; - skb_queue_head_init(&chan->tx_q); - if (chan->mode == L2CAP_MODE_ERTM) - l2cap_ertm_init(chan); - - l2cap_chan_ready(chan); - } - -done: - l2cap_chan_unlock(chan); - return 0; -} - -static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data; - struct l2cap_disconn_rsp rsp; - u16 dcid, scid; - struct l2cap_chan *chan; - struct sock *sk; - - scid = __le16_to_cpu(req->scid); - dcid = __le16_to_cpu(req->dcid); - - BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); - - mutex_lock(&conn->chan_lock); - - chan = __l2cap_get_chan_by_scid(conn, dcid); - if (!chan) { - mutex_unlock(&conn->chan_lock); - return 0; - } - - l2cap_chan_lock(chan); - - sk = chan->sk; - - rsp.dcid = cpu_to_le16(chan->scid); - rsp.scid = cpu_to_le16(chan->dcid); - l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); - - lock_sock(sk); - sk->sk_shutdown = SHUTDOWN_MASK; - release_sock(sk); - - l2cap_chan_del(chan, ECONNRESET); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - - mutex_unlock(&conn->chan_lock); - - return 0; -} - -static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data; - u16 dcid, scid; - struct l2cap_chan *chan; - - scid = __le16_to_cpu(rsp->scid); - dcid = __le16_to_cpu(rsp->dcid); - - BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); - - mutex_lock(&conn->chan_lock); - - chan = __l2cap_get_chan_by_scid(conn, scid); - if (!chan) { - mutex_unlock(&conn->chan_lock); - return 0; - } - - l2cap_chan_lock(chan); - - l2cap_chan_del(chan, 0); - - l2cap_chan_unlock(chan); - - chan->ops->close(chan->data); - - mutex_unlock(&conn->chan_lock); - - return 0; -} - -static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_info_req *req = (struct l2cap_info_req *) data; - u16 type; - - type = __le16_to_cpu(req->type); - - BT_DBG("type 0x%4.4x", type); - - if (type == L2CAP_IT_FEAT_MASK) { - u8 buf[8]; - u32 feat_mask = l2cap_feat_mask; - struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; - rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); - rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - if (!disable_ertm) - feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING - | L2CAP_FEAT_FCS; - if (enable_hs) - feat_mask |= L2CAP_FEAT_EXT_FLOW - | L2CAP_FEAT_EXT_WINDOW; - - put_unaligned_le32(feat_mask, rsp->data); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(buf), buf); - } else if (type == L2CAP_IT_FIXED_CHAN) { - u8 buf[12]; - struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; - - if (enable_hs) - l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; - else - l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; - - rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); - rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(buf), buf); - } else { - struct l2cap_info_rsp rsp; - rsp.type = cpu_to_le16(type); - rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP); - l2cap_send_cmd(conn, cmd->ident, - L2CAP_INFO_RSP, sizeof(rsp), &rsp); - } - - return 0; -} - -static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data; - u16 type, result; - - type = __le16_to_cpu(rsp->type); - result = __le16_to_cpu(rsp->result); - - BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); - - /* L2CAP Info req/rsp are unbound to channels, add extra checks */ - if (cmd->ident != conn->info_ident || - conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) - return 0; - - cancel_delayed_work(&conn->info_timer); - - if (result != L2CAP_IR_SUCCESS) { - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - - return 0; - } - - switch (type) { - case L2CAP_IT_FEAT_MASK: - conn->feat_mask = get_unaligned_le32(rsp->data); - - if (conn->feat_mask & L2CAP_FEAT_FIXED_CHAN) { - struct l2cap_info_req req; - req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); - - conn->info_ident = l2cap_get_ident(conn); - - l2cap_send_cmd(conn, conn->info_ident, - L2CAP_INFO_REQ, sizeof(req), &req); - } else { - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - } - break; - - case L2CAP_IT_FIXED_CHAN: - conn->fixed_chan_mask = rsp->data[0]; - conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE; - conn->info_ident = 0; - - l2cap_conn_start(conn); - break; - } - - return 0; -} - -static inline int l2cap_create_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, - void *data) -{ - struct l2cap_create_chan_req *req = data; - struct l2cap_create_chan_rsp rsp; - u16 psm, scid; - - if (cmd_len != sizeof(*req)) - return -EPROTO; - - if (!enable_hs) - return -EINVAL; - - psm = le16_to_cpu(req->psm); - scid = le16_to_cpu(req->scid); - - BT_DBG("psm %d, scid %d, amp_id %d", psm, scid, req->amp_id); - - /* Placeholder: Always reject */ - rsp.dcid = 0; - rsp.scid = cpu_to_le16(scid); - rsp.result = L2CAP_CR_NO_MEM; - rsp.status = L2CAP_CS_NO_INFO; - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, - sizeof(rsp), &rsp); - - return 0; -} - -static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, void *data) -{ - BT_DBG("conn %p", conn); - - return l2cap_connect_rsp(conn, cmd, data); -} - -static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, - u16 icid, u16 result) -{ - struct l2cap_move_chan_rsp rsp; - - BT_DBG("icid %d, result %d", icid, result); - - rsp.icid = cpu_to_le16(icid); - rsp.result = cpu_to_le16(result); - - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); -} - -static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, - struct l2cap_chan *chan, u16 icid, u16 result) -{ - struct l2cap_move_chan_cfm cfm; - u8 ident; - - BT_DBG("icid %d, result %d", icid, result); - - ident = l2cap_get_ident(conn); - if (chan) - chan->ident = ident; - - cfm.icid = cpu_to_le16(icid); - cfm.result = cpu_to_le16(result); - - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); -} - -static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, - u16 icid) -{ - struct l2cap_move_chan_cfm_rsp rsp; - - BT_DBG("icid %d", icid); - - rsp.icid = cpu_to_le16(icid); - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); -} - -static inline int l2cap_move_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_req *req = data; - u16 icid = 0; - u16 result = L2CAP_MR_NOT_ALLOWED; - - if (cmd_len != sizeof(*req)) - return -EPROTO; - - icid = le16_to_cpu(req->icid); - - BT_DBG("icid %d, dest_amp_id %d", icid, req->dest_amp_id); - - if (!enable_hs) - return -EINVAL; - - /* Placeholder: Always refuse */ - l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); - - return 0; -} - -static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_rsp *rsp = data; - u16 icid, result; - - if (cmd_len != sizeof(*rsp)) - return -EPROTO; - - icid = le16_to_cpu(rsp->icid); - result = le16_to_cpu(rsp->result); - - BT_DBG("icid %d, result %d", icid, result); - - /* Placeholder: Always unconfirmed */ - l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); - - return 0; -} - -static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_cfm *cfm = data; - u16 icid, result; - - if (cmd_len != sizeof(*cfm)) - return -EPROTO; - - icid = le16_to_cpu(cfm->icid); - result = le16_to_cpu(cfm->result); - - BT_DBG("icid %d, result %d", icid, result); - - l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); - - return 0; -} - -static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) -{ - struct l2cap_move_chan_cfm_rsp *rsp = data; - u16 icid; - - if (cmd_len != sizeof(*rsp)) - return -EPROTO; - - icid = le16_to_cpu(rsp->icid); - - BT_DBG("icid %d", icid); - - return 0; -} - -static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, - u16 to_multiplier) -{ - u16 max_latency; - - if (min > max || min < 6 || max > 3200) - return -EINVAL; - - if (to_multiplier < 10 || to_multiplier > 3200) - return -EINVAL; - - if (max >= to_multiplier * 8) - return -EINVAL; - - max_latency = (to_multiplier * 8 / max) - 1; - if (latency > 499 || latency > max_latency) - return -EINVAL; - - return 0; -} - -static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) -{ - struct hci_conn *hcon = conn->hcon; - struct l2cap_conn_param_update_req *req; - struct l2cap_conn_param_update_rsp rsp; - u16 min, max, latency, to_multiplier, cmd_len; - int err; - - if (!(hcon->link_mode & HCI_LM_MASTER)) - return -EINVAL; - - cmd_len = __le16_to_cpu(cmd->len); - if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) - return -EPROTO; - - req = (struct l2cap_conn_param_update_req *) data; - min = __le16_to_cpu(req->min); - max = __le16_to_cpu(req->max); - latency = __le16_to_cpu(req->latency); - to_multiplier = __le16_to_cpu(req->to_multiplier); - - BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x", - min, max, latency, to_multiplier); - - memset(&rsp, 0, sizeof(rsp)); - - err = l2cap_check_conn_param(min, max, latency, to_multiplier); - if (err) - rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); - else - rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, - sizeof(rsp), &rsp); - - if (!err) - hci_le_conn_update(hcon, min, max, latency, to_multiplier); - - return 0; -} - -static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) -{ - int err = 0; - - switch (cmd->code) { - case L2CAP_COMMAND_REJ: - l2cap_command_rej(conn, cmd, data); - break; - - case L2CAP_CONN_REQ: - err = l2cap_connect_req(conn, cmd, data); - break; - - case L2CAP_CONN_RSP: - err = l2cap_connect_rsp(conn, cmd, data); - break; - - case L2CAP_CONF_REQ: - err = l2cap_config_req(conn, cmd, cmd_len, data); - break; - - case L2CAP_CONF_RSP: - err = l2cap_config_rsp(conn, cmd, data); - break; - - case L2CAP_DISCONN_REQ: - err = l2cap_disconnect_req(conn, cmd, data); - break; - - case L2CAP_DISCONN_RSP: - err = l2cap_disconnect_rsp(conn, cmd, data); - break; - - case L2CAP_ECHO_REQ: - l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data); - break; - - case L2CAP_ECHO_RSP: - break; - - case L2CAP_INFO_REQ: - err = l2cap_information_req(conn, cmd, data); - break; - - case L2CAP_INFO_RSP: - err = l2cap_information_rsp(conn, cmd, data); - break; - - case L2CAP_CREATE_CHAN_REQ: - err = l2cap_create_channel_req(conn, cmd, cmd_len, data); - break; - - case L2CAP_CREATE_CHAN_RSP: - err = l2cap_create_channel_rsp(conn, cmd, data); - break; - - case L2CAP_MOVE_CHAN_REQ: - err = l2cap_move_channel_req(conn, cmd, cmd_len, data); - break; - - case L2CAP_MOVE_CHAN_RSP: - err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data); - break; - - case L2CAP_MOVE_CHAN_CFM: - err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data); - break; - - case L2CAP_MOVE_CHAN_CFM_RSP: - err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); - break; - - default: - BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); - err = -EINVAL; - break; - } - - return err; -} - -static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, u8 *data) -{ - switch (cmd->code) { - case L2CAP_COMMAND_REJ: - return 0; - - case L2CAP_CONN_PARAM_UPDATE_REQ: - return l2cap_conn_param_update_req(conn, cmd, data); - - case L2CAP_CONN_PARAM_UPDATE_RSP: - return 0; - - default: - BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code); - return -EINVAL; - } -} - -static inline void l2cap_sig_channel(struct l2cap_conn *conn, - struct sk_buff *skb) -{ - u8 *data = skb->data; - int len = skb->len; - struct l2cap_cmd_hdr cmd; - int err; - - l2cap_raw_recv(conn, skb); - - while (len >= L2CAP_CMD_HDR_SIZE) { - u16 cmd_len; - memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); - data += L2CAP_CMD_HDR_SIZE; - len -= L2CAP_CMD_HDR_SIZE; - - cmd_len = le16_to_cpu(cmd.len); - - BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident); - - if (cmd_len > len || !cmd.ident) { - BT_DBG("corrupted command"); - break; - } - - if (conn->hcon->type == LE_LINK) - err = l2cap_le_sig_cmd(conn, &cmd, data); - else - err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data); - - if (err) { - struct l2cap_cmd_rej_unk rej; - - BT_ERR("Wrong link type (%d)", err); - - /* FIXME: Map err to a valid reason */ - rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); - l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); - } - - data += cmd_len; - len -= cmd_len; - } - - kfree_skb(skb); -} - -static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) -{ - u16 our_fcs, rcv_fcs; - int hdr_size; - - if (test_bit(FLAG_EXT_CTRL, &chan->flags)) - hdr_size = L2CAP_EXT_HDR_SIZE; - else - hdr_size = L2CAP_ENH_HDR_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) { - skb_trim(skb, skb->len - L2CAP_FCS_SIZE); - rcv_fcs = get_unaligned_le16(skb->data + skb->len); - our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); - - if (our_fcs != rcv_fcs) - return -EBADMSG; - } - return 0; -} - -static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) -{ - u32 control = 0; - - chan->frames_sent = 0; - - control |= __set_reqseq(chan, chan->buffer_seq); - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); - l2cap_send_sframe(chan, control); - set_bit(CONN_RNR_SENT, &chan->conn_state); - } - - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) - l2cap_retransmit_frames(chan); - - l2cap_ertm_send(chan); - - if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && - chan->frames_sent == 0) { - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, control); - } -} - -static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar) -{ - struct sk_buff *next_skb; - int tx_seq_offset, next_tx_seq_offset; - - bt_cb(skb)->tx_seq = tx_seq; - bt_cb(skb)->sar = sar; - - next_skb = skb_peek(&chan->srej_q); - - tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - - while (next_skb) { - if (bt_cb(next_skb)->tx_seq == tx_seq) - return -EINVAL; - - next_tx_seq_offset = __seq_offset(chan, - bt_cb(next_skb)->tx_seq, chan->buffer_seq); - - if (next_tx_seq_offset > tx_seq_offset) { - __skb_queue_before(&chan->srej_q, next_skb, skb); - return 0; - } - - if (skb_queue_is_last(&chan->srej_q, next_skb)) - next_skb = NULL; - else - next_skb = skb_queue_next(&chan->srej_q, next_skb); - } - - __skb_queue_tail(&chan->srej_q, skb); - - return 0; -} - -static void append_skb_frag(struct sk_buff *skb, - struct sk_buff *new_frag, struct sk_buff **last_frag) -{ - /* skb->len reflects data in skb as well as all fragments - * skb->data_len reflects only data in fragments - */ - if (!skb_has_frag_list(skb)) - skb_shinfo(skb)->frag_list = new_frag; - - new_frag->next = NULL; - - (*last_frag)->next = new_frag; - *last_frag = new_frag; - - skb->len += new_frag->len; - skb->data_len += new_frag->len; - skb->truesize += new_frag->truesize; -} - -static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control) -{ - int err = -EINVAL; - - switch (__get_ctrl_sar(chan, control)) { - case L2CAP_SAR_UNSEGMENTED: - if (chan->sdu) - break; - - err = chan->ops->recv(chan->data, skb); - break; - - case L2CAP_SAR_START: - if (chan->sdu) - break; - - chan->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, L2CAP_SDULEN_SIZE); - - if (chan->sdu_len > chan->imtu) { - err = -EMSGSIZE; - break; - } - - if (skb->len >= chan->sdu_len) - break; - - chan->sdu = skb; - chan->sdu_last_frag = skb; - - skb = NULL; - err = 0; - break; - - case L2CAP_SAR_CONTINUE: - if (!chan->sdu) - break; - - append_skb_frag(chan->sdu, skb, - &chan->sdu_last_frag); - skb = NULL; - - if (chan->sdu->len >= chan->sdu_len) - break; - - err = 0; - break; - - case L2CAP_SAR_END: - if (!chan->sdu) - break; - - append_skb_frag(chan->sdu, skb, - &chan->sdu_last_frag); - skb = NULL; - - if (chan->sdu->len != chan->sdu_len) - break; - - err = chan->ops->recv(chan->data, chan->sdu); - - if (!err) { - /* Reassembly complete */ - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - } - break; - } - - if (err) { - kfree_skb(skb); - kfree_skb(chan->sdu); - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - } - - return err; -} - -static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) -{ - BT_DBG("chan %p, Enter local busy", chan); - - set_bit(CONN_LOCAL_BUSY, &chan->conn_state); - - __set_ack_timer(chan); -} - -static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) -{ - u32 control; - - if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) - goto done; - - control = __set_reqseq(chan, chan->buffer_seq); - control |= __set_ctrl_poll(chan); - control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, control); - chan->retry_count = 1; - - __clear_retrans_timer(chan); - __set_monitor_timer(chan); - - set_bit(CONN_WAIT_F, &chan->conn_state); - -done: - clear_bit(CONN_LOCAL_BUSY, &chan->conn_state); - clear_bit(CONN_RNR_SENT, &chan->conn_state); - - BT_DBG("chan %p, Exit local busy", chan); -} - -void l2cap_chan_busy(struct l2cap_chan *chan, int busy) -{ - if (chan->mode == L2CAP_MODE_ERTM) { - if (busy) - l2cap_ertm_enter_local_busy(chan); - else - l2cap_ertm_exit_local_busy(chan); - } -} - -static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) -{ - struct sk_buff *skb; - u32 control; - - while ((skb = skb_peek(&chan->srej_q)) && - !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - int err; - - if (bt_cb(skb)->tx_seq != tx_seq) - break; - - skb = skb_dequeue(&chan->srej_q); - control = __set_ctrl_sar(chan, bt_cb(skb)->sar); - err = l2cap_reassemble_sdu(chan, skb, control); - - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - break; - } - - chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej); - tx_seq = __next_seq(chan, tx_seq); - } -} - -static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) -{ - struct srej_list *l, *tmp; - u32 control; - - list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { - if (l->tx_seq == tx_seq) { - list_del(&l->list); - kfree(l); - return; - } - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_reqseq(chan, l->tx_seq); - l2cap_send_sframe(chan, control); - list_del(&l->list); - list_add_tail(&l->list, &chan->srej_l); - } -} - -static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) -{ - struct srej_list *new; - u32 control; - - while (tx_seq != chan->expected_tx_seq) { - control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= __set_reqseq(chan, chan->expected_tx_seq); - l2cap_send_sframe(chan, control); - - new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); - if (!new) - return -ENOMEM; - - new->tx_seq = chan->expected_tx_seq; - - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - list_add_tail(&new->list, &chan->srej_l); - } - - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - return 0; -} - -static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) -{ - u16 tx_seq = __get_txseq(chan, rx_control); - u16 req_seq = __get_reqseq(chan, rx_control); - u8 sar = __get_ctrl_sar(chan, rx_control); - int tx_seq_offset, expected_tx_seq_offset; - int num_to_ack = (chan->tx_win/6) + 1; - int err = 0; - - BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len, - tx_seq, rx_control); - - if (__is_ctrl_final(chan, rx_control) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - __clear_monitor_timer(chan); - if (chan->unacked_frames > 0) - __set_retrans_timer(chan); - clear_bit(CONN_WAIT_F, &chan->conn_state); - } - - chan->expected_ack_seq = req_seq; - l2cap_drop_acked_frames(chan); - - tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - - /* invalid tx_seq */ - if (tx_seq_offset >= chan->tx_win) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) - l2cap_send_ack(chan); - goto drop; - } - - if (tx_seq == chan->expected_tx_seq) - goto expected; - - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - struct srej_list *first; - - first = list_first_entry(&chan->srej_l, - struct srej_list, list); - if (tx_seq == first->tx_seq) { - l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - l2cap_check_srej_gap(chan, tx_seq); - - list_del(&first->list); - kfree(first); - - if (list_empty(&chan->srej_l)) { - chan->buffer_seq = chan->buffer_seq_srej; - clear_bit(CONN_SREJ_SENT, &chan->conn_state); - l2cap_send_ack(chan); - BT_DBG("chan %p, Exit SREJ_SENT", chan); - } - } else { - struct srej_list *l; - - /* duplicated tx_seq */ - if (l2cap_add_to_srej_queue(chan, skb, tx_seq, sar) < 0) - goto drop; - - list_for_each_entry(l, &chan->srej_l, list) { - if (l->tx_seq == tx_seq) { - l2cap_resend_srejframe(chan, tx_seq); - return 0; - } - } - - err = l2cap_send_srejframe(chan, tx_seq); - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, -err); - return err; - } - } - } else { - expected_tx_seq_offset = __seq_offset(chan, - chan->expected_tx_seq, chan->buffer_seq); - - /* duplicated tx_seq */ - if (tx_seq_offset < expected_tx_seq_offset) - goto drop; - - set_bit(CONN_SREJ_SENT, &chan->conn_state); - - BT_DBG("chan %p, Enter SREJ", chan); - - INIT_LIST_HEAD(&chan->srej_l); - chan->buffer_seq_srej = chan->buffer_seq; - - __skb_queue_head_init(&chan->srej_q); - l2cap_add_to_srej_queue(chan, skb, tx_seq, sar); - - /* Set P-bit only if there are some I-frames to ack. */ - if (__clear_ack_timer(chan)) - set_bit(CONN_SEND_PBIT, &chan->conn_state); - - err = l2cap_send_srejframe(chan, tx_seq); - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, -err); - return err; - } - } - return 0; - -expected: - chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); - - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - bt_cb(skb)->tx_seq = tx_seq; - bt_cb(skb)->sar = sar; - __skb_queue_tail(&chan->srej_q, skb); - return 0; - } - - err = l2cap_reassemble_sdu(chan, skb, rx_control); - chan->buffer_seq = __next_seq(chan, chan->buffer_seq); - - if (err < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - return err; - } - - if (__is_ctrl_final(chan, rx_control)) { - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - } - - - chan->num_acked = (chan->num_acked + 1) % num_to_ack; - if (chan->num_acked == num_to_ack - 1) - l2cap_send_ack(chan); - else - __set_ack_timer(chan); - - return 0; - -drop: - kfree_skb(skb); - return 0; -} - -static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control) -{ - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, - __get_reqseq(chan, rx_control), rx_control); - - chan->expected_ack_seq = __get_reqseq(chan, rx_control); - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_poll(chan, rx_control)) { - set_bit(CONN_SEND_FBIT, &chan->conn_state); - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - (chan->unacked_frames > 0)) - __set_retrans_timer(chan); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - l2cap_send_srejtail(chan); - } else { - l2cap_send_i_or_rr_or_rnr(chan); - } - - } else if (__is_ctrl_final(chan, rx_control)) { - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - - } else { - if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && - (chan->unacked_frames > 0)) - __set_retrans_timer(chan); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) - l2cap_send_ack(chan); - else - l2cap_ertm_send(chan); - } -} - -static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_final(chan, rx_control)) { - if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) - l2cap_retransmit_frames(chan); - } else { - l2cap_retransmit_frames(chan); - - if (test_bit(CONN_WAIT_F, &chan->conn_state)) - set_bit(CONN_REJ_ACT, &chan->conn_state); - } -} -static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - - if (__is_ctrl_poll(chan, rx_control)) { - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - set_bit(CONN_SEND_FBIT, &chan->conn_state); - l2cap_retransmit_one_frame(chan, tx_seq); - - l2cap_ertm_send(chan); - - if (test_bit(CONN_WAIT_F, &chan->conn_state)) { - chan->srej_save_reqseq = tx_seq; - set_bit(CONN_SREJ_ACT, &chan->conn_state); - } - } else if (__is_ctrl_final(chan, rx_control)) { - if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && - chan->srej_save_reqseq == tx_seq) - clear_bit(CONN_SREJ_ACT, &chan->conn_state); - else - l2cap_retransmit_one_frame(chan, tx_seq); - } else { - l2cap_retransmit_one_frame(chan, tx_seq); - if (test_bit(CONN_WAIT_F, &chan->conn_state)) { - chan->srej_save_reqseq = tx_seq; - set_bit(CONN_SREJ_ACT, &chan->conn_state); - } - } -} - -static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control) -{ - u16 tx_seq = __get_reqseq(chan, rx_control); - - BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); - - set_bit(CONN_REMOTE_BUSY, &chan->conn_state); - chan->expected_ack_seq = tx_seq; - l2cap_drop_acked_frames(chan); - - if (__is_ctrl_poll(chan, rx_control)) - set_bit(CONN_SEND_FBIT, &chan->conn_state); - - if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { - __clear_retrans_timer(chan); - if (__is_ctrl_poll(chan, rx_control)) - l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); - return; - } - - if (__is_ctrl_poll(chan, rx_control)) { - l2cap_send_srejtail(chan); - } else { - rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR); - l2cap_send_sframe(chan, rx_control); - } -} - -static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) -{ - BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len); - - if (__is_ctrl_final(chan, rx_control) && - test_bit(CONN_WAIT_F, &chan->conn_state)) { - __clear_monitor_timer(chan); - if (chan->unacked_frames > 0) - __set_retrans_timer(chan); - clear_bit(CONN_WAIT_F, &chan->conn_state); - } - - switch (__get_ctrl_super(chan, rx_control)) { - case L2CAP_SUPER_RR: - l2cap_data_channel_rrframe(chan, rx_control); - break; - - case L2CAP_SUPER_REJ: - l2cap_data_channel_rejframe(chan, rx_control); - break; - - case L2CAP_SUPER_SREJ: - l2cap_data_channel_srejframe(chan, rx_control); - break; - - case L2CAP_SUPER_RNR: - l2cap_data_channel_rnrframe(chan, rx_control); - break; - } - - kfree_skb(skb); - return 0; -} - -static int l2cap_ertm_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) -{ - u32 control; - u16 req_seq; - int len, next_tx_seq_offset, req_seq_offset; - - control = __get_control(chan, skb->data); - skb_pull(skb, __ctrl_size(chan)); - len = skb->len; - - /* - * We can just drop the corrupted I-frame here. - * Receiver will miss it and start proper recovery - * procedures and ask retransmission. - */ - if (l2cap_check_fcs(chan, skb)) - goto drop; - - if (__is_sar_start(chan, control) && !__is_sframe(chan, control)) - len -= L2CAP_SDULEN_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - len -= L2CAP_FCS_SIZE; - - if (len > chan->mps) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - req_seq = __get_reqseq(chan, control); - - req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq); - - next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq, - chan->expected_ack_seq); - - /* check for invalid req-seq */ - if (req_seq_offset > next_tx_seq_offset) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - if (!__is_sframe(chan, control)) { - if (len < 0) { - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - l2cap_data_channel_iframe(chan, control, skb); - } else { - if (len != 0) { - BT_ERR("%d", len); - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - goto drop; - } - - l2cap_data_channel_sframe(chan, control, skb); - } - - return 0; - -drop: - kfree_skb(skb); - return 0; -} - -static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb) -{ - struct l2cap_chan *chan; - u32 control; - u16 tx_seq; - int len; - - chan = l2cap_get_chan_by_scid(conn, cid); - if (!chan) { - BT_DBG("unknown cid 0x%4.4x", cid); - /* Drop packet and return */ - kfree_skb(skb); - return 0; - } - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->state != BT_CONNECTED) - goto drop; - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - /* If socket recv buffers overflows we drop data here - * which is *bad* because L2CAP has to be reliable. - * But we don't have any other choice. L2CAP doesn't - * provide flow control mechanism. */ - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan->data, skb)) - goto done; - break; - - case L2CAP_MODE_ERTM: - l2cap_ertm_data_rcv(chan, skb); - - goto done; - - case L2CAP_MODE_STREAMING: - control = __get_control(chan, skb->data); - skb_pull(skb, __ctrl_size(chan)); - len = skb->len; - - if (l2cap_check_fcs(chan, skb)) - goto drop; - - if (__is_sar_start(chan, control)) - len -= L2CAP_SDULEN_SIZE; - - if (chan->fcs == L2CAP_FCS_CRC16) - len -= L2CAP_FCS_SIZE; - - if (len > chan->mps || len < 0 || __is_sframe(chan, control)) - goto drop; - - tx_seq = __get_txseq(chan, control); - - if (chan->expected_tx_seq != tx_seq) { - /* Frame(s) missing - must discard partial SDU */ - kfree_skb(chan->sdu); - chan->sdu = NULL; - chan->sdu_last_frag = NULL; - chan->sdu_len = 0; - - /* TODO: Notify userland of missing data */ - } - - chan->expected_tx_seq = __next_seq(chan, tx_seq); - - if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE) - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - - goto done; - - default: - BT_DBG("chan %p: bad mode 0x%2.2x", chan, chan->mode); - break; - } - -drop: - kfree_skb(skb); - -done: - l2cap_chan_unlock(chan); - - return 0; -} - -static inline int l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb) -{ - struct l2cap_chan *chan; - - chan = l2cap_global_chan_by_psm(0, psm, conn->src); - if (!chan) - goto drop; - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) - goto drop; - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan->data, skb)) - return 0; - -drop: - kfree_skb(skb); - - return 0; -} - -static inline int l2cap_att_channel(struct l2cap_conn *conn, __le16 cid, struct sk_buff *skb) -{ - struct l2cap_chan *chan; - - chan = l2cap_global_chan_by_scid(0, cid, conn->src); - if (!chan) - goto drop; - - BT_DBG("chan %p, len %d", chan, skb->len); - - if (chan->state != BT_BOUND && chan->state != BT_CONNECTED) - goto drop; - - if (chan->imtu < skb->len) - goto drop; - - if (!chan->ops->recv(chan->data, skb)) - return 0; - -drop: - kfree_skb(skb); - - return 0; -} - -static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct l2cap_hdr *lh = (void *) skb->data; - u16 cid, len; - __le16 psm; - - skb_pull(skb, L2CAP_HDR_SIZE); - cid = __le16_to_cpu(lh->cid); - len = __le16_to_cpu(lh->len); - - if (len != skb->len) { - kfree_skb(skb); - return; - } - - BT_DBG("len %d, cid 0x%4.4x", len, cid); - - switch (cid) { - case L2CAP_CID_LE_SIGNALING: - case L2CAP_CID_SIGNALING: - l2cap_sig_channel(conn, skb); - break; - - case L2CAP_CID_CONN_LESS: - psm = get_unaligned_le16(skb->data); - skb_pull(skb, 2); - l2cap_conless_channel(conn, psm, skb); - break; - - case L2CAP_CID_LE_DATA: - l2cap_att_channel(conn, cid, skb); - break; - - case L2CAP_CID_SMP: - if (smp_sig_channel(conn, skb)) - l2cap_conn_del(conn->hcon, EACCES); - break; - - default: - l2cap_data_channel(conn, cid, skb); - break; - } -} - -/* ---- L2CAP interface with lower layer (HCI) ---- */ - -int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - int exact = 0, lm1 = 0, lm2 = 0; - struct l2cap_chan *c; - - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); - - /* Find listening sockets and check their link_mode */ - read_lock(&chan_list_lock); - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - if (c->state != BT_LISTEN) - continue; - - if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { - lm1 |= HCI_LM_ACCEPT; - if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) - lm1 |= HCI_LM_MASTER; - exact++; - } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { - lm2 |= HCI_LM_ACCEPT; - if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) - lm2 |= HCI_LM_MASTER; - } - } - read_unlock(&chan_list_lock); - - return exact ? lm1 : lm2; -} - -int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) -{ - struct l2cap_conn *conn; - - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); - - if (!status) { - conn = l2cap_conn_add(hcon, status); - if (conn) - l2cap_conn_ready(conn); - } else - l2cap_conn_del(hcon, bt_to_errno(status)); - - return 0; -} - -int l2cap_disconn_ind(struct hci_conn *hcon) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - - BT_DBG("hcon %p", hcon); - - if (!conn) - return HCI_ERROR_REMOTE_USER_TERM; - return conn->disc_reason; -} - -int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) -{ - BT_DBG("hcon %p reason %d", hcon, reason); - - l2cap_conn_del(hcon, bt_to_errno(reason)); - return 0; -} - -static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt) -{ - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) - return; - - if (encrypt == 0x00) { - if (chan->sec_level == BT_SECURITY_MEDIUM) { - __clear_chan_timer(chan); - __set_chan_timer(chan, L2CAP_ENC_TIMEOUT); - } else if (chan->sec_level == BT_SECURITY_HIGH) - l2cap_chan_close(chan, ECONNREFUSED); - } else { - if (chan->sec_level == BT_SECURITY_MEDIUM) - __clear_chan_timer(chan); - } -} - -int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - struct l2cap_chan *chan; - - if (!conn) - return 0; - - BT_DBG("conn %p", conn); - - if (hcon->type == LE_LINK) { - smp_distribute_keys(conn, 0); - cancel_delayed_work(&conn->security_timer); - } - - mutex_lock(&conn->chan_lock); - - list_for_each_entry(chan, &conn->chan_l, list) { - l2cap_chan_lock(chan); - - BT_DBG("chan->scid %d", chan->scid); - - if (chan->scid == L2CAP_CID_LE_DATA) { - if (!status && encrypt) { - chan->sec_level = hcon->sec_level; - l2cap_chan_ready(chan); - } - - l2cap_chan_unlock(chan); - continue; - } - - if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { - l2cap_chan_unlock(chan); - continue; - } - - if (!status && (chan->state == BT_CONNECTED || - chan->state == BT_CONFIG)) { - struct sock *sk = chan->sk; - - bt_sk(sk)->suspended = false; - sk->sk_state_change(sk); - - l2cap_check_encryption(chan, encrypt); - l2cap_chan_unlock(chan); - continue; - } - - if (chan->state == BT_CONNECT) { - if (!status) { - l2cap_send_conn_req(chan); - } else { - __clear_chan_timer(chan); - __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); - } - } else if (chan->state == BT_CONNECT2) { - struct sock *sk = chan->sk; - struct l2cap_conn_rsp rsp; - __u16 res, stat; - - lock_sock(sk); - - if (!status) { - if (bt_sk(sk)->defer_setup) { - struct sock *parent = bt_sk(sk)->parent; - res = L2CAP_CR_PEND; - stat = L2CAP_CS_AUTHOR_PEND; - if (parent) - parent->sk_data_ready(parent, 0); - } else { - __l2cap_state_change(chan, BT_CONFIG); - res = L2CAP_CR_SUCCESS; - stat = L2CAP_CS_NO_INFO; - } - } else { - __l2cap_state_change(chan, BT_DISCONN); - __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); - res = L2CAP_CR_SEC_BLOCK; - stat = L2CAP_CS_NO_INFO; - } - - release_sock(sk); - - rsp.scid = cpu_to_le16(chan->dcid); - rsp.dcid = cpu_to_le16(chan->scid); - rsp.result = cpu_to_le16(res); - rsp.status = cpu_to_le16(stat); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, - sizeof(rsp), &rsp); - } - - l2cap_chan_unlock(chan); - } - - mutex_unlock(&conn->chan_lock); - - return 0; -} - -int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) -{ - struct l2cap_conn *conn = hcon->l2cap_data; - - if (!conn) - conn = l2cap_conn_add(hcon, 0); - - if (!conn) - goto drop; - - BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); - - if (!(flags & ACL_CONT)) { - struct l2cap_hdr *hdr; - int len; - - if (conn->rx_len) { - BT_ERR("Unexpected start frame (len %d)", skb->len); - kfree_skb(conn->rx_skb); - conn->rx_skb = NULL; - conn->rx_len = 0; - l2cap_conn_unreliable(conn, ECOMM); - } - - /* Start fragment always begin with Basic L2CAP header */ - if (skb->len < L2CAP_HDR_SIZE) { - BT_ERR("Frame is too short (len %d)", skb->len); - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - hdr = (struct l2cap_hdr *) skb->data; - len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE; - - if (len == skb->len) { - /* Complete frame received */ - l2cap_recv_frame(conn, skb); - return 0; - } - - BT_DBG("Start: total len %d, frag len %d", len, skb->len); - - if (skb->len > len) { - BT_ERR("Frame is too long (len %d, expected len %d)", - skb->len, len); - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - /* Allocate skb for the complete frame (with header) */ - conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!conn->rx_skb) - goto drop; - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len = len - skb->len; - } else { - BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); - - if (!conn->rx_len) { - BT_ERR("Unexpected continuation frame (len %d)", skb->len); - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - if (skb->len > conn->rx_len) { - BT_ERR("Fragment is too long (len %d, expected %d)", - skb->len, conn->rx_len); - kfree_skb(conn->rx_skb); - conn->rx_skb = NULL; - conn->rx_len = 0; - l2cap_conn_unreliable(conn, ECOMM); - goto drop; - } - - skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len), - skb->len); - conn->rx_len -= skb->len; - - if (!conn->rx_len) { - /* Complete frame received */ - l2cap_recv_frame(conn, conn->rx_skb); - conn->rx_skb = NULL; - } - } - -drop: - kfree_skb(skb); - return 0; -} - -static int l2cap_debugfs_show(struct seq_file *f, void *p) -{ - struct l2cap_chan *c; - - read_lock(&chan_list_lock); - - list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - - seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - c->state, __le16_to_cpu(c->psm), - c->scid, c->dcid, c->imtu, c->omtu, - c->sec_level, c->mode); - } - - read_unlock(&chan_list_lock); - - return 0; -} - -static int l2cap_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, l2cap_debugfs_show, inode->i_private); -} - -static const struct file_operations l2cap_debugfs_fops = { - .open = l2cap_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *l2cap_debugfs; - -int __init l2cap_init(void) -{ - int err; - - err = l2cap_init_sockets(); - if (err < 0) - return err; - - if (bt_debugfs) { - l2cap_debugfs = debugfs_create_file("l2cap", 0444, - bt_debugfs, NULL, &l2cap_debugfs_fops); - if (!l2cap_debugfs) - BT_ERR("Failed to create L2CAP debug file"); - } - - return 0; -} - -void l2cap_exit(void) -{ - debugfs_remove(l2cap_debugfs); - l2cap_cleanup_sockets(); -} - -module_param(disable_ertm, bool, 0644); -MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); diff --git a/net/bluetooth_tizen/l2cap_sock.c b/net/bluetooth_tizen/l2cap_sock.c deleted file mode 100644 index 2cae7af..0000000 --- a/net/bluetooth_tizen/l2cap_sock.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> - Copyright (C) 2010 Google Inc. - Copyright (C) 2011 ProFUSION Embedded Systems - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth L2CAP sockets. */ - -#include <linux/security.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/smp.h> - -static const struct proto_ops l2cap_sock_ops; -static void l2cap_sock_init(struct sock *sk, struct sock *parent); -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio); - -static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid && la.l2_psm) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - if (la.l2_psm) { - __u16 psm = __le16_to_cpu(la.l2_psm); - - /* PSM must be odd and lsb of upper byte must be 0 */ - if ((psm & 0x0101) != 0x0001) { - err = -EINVAL; - goto done; - } - - /* Restrict usage of well-known PSMs */ - if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) { - err = -EACCES; - goto done; - } - } - - if (la.l2_cid) - err = l2cap_add_scid(chan, __le16_to_cpu(la.l2_cid)); - else - err = l2cap_add_psm(chan, &la.l2_bdaddr, la.l2_psm); - - if (err < 0) - goto done; - - if (__le16_to_cpu(la.l2_psm) == 0x0001 || - __le16_to_cpu(la.l2_psm) == 0x0003) - chan->sec_level = BT_SECURITY_SDP; - - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); - - chan->state = BT_BOUND; - sk->sk_state = BT_BOUND; - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct sockaddr_l2 la; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (!addr || alen < sizeof(addr->sa_family) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - memset(&la, 0, sizeof(la)); - len = min_t(unsigned int, sizeof(la), alen); - memcpy(&la, addr, len); - - if (la.l2_cid && la.l2_psm) - return -EINVAL; - - err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), - &la.l2_bdaddr); - if (err) - return err; - - lock_sock(sk); - - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); - - release_sock(sk); - - return err; -} - -static int l2cap_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) - || sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - switch (chan->mode) { - case L2CAP_MODE_BASIC: - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -ENOTSUPP; - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - - chan->state = BT_LISTEN; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *nsk; - long timeo; - int err = 0; - - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - nsk = bt_accept_dequeue(sk, newsock); - if (nsk) - break; - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", nsk); - -done: - release_sock(sk); - return err; -} - -static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - - BT_DBG("sock %p, sk %p", sock, sk); - - addr->sa_family = AF_BLUETOOTH; - *len = sizeof(struct sockaddr_l2); - - if (peer) { - la->l2_psm = chan->psm; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); - la->l2_cid = cpu_to_le16(chan->dcid); - } else { - la->l2_psm = chan->sport; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); - la->l2_cid = cpu_to_le16(chan->scid); - } - - return 0; -} - -static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct l2cap_options opts; - struct l2cap_conninfo cinfo; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - memset(&opts, 0, sizeof(opts)); - opts.imtu = chan->imtu; - opts.omtu = chan->omtu; - opts.flush_to = chan->flush_to; - opts.mode = chan->mode; - opts.fcs = chan->fcs; - opts.max_tx = chan->max_tx; - opts.txwin_size = chan->tx_win; - - len = min_t(unsigned int, len, sizeof(opts)); - if (copy_to_user(optval, (char *) &opts, len)) - err = -EFAULT; - - break; - - case L2CAP_LM: - switch (chan->sec_level) { - case BT_SECURITY_LOW: - opt = L2CAP_LM_AUTH; - break; - case BT_SECURITY_MEDIUM: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT; - break; - case BT_SECURITY_HIGH: - opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT | - L2CAP_LM_SECURE; - break; - default: - opt = 0; - break; - } - - if (test_bit(FLAG_ROLE_SWITCH, &chan->flags)) - opt |= L2CAP_LM_MASTER; - - if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) - opt |= L2CAP_LM_RELIABLE; - - if (put_user(opt, (u32 __user *) optval)) - err = -EFAULT; - break; - - case L2CAP_CONNINFO: - if (sk->sk_state != BT_CONNECTED && - !(sk->sk_state == BT_CONNECT2 && - bt_sk(sk)->defer_setup)) { - err = -ENOTCONN; - break; - } - - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = chan->conn->hcon->handle; - memcpy(cinfo.dev_class, chan->conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct bt_security sec; - struct bt_power pwr; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_getsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - break; - } - - memset(&sec, 0, sizeof(sec)); - if (chan->conn) - sec.level = chan->conn->hcon->sec_level; - else - sec.level = chan->sec_level; - - if (sk->sk_state == BT_CONNECTED) - sec.key_size = chan->conn->hcon->enc_key_size; - - len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) - err = -EFAULT; - - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) - err = -EFAULT; - - break; - - case BT_FLUSHABLE: - if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), - (u32 __user *) optval)) - err = -EFAULT; - - break; - - case BT_POWER: - if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM - && sk->sk_type != SOCK_RAW) { - err = -EINVAL; - break; - } - - pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); - - len = min_t(unsigned int, len, sizeof(pwr)); - if (copy_to_user(optval, (char *) &pwr, len)) - err = -EFAULT; - - break; - - case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - - if (put_user(chan->chan_policy, (u32 __user *) optval)) - err = -EFAULT; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct l2cap_options opts; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - case L2CAP_OPTIONS: - if (sk->sk_state == BT_CONNECTED) { - err = -EINVAL; - break; - } - - opts.imtu = chan->imtu; - opts.omtu = chan->omtu; - opts.flush_to = chan->flush_to; - opts.mode = chan->mode; - opts.fcs = chan->fcs; - opts.max_tx = chan->max_tx; - opts.txwin_size = chan->tx_win; - - len = min_t(unsigned int, sizeof(opts), optlen); - if (copy_from_user((char *) &opts, optval, len)) { - err = -EFAULT; - break; - } - - if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) { - err = -EINVAL; - break; - } - - chan->mode = opts.mode; - switch (chan->mode) { - case L2CAP_MODE_BASIC: - clear_bit(CONF_STATE2_DEVICE, &chan->conf_state); - break; - case L2CAP_MODE_ERTM: - case L2CAP_MODE_STREAMING: - if (!disable_ertm) - break; - /* fall through */ - default: - err = -EINVAL; - break; - } - - chan->imtu = opts.imtu; - chan->omtu = opts.omtu; - chan->fcs = opts.fcs; - chan->max_tx = opts.max_tx; - chan->tx_win = opts.txwin_size; - break; - - case L2CAP_LM: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt & L2CAP_LM_AUTH) - chan->sec_level = BT_SECURITY_LOW; - if (opt & L2CAP_LM_ENCRYPT) - chan->sec_level = BT_SECURITY_MEDIUM; - if (opt & L2CAP_LM_SECURE) - chan->sec_level = BT_SECURITY_HIGH; - - if (opt & L2CAP_LM_MASTER) - set_bit(FLAG_ROLE_SWITCH, &chan->flags); - else - clear_bit(FLAG_ROLE_SWITCH, &chan->flags); - - if (opt & L2CAP_LM_RELIABLE) - set_bit(FLAG_FORCE_RELIABLE, &chan->flags); - else - clear_bit(FLAG_FORCE_RELIABLE, &chan->flags); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - struct bt_security sec; - struct bt_power pwr; - struct l2cap_conn *conn; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (level == SOL_L2CAP) - return l2cap_sock_setsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - break; - } - - sec.level = BT_SECURITY_LOW; - - len = min_t(unsigned int, sizeof(sec), optlen); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; - } - - if (sec.level < BT_SECURITY_LOW || - sec.level > BT_SECURITY_HIGH) { - err = -EINVAL; - break; - } - - chan->sec_level = sec.level; - - if (!chan->conn) - break; - - conn = chan->conn; - - /*change security for LE channels */ - if (chan->scid == L2CAP_CID_LE_DATA) { - if (!conn->hcon->out) { - err = -EINVAL; - break; - } - - if (smp_conn_security(conn, sec.level)) - break; - sk->sk_state = BT_CONFIG; - chan->state = BT_CONFIG; - - /* or for ACL link */ - } else if ((sk->sk_state == BT_CONNECT2 && - bt_sk(sk)->defer_setup) || - sk->sk_state == BT_CONNECTED) { - if (!l2cap_chan_check_security(chan)) - bt_sk(sk)->suspended = true; - else - sk->sk_state_change(sk); - } - else { - err = -EINVAL; - } - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - bt_sk(sk)->defer_setup = opt; - break; - - case BT_FLUSHABLE: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt > BT_FLUSHABLE_ON) { - err = -EINVAL; - break; - } - - if (opt == BT_FLUSHABLE_OFF) { - struct l2cap_conn *conn = chan->conn; - /* proceed further only when we have l2cap_conn and - No Flush support in the LM */ - if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { - err = -EINVAL; - break; - } - } - - if (opt) - set_bit(FLAG_FLUSHABLE, &chan->flags); - else - clear_bit(FLAG_FLUSHABLE, &chan->flags); - break; - - case BT_POWER: - if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED && - chan->chan_type != L2CAP_CHAN_RAW) { - err = -EINVAL; - break; - } - - pwr.force_active = BT_POWER_FORCE_ACTIVE_ON; - - len = min_t(unsigned int, sizeof(pwr), optlen); - if (copy_from_user((char *) &pwr, optval, len)) { - err = -EFAULT; - break; - } - - if (pwr.force_active) - set_bit(FLAG_FORCE_ACTIVE, &chan->flags); - else - clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); - break; - - case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) { - err = -EINVAL; - break; - } - - if (chan->mode != L2CAP_MODE_ERTM && - chan->mode != L2CAP_MODE_STREAMING) { - err = -EOPNOTSUPP; - break; - } - - chan->chan_policy = (u8) opt; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - err = sock_error(sk); - if (err) - return err; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - l2cap_chan_lock(chan); - - if (sk->sk_state != BT_CONNECTED) { - l2cap_chan_unlock(chan); - return -ENOTCONN; - } - - err = l2cap_chan_send(chan, msg, len, sk->sk_priority); - l2cap_chan_unlock(chan); - - return err; -} - -static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags) -{ - struct sock *sk = sock->sk; - struct l2cap_pinfo *pi = l2cap_pi(sk); - int err; - - lock_sock(sk); - - if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) { - sk->sk_state = BT_CONFIG; - pi->chan->state = BT_CONFIG; - - __l2cap_connect_rsp_defer(pi->chan); - release_sock(sk); - return 0; - } - - release_sock(sk); - - if (sock->type == SOCK_STREAM) - err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags); - else - err = bt_sock_recvmsg(iocb, sock, msg, len, flags); - - if (pi->chan->mode != L2CAP_MODE_ERTM) - return err; - - /* Attempt to put pending rx data in the socket buffer */ - - lock_sock(sk); - - if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state)) - goto done; - - if (pi->rx_busy_skb) { - if (!sock_queue_rcv_skb(sk, pi->rx_busy_skb)) - pi->rx_busy_skb = NULL; - else - goto done; - } - - /* Restore data flow when half of the receive buffer is - * available. This avoids resending large numbers of - * frames. - */ - if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1) - l2cap_chan_busy(pi->chan, 0); - -done: - release_sock(sk); - return err; -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -static void l2cap_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket || - sock_flag(sk, SOCK_DEAD)) - return; - - BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state)); - - /* Kill poor orphan */ - - l2cap_chan_destroy(l2cap_pi(sk)->chan); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -static int l2cap_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - struct l2cap_chan *chan; - struct l2cap_conn *conn; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - chan = l2cap_pi(sk)->chan; - conn = chan->conn; - - if (conn) - mutex_lock(&conn->chan_lock); - - l2cap_chan_lock(chan); - lock_sock(sk); - - if (!sk->sk_shutdown) { - if (chan->mode == L2CAP_MODE_ERTM) - err = __l2cap_wait_ack(sk); - - sk->sk_shutdown = SHUTDOWN_MASK; - - release_sock(sk); - l2cap_chan_close(chan, 0); - lock_sock(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); - } - - if (!err && sk->sk_err) - err = -sk->sk_err; - - release_sock(sk); - l2cap_chan_unlock(chan); - - if (conn) - mutex_unlock(&conn->chan_lock); - - return err; -} - -static int l2cap_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - err = l2cap_sock_shutdown(sock, 2); - - sock_orphan(sk); - l2cap_sock_kill(sk); - return err; -} - -static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data) -{ - struct sock *sk, *parent = data; - - sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, - GFP_ATOMIC); - if (!sk) - return NULL; - - bt_sock_reclassify_lock(sk, BTPROTO_L2CAP); - - l2cap_sock_init(sk, parent); - - return l2cap_pi(sk)->chan; -} - -static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb) -{ - int err; - struct sock *sk = data; - struct l2cap_pinfo *pi = l2cap_pi(sk); - - lock_sock(sk); - - if (pi->rx_busy_skb) { - err = -ENOMEM; - goto done; - } - - err = sock_queue_rcv_skb(sk, skb); - - /* For ERTM, handle one skb that doesn't fit into the recv - * buffer. This is important to do because the data frames - * have already been acked, so the skb cannot be discarded. - * - * Notify the l2cap core that the buffer is full, so the - * LOCAL_BUSY state is entered and no more frames are - * acked and reassembled until there is buffer space - * available. - */ - if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) { - pi->rx_busy_skb = skb; - l2cap_chan_busy(pi->chan, 1); - err = 0; - } - -done: - release_sock(sk); - - return err; -} - -static void l2cap_sock_close_cb(void *data) -{ - struct sock *sk = data; - - l2cap_sock_kill(sk); -} - -static void l2cap_sock_state_change_cb(void *data, int state) -{ - struct sock *sk = data; - - sk->sk_state = state; -} - -static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, - unsigned long len, int nb, - int *err) -{ - struct sk_buff *skb; - - l2cap_chan_unlock(chan); - - skb = bt_skb_send_alloc(chan->sk, len, nb, err); - - l2cap_chan_lock(chan); - - return skb; -} - -static struct l2cap_ops l2cap_chan_ops = { - .name = "L2CAP Socket Interface", - .new_connection = l2cap_sock_new_connection_cb, - .recv = l2cap_sock_recv_cb, - .close = l2cap_sock_close_cb, - .state_change = l2cap_sock_state_change_cb, - .alloc_skb = l2cap_sock_alloc_skb_cb, -}; - -static void l2cap_sock_destruct(struct sock *sk) -{ - BT_DBG("sk %p", sk); - - if (l2cap_pi(sk)->rx_busy_skb) { - kfree_skb(l2cap_pi(sk)->rx_busy_skb); - l2cap_pi(sk)->rx_busy_skb = NULL; - } - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); -} - -static void l2cap_sock_init(struct sock *sk, struct sock *parent) -{ - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct l2cap_chan *chan = pi->chan; - - BT_DBG("sk %p", sk); - - if (parent) { - struct l2cap_chan *pchan = l2cap_pi(parent)->chan; - - sk->sk_type = parent->sk_type; - bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup; - - chan->chan_type = pchan->chan_type; - chan->imtu = pchan->imtu; - chan->omtu = pchan->omtu; - chan->conf_state = pchan->conf_state; - chan->mode = pchan->mode; - chan->fcs = pchan->fcs; - chan->max_tx = pchan->max_tx; - chan->tx_win = pchan->tx_win; - chan->tx_win_max = pchan->tx_win_max; - chan->sec_level = pchan->sec_level; - chan->flags = pchan->flags; - - security_sk_clone(parent, sk); - } else { - - switch (sk->sk_type) { - case SOCK_RAW: - chan->chan_type = L2CAP_CHAN_RAW; - break; - case SOCK_DGRAM: - chan->chan_type = L2CAP_CHAN_CONN_LESS; - break; - case SOCK_SEQPACKET: - case SOCK_STREAM: - chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; - break; - } - - chan->imtu = L2CAP_DEFAULT_MTU; - chan->omtu = 0; - if (!disable_ertm && sk->sk_type == SOCK_STREAM) { - chan->mode = L2CAP_MODE_ERTM; - set_bit(CONF_STATE2_DEVICE, &chan->conf_state); - } else { - chan->mode = L2CAP_MODE_BASIC; - } - chan->max_tx = L2CAP_DEFAULT_MAX_TX; - chan->fcs = L2CAP_FCS_CRC16; - chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; - chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; - chan->sec_level = BT_SECURITY_LOW; - chan->flags = 0; - set_bit(FLAG_FORCE_ACTIVE, &chan->flags); - } - - /* Default config options */ - chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; - - chan->data = sk; - chan->ops = &l2cap_chan_ops; -} - -static struct proto l2cap_proto = { - .name = "L2CAP", - .owner = THIS_MODULE, - .obj_size = sizeof(struct l2cap_pinfo) -}; - -static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct sock *sk; - struct l2cap_chan *chan; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - chan = l2cap_chan_create(sk); - if (!chan) { - l2cap_sock_kill(sk); - return NULL; - } - - l2cap_pi(sk)->chan = chan; - - return sk; -} - -static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM && - sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW)) - return -EPERM; - - sock->ops = &l2cap_sock_ops; - - sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - l2cap_sock_init(sk, NULL); - return 0; -} - -static const struct proto_ops l2cap_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = l2cap_sock_release, - .bind = l2cap_sock_bind, - .connect = l2cap_sock_connect, - .listen = l2cap_sock_listen, - .accept = l2cap_sock_accept, - .getname = l2cap_sock_getname, - .sendmsg = l2cap_sock_sendmsg, - .recvmsg = l2cap_sock_recvmsg, - .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, - .mmap = sock_no_mmap, - .socketpair = sock_no_socketpair, - .shutdown = l2cap_sock_shutdown, - .setsockopt = l2cap_sock_setsockopt, - .getsockopt = l2cap_sock_getsockopt -}; - -static const struct net_proto_family l2cap_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = l2cap_sock_create, -}; - -int __init l2cap_init_sockets(void) -{ - int err; - - err = proto_register(&l2cap_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops); - if (err < 0) - goto error; - - BT_INFO("L2CAP socket layer initialized"); - - return 0; - -error: - BT_ERR("L2CAP socket registration failed"); - proto_unregister(&l2cap_proto); - return err; -} - -void l2cap_cleanup_sockets(void) -{ - if (bt_sock_unregister(BTPROTO_L2CAP) < 0) - BT_ERR("L2CAP socket unregistration failed"); - - proto_unregister(&l2cap_proto); -} diff --git a/net/bluetooth_tizen/lib.c b/net/bluetooth_tizen/lib.c deleted file mode 100644 index 5066288..0000000 --- a/net/bluetooth_tizen/lib.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth kernel library. */ - -#define pr_fmt(fmt) "Bluetooth: " fmt - -#include <linux/module.h> - -#include <linux/kernel.h> -#include <linux/stddef.h> -#include <linux/string.h> -#include <asm/errno.h> - -#include <net/bluetooth/bluetooth.h> - -void baswap(bdaddr_t *dst, bdaddr_t *src) -{ - unsigned char *d = (unsigned char *) dst; - unsigned char *s = (unsigned char *) src; - unsigned int i; - - for (i = 0; i < 6; i++) - d[i] = s[5 - i]; -} -EXPORT_SYMBOL(baswap); - -char *batostr(bdaddr_t *ba) -{ - static char str[2][18]; - static int i = 1; - - i ^= 1; - sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", - ba->b[5], ba->b[4], ba->b[3], - ba->b[2], ba->b[1], ba->b[0]); - - return str[i]; -} -EXPORT_SYMBOL(batostr); - -/* Bluetooth error codes to Unix errno mapping */ -int bt_to_errno(__u16 code) -{ - switch (code) { - case 0: - return 0; - - case 0x01: - return EBADRQC; - - case 0x02: - return ENOTCONN; - - case 0x03: - return EIO; - - case 0x04: - return EHOSTDOWN; - - case 0x05: - return EACCES; - - case 0x06: - return EBADE; - - case 0x07: - return ENOMEM; - - case 0x08: - return ETIMEDOUT; - - case 0x09: - return EMLINK; - - case 0x0a: - return EMLINK; - - case 0x0b: - return EALREADY; - - case 0x0c: - return EBUSY; - - case 0x0d: - case 0x0e: - case 0x0f: - return ECONNREFUSED; - - case 0x10: - return ETIMEDOUT; - - case 0x11: - case 0x27: - case 0x29: - case 0x20: - return EOPNOTSUPP; - - case 0x12: - return EINVAL; - - case 0x13: - case 0x14: - case 0x15: - return ECONNRESET; - - case 0x16: - return ECONNABORTED; - - case 0x17: - return ELOOP; - - case 0x18: - return EACCES; - - case 0x1a: - return EPROTONOSUPPORT; - - case 0x1b: - return ECONNREFUSED; - - case 0x19: - case 0x1e: - case 0x23: - case 0x24: - case 0x25: - return EPROTO; - - default: - return ENOSYS; - } -} -EXPORT_SYMBOL(bt_to_errno); - -int bt_info(const char *format, ...) -{ - struct va_format vaf; - va_list args; - int r; - - va_start(args, format); - - vaf.fmt = format; - vaf.va = &args; - - r = pr_info("%pV", &vaf); - - va_end(args); - - return r; -} -EXPORT_SYMBOL(bt_info); - -int bt_err(const char *format, ...) -{ - struct va_format vaf; - va_list args; - int r; - - va_start(args, format); - - vaf.fmt = format; - vaf.va = &args; - - r = pr_err("%pV", &vaf); - - va_end(args); - - return r; -} -EXPORT_SYMBOL(bt_err); diff --git a/net/bluetooth_tizen/mgmt.c b/net/bluetooth_tizen/mgmt.c deleted file mode 100644 index 4bb03b1..0000000 --- a/net/bluetooth_tizen/mgmt.c +++ /dev/null @@ -1,3599 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - - Copyright (C) 2010 Nokia Corporation - Copyright (C) 2011-2012 Intel Corporation - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth HCI Management interface */ - -#include <linux/kernel.h> -#include <linux/uaccess.h> -#include <linux/module.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/mgmt.h> -#include <net/bluetooth/smp.h> - -bool enable_hs; -bool enable_le; - -#define MGMT_VERSION 1 -#define MGMT_REVISION 0 - -static const u16 mgmt_commands[] = { - MGMT_OP_READ_INDEX_LIST, - MGMT_OP_READ_INFO, - MGMT_OP_SET_POWERED, - MGMT_OP_SET_DISCOVERABLE, - MGMT_OP_SET_CONNECTABLE, - MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_OP_SET_PAIRABLE, - MGMT_OP_SET_LINK_SECURITY, - MGMT_OP_SET_SSP, - MGMT_OP_SET_HS, - MGMT_OP_SET_LE, - MGMT_OP_SET_DEV_CLASS, - MGMT_OP_SET_LOCAL_NAME, - MGMT_OP_ADD_UUID, - MGMT_OP_REMOVE_UUID, - MGMT_OP_LOAD_LINK_KEYS, - MGMT_OP_LOAD_LONG_TERM_KEYS, - MGMT_OP_DISCONNECT, - MGMT_OP_GET_CONNECTIONS, - MGMT_OP_PIN_CODE_REPLY, - MGMT_OP_PIN_CODE_NEG_REPLY, - MGMT_OP_SET_IO_CAPABILITY, - MGMT_OP_PAIR_DEVICE, - MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_OP_UNPAIR_DEVICE, - MGMT_OP_USER_CONFIRM_REPLY, - MGMT_OP_USER_CONFIRM_NEG_REPLY, - MGMT_OP_USER_PASSKEY_REPLY, - MGMT_OP_USER_PASSKEY_NEG_REPLY, - MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_OP_START_DISCOVERY, - MGMT_OP_STOP_DISCOVERY, - MGMT_OP_CONFIRM_NAME, - MGMT_OP_BLOCK_DEVICE, - MGMT_OP_UNBLOCK_DEVICE, -}; - -static const u16 mgmt_events[] = { - MGMT_EV_CONTROLLER_ERROR, - MGMT_EV_INDEX_ADDED, - MGMT_EV_INDEX_REMOVED, - MGMT_EV_NEW_SETTINGS, - MGMT_EV_CLASS_OF_DEV_CHANGED, - MGMT_EV_LOCAL_NAME_CHANGED, - MGMT_EV_NEW_LINK_KEY, - MGMT_EV_NEW_LONG_TERM_KEY, - MGMT_EV_DEVICE_CONNECTED, - MGMT_EV_DEVICE_DISCONNECTED, - MGMT_EV_CONNECT_FAILED, - MGMT_EV_PIN_CODE_REQUEST, - MGMT_EV_USER_CONFIRM_REQUEST, - MGMT_EV_USER_PASSKEY_REQUEST, - MGMT_EV_AUTH_FAILED, - MGMT_EV_DEVICE_FOUND, - MGMT_EV_DISCOVERING, - MGMT_EV_DEVICE_BLOCKED, - MGMT_EV_DEVICE_UNBLOCKED, - MGMT_EV_DEVICE_UNPAIRED, -}; - -/* - * These LE scan and inquiry parameters were chosen according to LE General - * Discovery Procedure specification. - */ -#define LE_SCAN_TYPE 0x01 -#define LE_SCAN_WIN 0x12 -#define LE_SCAN_INT 0x12 -#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */ -#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */ - -#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ -#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */ - -#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) - -#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \ - !test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - -struct pending_cmd { - struct list_head list; - u16 opcode; - int index; - void *param; - struct sock *sk; - void *user_data; -}; - -/* HCI to MGMT error code conversion table */ -static u8 mgmt_status_table[] = { - MGMT_STATUS_SUCCESS, - MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */ - MGMT_STATUS_NOT_CONNECTED, /* No Connection */ - MGMT_STATUS_FAILED, /* Hardware Failure */ - MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */ - MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */ - MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */ - MGMT_STATUS_NO_RESOURCES, /* Memory Full */ - MGMT_STATUS_TIMEOUT, /* Connection Timeout */ - MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */ - MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */ - MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */ - MGMT_STATUS_BUSY, /* Command Disallowed */ - MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */ - MGMT_STATUS_REJECTED, /* Rejected Security */ - MGMT_STATUS_REJECTED, /* Rejected Personal */ - MGMT_STATUS_TIMEOUT, /* Host Timeout */ - MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */ - MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */ - MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */ - MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */ - MGMT_STATUS_DISCONNECTED, /* OE Power Off */ - MGMT_STATUS_DISCONNECTED, /* Connection Terminated */ - MGMT_STATUS_BUSY, /* Repeated Attempts */ - MGMT_STATUS_REJECTED, /* Pairing Not Allowed */ - MGMT_STATUS_FAILED, /* Unknown LMP PDU */ - MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */ - MGMT_STATUS_REJECTED, /* SCO Offset Rejected */ - MGMT_STATUS_REJECTED, /* SCO Interval Rejected */ - MGMT_STATUS_REJECTED, /* Air Mode Rejected */ - MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */ - MGMT_STATUS_FAILED, /* Unspecified Error */ - MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */ - MGMT_STATUS_FAILED, /* Role Change Not Allowed */ - MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */ - MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */ - MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */ - MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */ - MGMT_STATUS_FAILED, /* Unit Link Key Used */ - MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */ - MGMT_STATUS_TIMEOUT, /* Instant Passed */ - MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */ - MGMT_STATUS_FAILED, /* Transaction Collision */ - MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */ - MGMT_STATUS_REJECTED, /* QoS Rejected */ - MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */ - MGMT_STATUS_REJECTED, /* Insufficient Security */ - MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */ - MGMT_STATUS_BUSY, /* Role Switch Pending */ - MGMT_STATUS_FAILED, /* Slot Violation */ - MGMT_STATUS_FAILED, /* Role Switch Failed */ - MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */ - MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */ - MGMT_STATUS_BUSY, /* Host Busy Pairing */ - MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */ - MGMT_STATUS_BUSY, /* Controller Busy */ - MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */ - MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */ - MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */ - MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */ - MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ -}; - -static u8 mgmt_status(u8 hci_status) -{ - if (hci_status < ARRAY_SIZE(mgmt_status_table)) - return mgmt_status_table[hci_status]; - - return MGMT_STATUS_FAILED; -} - -static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_status *ev; - int err; - - BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); - hdr->index = cpu_to_le16(index); - hdr->len = cpu_to_le16(sizeof(*ev)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - ev->status = status; - put_unaligned_le16(cmd, &ev->opcode); - - err = sock_queue_rcv_skb(sk, skb); - if (err < 0) - kfree_skb(skb); - - return err; -} - -static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, - void *rp, size_t rp_len) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - int err; - - BT_DBG("sock %p", sk); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->index = cpu_to_le16(index); - hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); - - ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); - put_unaligned_le16(cmd, &ev->opcode); - ev->status = status; - - if (rp) - memcpy(ev->data, rp, rp_len); - - err = sock_queue_rcv_skb(sk, skb); - if (err < 0) - kfree_skb(skb); - - return err; -} - -static int read_version(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_read_version rp; - - BT_DBG("sock %p", sk); - - rp.version = MGMT_VERSION; - put_unaligned_le16(MGMT_REVISION, &rp.revision); - - return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp, - sizeof(rp)); -} - -static int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_read_commands *rp; - u16 num_commands = ARRAY_SIZE(mgmt_commands); - u16 num_events = ARRAY_SIZE(mgmt_events); - u16 *opcode; - size_t rp_size; - int i, err; - - BT_DBG("sock %p", sk); - - rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16)); - - rp = kmalloc(rp_size, GFP_KERNEL); - if (!rp) - return -ENOMEM; - - put_unaligned_le16(num_commands, &rp->num_commands); - put_unaligned_le16(num_events, &rp->num_events); - - for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++) - put_unaligned_le16(mgmt_commands[i], opcode); - - for (i = 0; i < num_events; i++, opcode++) - put_unaligned_le16(mgmt_events[i], opcode); - - err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp, - rp_size); - kfree(rp); - - return err; -} - -static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_read_index_list *rp; - struct list_head *p; - struct hci_dev *d; - size_t rp_len; - u16 count; - int i, err; - - BT_DBG("sock %p", sk); - - read_lock(&hci_dev_list_lock); - - count = 0; - list_for_each(p, &hci_dev_list) { - count++; - } - - rp_len = sizeof(*rp) + (2 * count); - rp = kmalloc(rp_len, GFP_ATOMIC); - if (!rp) { - read_unlock(&hci_dev_list_lock); - return -ENOMEM; - } - - put_unaligned_le16(count, &rp->num_controllers); - - i = 0; - list_for_each_entry(d, &hci_dev_list, list) { - if (test_bit(HCI_SETUP, &d->dev_flags)) - continue; - - put_unaligned_le16(d->id, &rp->index[i++]); - BT_DBG("Added hci%u", d->id); - } - - read_unlock(&hci_dev_list_lock); - - err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp, - rp_len); - - kfree(rp); - - return err; -} - -static u32 get_supported_settings(struct hci_dev *hdev) -{ - u32 settings = 0; - - settings |= MGMT_SETTING_POWERED; - settings |= MGMT_SETTING_CONNECTABLE; - settings |= MGMT_SETTING_FAST_CONNECTABLE; - settings |= MGMT_SETTING_DISCOVERABLE; - settings |= MGMT_SETTING_PAIRABLE; - - if (hdev->features[6] & LMP_SIMPLE_PAIR) - settings |= MGMT_SETTING_SSP; - - if (!(hdev->features[4] & LMP_NO_BREDR)) { - settings |= MGMT_SETTING_BREDR; - settings |= MGMT_SETTING_LINK_SECURITY; - } - - if (enable_hs) - settings |= MGMT_SETTING_HS; - - if (enable_le) { - if (hdev->features[4] & LMP_LE) - settings |= MGMT_SETTING_LE; - } - - return settings; -} - -static u32 get_current_settings(struct hci_dev *hdev) -{ - u32 settings = 0; - - if (hdev_is_powered(hdev)) - settings |= MGMT_SETTING_POWERED; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_CONNECTABLE; - - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_DISCOVERABLE; - - if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) - settings |= MGMT_SETTING_PAIRABLE; - - if (!(hdev->features[4] & LMP_NO_BREDR)) - settings |= MGMT_SETTING_BREDR; - - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - settings |= MGMT_SETTING_LE; - - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - settings |= MGMT_SETTING_LINK_SECURITY; - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - settings |= MGMT_SETTING_SSP; - - if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) - settings |= MGMT_SETTING_HS; - - return settings; -} - -#define PNP_INFO_SVCLASS_ID 0x1200 - -static u8 bluetooth_base_uuid[] = { - 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; - -static u16 get_uuid16(u8 *uuid128) -{ - u32 val; - int i; - - for (i = 0; i < 12; i++) { - if (bluetooth_base_uuid[i] != uuid128[i]) - return 0; - } - - memcpy(&val, &uuid128[12], 4); - - val = le32_to_cpu(val); - if (val > 0xffff) - return 0; - - return (u16) val; -} - -static void create_eir(struct hci_dev *hdev, u8 *data) -{ - u8 *ptr = data; - u16 eir_len = 0; - u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; - int i, truncated = 0; - struct bt_uuid *uuid; - size_t name_len; - - name_len = strlen(hdev->dev_name); - - if (name_len > 0) { - /* EIR Data type */ - if (name_len > 48) { - name_len = 48; - ptr[1] = EIR_NAME_SHORT; - } else - ptr[1] = EIR_NAME_COMPLETE; - - /* EIR Data length */ - ptr[0] = name_len + 1; - - memcpy(ptr + 2, hdev->dev_name, name_len); - - eir_len += (name_len + 2); - ptr += (name_len + 2); - } - - memset(uuid16_list, 0, sizeof(uuid16_list)); - - /* Group all UUID16 types */ - list_for_each_entry(uuid, &hdev->uuids, list) { - u16 uuid16; - - uuid16 = get_uuid16(uuid->uuid); - if (uuid16 == 0) - return; - - if (uuid16 < 0x1100) - continue; - - if (uuid16 == PNP_INFO_SVCLASS_ID) - continue; - - /* Stop if not enough space to put next UUID */ - if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) { - truncated = 1; - break; - } - - /* Check for duplicates */ - for (i = 0; uuid16_list[i] != 0; i++) - if (uuid16_list[i] == uuid16) - break; - - if (uuid16_list[i] == 0) { - uuid16_list[i] = uuid16; - eir_len += sizeof(u16); - } - } - - if (uuid16_list[0] != 0) { - u8 *length = ptr; - - /* EIR Data type */ - ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL; - - ptr += 2; - eir_len += 2; - - for (i = 0; uuid16_list[i] != 0; i++) { - *ptr++ = (uuid16_list[i] & 0x00ff); - *ptr++ = (uuid16_list[i] & 0xff00) >> 8; - } - - /* EIR Data length */ - *length = (i * sizeof(u16)) + 1; - } -} - -static int update_eir(struct hci_dev *hdev) -{ - struct hci_cp_write_eir cp; - - if (!hdev_is_powered(hdev)) - return 0; - - if (!(hdev->features[6] & LMP_EXT_INQ)) - return 0; - - if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - return 0; - - if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return 0; - - memset(&cp, 0, sizeof(cp)); - - create_eir(hdev, cp.data); - - if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) - return 0; - - memcpy(hdev->eir, cp.data, sizeof(cp.data)); - - return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); -} - -static u8 get_service_classes(struct hci_dev *hdev) -{ - struct bt_uuid *uuid; - u8 val = 0; - - list_for_each_entry(uuid, &hdev->uuids, list) - val |= uuid->svc_hint; - - return val; -} - -static int update_class(struct hci_dev *hdev) -{ - u8 cod[3]; - int err; - - BT_DBG("%s", hdev->name); - - if (!hdev_is_powered(hdev)) - return 0; - - if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return 0; - - cod[0] = hdev->minor_class; - cod[1] = hdev->major_class; - cod[2] = get_service_classes(hdev); - - if (memcmp(cod, hdev->dev_class, 3) == 0) - return 0; - - err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - if (err == 0) - set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - - return err; -} - -static void service_cache_off(struct work_struct *work) -{ - struct hci_dev *hdev = container_of(work, struct hci_dev, - service_cache.work); - - if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return; - - hci_dev_lock(hdev); - - update_eir(hdev); - update_class(hdev); - - hci_dev_unlock(hdev); -} - -static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) -{ - if (test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) - return; - - INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); - - /* Non-mgmt controlled devices get this bit set - * implicitly so that pairing works for them, however - * for mgmt we require user-space to explicitly enable - * it - */ - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); -} - -static int read_controller_info(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) -{ - struct mgmt_rp_read_info rp; - - BT_DBG("sock %p %s", sk, hdev->name); - - hci_dev_lock(hdev); - - memset(&rp, 0, sizeof(rp)); - - bacpy(&rp.bdaddr, &hdev->bdaddr); - - rp.version = hdev->hci_ver; - - put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); - - rp.supported_settings = cpu_to_le32(get_supported_settings(hdev)); - rp.current_settings = cpu_to_le32(get_current_settings(hdev)); - - memcpy(rp.dev_class, hdev->dev_class, 3); - - memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); - memcpy(rp.short_name, hdev->short_name, sizeof(hdev->short_name)); - - hci_dev_unlock(hdev); - - return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp, - sizeof(rp)); -} - -static void mgmt_pending_free(struct pending_cmd *cmd) -{ - sock_put(cmd->sk); - kfree(cmd->param); - kfree(cmd); -} - -static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, - struct hci_dev *hdev, void *data, - u16 len) -{ - struct pending_cmd *cmd; - - cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); - if (!cmd) - return NULL; - - cmd->opcode = opcode; - cmd->index = hdev->id; - - cmd->param = kmalloc(len, GFP_ATOMIC); - if (!cmd->param) { - kfree(cmd); - return NULL; - } - - if (data) - memcpy(cmd->param, data, len); - - cmd->sk = sk; - sock_hold(sk); - - list_add(&cmd->list, &hdev->mgmt_pending); - - return cmd; -} - -static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, - void (*cb)(struct pending_cmd *cmd, void *data), - void *data) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->mgmt_pending) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); - - if (opcode > 0 && cmd->opcode != opcode) - continue; - - cb(cmd, data); - } -} - -static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) -{ - struct pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->opcode == opcode) - return cmd; - } - - return NULL; -} - -static void mgmt_pending_remove(struct pending_cmd *cmd) -{ - list_del(&cmd->list); - mgmt_pending_free(cmd); -} - -static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) -{ - __le32 settings = cpu_to_le32(get_current_settings(hdev)); - - return cmd_complete(sk, hdev->id, opcode, 0, &settings, - sizeof(settings)); -} - -static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { - cancel_delayed_work(&hdev->power_off); - - if (cp->val) { - err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); - mgmt_powered(hdev, 1); - goto failed; - } - } - - if (!!cp->val == hdev_is_powered(hdev)) { - err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, - MGMT_STATUS_BUSY); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - if (cp->val) - schedule_work(&hdev->power_on); - else - schedule_work(&hdev->power_off.work); - - err = 0; - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, - struct sock *skip_sk) -{ - struct sk_buff *skb; - struct mgmt_hdr *hdr; - - skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(event); - if (hdev) - hdr->index = cpu_to_le16(hdev->id); - else - hdr->index = cpu_to_le16(MGMT_INDEX_NONE); - hdr->len = cpu_to_le16(data_len); - - if (data) - memcpy(skb_put(skb, data_len), data, data_len); - - /* Time stamp */ - __net_timestamp(skb); - - hci_send_to_control(skb, skip_sk); - kfree_skb(skb); - - return 0; -} - -static int new_settings(struct hci_dev *hdev, struct sock *skip) -{ - __le32 ev; - - ev = cpu_to_le32(get_current_settings(hdev)); - - return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); -} - -static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_discoverable *cp = data; - struct pending_cmd *cmd; - u16 timeout; - u8 scan; - int err; - - BT_DBG("request for %s", hdev->name); - - timeout = get_unaligned_le16(&cp->timeout); - if (!cp->val && timeout > 0) - return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_INVALID_PARAMS); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev) && timeout > 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_BUSY); - goto failed; - } - - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_REJECTED); - goto failed; - } - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { - change_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { - if (hdev->discov_timeout > 0) { - cancel_delayed_work(&hdev->discov_off); - hdev->discov_timeout = 0; - } - - if (cp->val && timeout > 0) { - hdev->discov_timeout = timeout; - queue_delayed_work(hdev->workqueue, &hdev->discov_off, - msecs_to_jiffies(hdev->discov_timeout * 1000)); - } - - err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - scan = SCAN_PAGE; - - if (cp->val) - scan |= SCAN_INQUIRY; - else - cancel_delayed_work(&hdev->discov_off); - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (err < 0) - mgmt_pending_remove(cmd); - - if (cp->val) - hdev->discov_timeout = timeout; - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - u8 scan; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - - if (cp->val) { - set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - } else { - clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, - MGMT_STATUS_BUSY); - goto failed; - } - - if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - if (cp->val) { - scan = SCAN_PAGE; - } else { - scan = 0; - - if (test_bit(HCI_ISCAN, &hdev->flags) && - hdev->discov_timeout > 0) - cancel_delayed_work(&hdev->discov_off); - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (cp->val) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - else - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); - - err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); - if (err < 0) - goto failed; - - err = new_settings(hdev, sk); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - u8 val; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_LINK_SECURITY, - &hdev->dev_flags)) { - change_bit(HCI_LINK_SECURITY, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, - MGMT_STATUS_BUSY); - goto failed; - } - - val = !!cp->val; - - if (test_bit(HCI_AUTH, &hdev->flags) == val) { - err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val); - if (err < 0) { - mgmt_pending_remove(cmd); - goto failed; - } - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_mode *cp = data; - struct pending_cmd *cmd; - u8 val; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_NOT_SUPPORTED); - goto failed; - } - - val = !!cp->val; - - if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - change_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, - MGMT_STATUS_BUSY); - goto failed; - } - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) { - err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val); - if (err < 0) { - mgmt_pending_remove(cmd); - goto failed; - } - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_mode *cp = data; - - BT_DBG("request for %s", hdev->name); - - if (!enable_hs) - return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, - MGMT_STATUS_NOT_SUPPORTED); - - if (cp->val) - set_bit(HCI_HS_ENABLED, &hdev->dev_flags); - else - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); - - return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); -} - -static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_mode *cp = data; - struct hci_cp_write_le_host_supported hci_cp; - struct pending_cmd *cmd; - int err; - u8 val, enabled; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (!enable_le || !(hdev->features[4] & LMP_LE)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } - - val = !!cp->val; - enabled = !!(hdev->host_features[0] & LMP_HOST_LE); - - if (!hdev_is_powered(hdev) || val == enabled) { - bool changed = false; - - if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { - change_bit(HCI_LE_ENABLED, &hdev->dev_flags); - changed = true; - } - - err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); - if (err < 0) - goto unlock; - - if (changed) - err = new_settings(hdev, sk); - - goto unlock; - } - - if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, - MGMT_STATUS_BUSY); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - memset(&hci_cp, 0, sizeof(hci_cp)); - - if (val) { - hci_cp.le = val; - hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), - &hci_cp); - if (err < 0) { - mgmt_pending_remove(cmd); - goto unlock; - } - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) -{ - struct mgmt_cp_add_uuid *cp = data; - struct pending_cmd *cmd; - struct bt_uuid *uuid; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID, - MGMT_STATUS_BUSY); - goto failed; - } - - uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC); - if (!uuid) { - err = -ENOMEM; - goto failed; - } - - memcpy(uuid->uuid, cp->uuid, 16); - uuid->svc_hint = cp->svc_hint; - - list_add(&uuid->list, &hdev->uuids); - - err = update_class(hdev); - if (err < 0) - goto failed; - - err = update_eir(hdev); - if (err < 0) - goto failed; - - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, - hdev->dev_class, 3); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - -failed: - hci_dev_unlock(hdev); - return err; -} - -static bool enable_service_cache(struct hci_dev *hdev) -{ - if (!hdev_is_powered(hdev)) - return false; - - if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { - schedule_delayed_work(&hdev->service_cache, CACHE_TIMEOUT); - return true; - } - - return false; -} - -static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_remove_uuid *cp = data; - struct pending_cmd *cmd; - struct list_head *p, *n; - u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int err, found; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, - MGMT_STATUS_BUSY); - goto unlock; - } - - if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { - err = hci_uuids_clear(hdev); - - if (enable_service_cache(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, - 0, hdev->dev_class, 3); - goto unlock; - } - - goto update_class; - } - - found = 0; - - list_for_each_safe(p, n, &hdev->uuids) { - struct bt_uuid *match = list_entry(p, struct bt_uuid, list); - - if (memcmp(match->uuid, cp->uuid, 16) != 0) - continue; - - list_del(&match->list); - found++; - } - - if (found == 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - -update_class: - err = update_class(hdev); - if (err < 0) - goto unlock; - - err = update_eir(hdev); - if (err < 0) - goto unlock; - - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, - hdev->dev_class, 3); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_dev_class *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG("request for %s", hdev->name); - - hci_dev_lock(hdev); - - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_BUSY); - goto unlock; - } - - hdev->major_class = cp->major; - hdev->minor_class = cp->minor; - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); - goto unlock; - } - - if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { - hci_dev_unlock(hdev); - cancel_delayed_work_sync(&hdev->service_cache); - hci_dev_lock(hdev); - update_eir(hdev); - } - - err = update_class(hdev); - if (err < 0) - goto unlock; - - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, - hdev->dev_class, 3); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_load_link_keys *cp = data; - u16 key_count, expected_len; - int i; - - key_count = get_unaligned_le16(&cp->key_count); - - expected_len = sizeof(*cp) + key_count * - sizeof(struct mgmt_link_key_info); - if (expected_len != len) { - BT_ERR("load_link_keys: expected %u bytes, got %u bytes", - len, expected_len); - return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, - MGMT_STATUS_INVALID_PARAMS); - } - - BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys, - key_count); - - hci_dev_lock(hdev); - - hci_link_keys_clear(hdev); - - set_bit(HCI_LINK_KEYS, &hdev->dev_flags); - - if (cp->debug_keys) - set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); - else - clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); - - for (i = 0; i < key_count; i++) { - struct mgmt_link_key_info *key = &cp->keys[i]; - - hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, - key->type, key->pin_len); - } - - cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); - - hci_dev_unlock(hdev); - - return 0; -} - -static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 addr_type, struct sock *skip_sk) -{ - struct mgmt_ev_device_unpaired ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = addr_type; - - return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev), - skip_sk); -} - -static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_unpair_device *cp = data; - struct mgmt_rp_unpair_device rp; - struct hci_cp_disconnect dc; - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - hci_dev_lock(hdev); - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, - MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); - goto unlock; - } - - if (cp->addr.type == MGMT_ADDR_BREDR) - err = hci_remove_link_key(hdev, &cp->addr.bdaddr); - else - err = hci_remove_ltk(hdev, &cp->addr.bdaddr); - - if (err < 0) { - err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, - MGMT_STATUS_NOT_PAIRED, &rp, sizeof(rp)); - goto unlock; - } - - if (cp->disconnect) { - if (cp->addr.type == MGMT_ADDR_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 (!conn) { - err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, - &rp, sizeof(rp)); - device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp, - sizeof(*cp)); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); - if (err < 0) - mgmt_pending_remove(cmd); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_disconnect *cp = data; - struct hci_cp_disconnect dc; - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_BUSY); - goto failed; - } - - if (cp->addr.type == MGMT_ADDR_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); - - if (!conn) { - err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT, - MGMT_STATUS_NOT_CONNECTED); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static u8 link_to_mgmt(u8 link_type, u8 addr_type) -{ - switch (link_type) { - case LE_LINK: - switch (addr_type) { - case ADDR_LE_DEV_PUBLIC: - return MGMT_ADDR_LE_PUBLIC; - case ADDR_LE_DEV_RANDOM: - return MGMT_ADDR_LE_RANDOM; - default: - return MGMT_ADDR_INVALID; - } - case ACL_LINK: - return MGMT_ADDR_BREDR; - default: - return MGMT_ADDR_INVALID; - } -} - -static int get_connections(struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len) -{ - struct mgmt_rp_get_connections *rp; - struct hci_conn *c; - size_t rp_len; - int err; - u16 i; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - i = 0; - list_for_each_entry(c, &hdev->conn_hash.list, list) { - if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) - i++; - } - - rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); - rp = kmalloc(rp_len, GFP_ATOMIC); - if (!rp) { - err = -ENOMEM; - goto unlock; - } - - i = 0; - list_for_each_entry(c, &hdev->conn_hash.list, list) { - if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) - continue; - bacpy(&rp->addr[i].bdaddr, &c->dst); - rp->addr[i].type = link_to_mgmt(c->type, c->dst_type); - if (rp->addr[i].type == MGMT_ADDR_INVALID) - continue; - i++; - } - - put_unaligned_le16(i, &rp->conn_count); - - /* Recalculate length in case of filtered SCO connections, etc */ - rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); - - err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp, - rp_len); - - kfree(rp); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, - struct mgmt_cp_pin_code_neg_reply *cp) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, - sizeof(*cp)); - if (!cmd) - return -ENOMEM; - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, - sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); - if (err < 0) - mgmt_pending_remove(cmd); - - return err; -} - -static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct hci_conn *conn; - struct mgmt_cp_pin_code_reply *cp = data; - struct hci_cp_pin_code_reply reply; - struct pending_cmd *cmd; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); - if (!conn) { - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_NOT_CONNECTED); - goto failed; - } - - if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) { - struct mgmt_cp_pin_code_neg_reply ncp; - - memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr)); - - BT_ERR("PIN code is not 16 bytes long"); - - err = send_pin_code_neg_reply(sk, hdev, &ncp); - if (err >= 0) - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - MGMT_STATUS_INVALID_PARAMS); - - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - bacpy(&reply.bdaddr, &cp->addr.bdaddr); - reply.pin_len = cp->pin_len; - memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); - - err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_pin_code_neg_reply *cp = data; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - err = send_pin_code_neg_reply(sk, hdev, cp); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_io_capability *cp = data; - - BT_DBG(""); - - hci_dev_lock(hdev); - - hdev->io_capability = cp->io_capability; - - BT_DBG("%s IO capability set to 0x%02x", hdev->name, - hdev->io_capability); - - hci_dev_unlock(hdev); - - return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, - 0); -} - -static inline struct pending_cmd *find_pairing(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->opcode != MGMT_OP_PAIR_DEVICE) - continue; - - if (cmd->user_data != conn) - continue; - - return cmd; - } - - return NULL; -} - -static void pairing_complete(struct pending_cmd *cmd, u8 status) -{ - struct mgmt_rp_pair_device rp; - struct hci_conn *conn = cmd->user_data; - - bacpy(&rp.addr.bdaddr, &conn->dst); - rp.addr.type = link_to_mgmt(conn->type, conn->dst_type); - - cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status, - &rp, sizeof(rp)); - - /* So we don't get further callbacks for this connection */ - conn->connect_cfm_cb = NULL; - conn->security_cfm_cb = NULL; - conn->disconn_cfm_cb = NULL; - - hci_conn_put(conn); - - mgmt_pending_remove(cmd); -} - -static void pairing_complete_cb(struct hci_conn *conn, u8 status) -{ - struct pending_cmd *cmd; - - BT_DBG("status %u", status); - - cmd = find_pairing(conn); - if (!cmd) - BT_DBG("Unable to find a pending command"); - else - pairing_complete(cmd, mgmt_status(status)); -} - -static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_pair_device *cp = data; - struct mgmt_rp_pair_device rp; - struct pending_cmd *cmd; - u8 sec_level, auth_type; - struct hci_conn *conn; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - sec_level = BT_SECURITY_MEDIUM; - if (cp->io_cap == 0x03) - auth_type = HCI_AT_DEDICATED_BONDING; - else - auth_type = HCI_AT_DEDICATED_BONDING_MITM; - - if (cp->addr.type == MGMT_ADDR_BREDR) - conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level, - auth_type); - else - conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level, - auth_type); - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - if (IS_ERR(conn)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_CONNECT_FAILED, &rp, - sizeof(rp)); - goto unlock; - } - - if (conn->connect_cfm_cb) { - hci_conn_put(conn); - err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, - MGMT_STATUS_BUSY, &rp, sizeof(rp)); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - hci_conn_put(conn); - goto unlock; - } - - /* For LE, just connecting isn't a proof that the pairing finished */ - if (cp->addr.type == MGMT_ADDR_BREDR) - conn->connect_cfm_cb = pairing_complete_cb; - - conn->security_cfm_cb = pairing_complete_cb; - conn->disconn_cfm_cb = pairing_complete_cb; - conn->io_capability = cp->io_cap; - cmd->user_data = conn; - - if (conn->state == BT_CONNECTED && - hci_conn_security(conn, sec_level, auth_type)) - pairing_complete(cmd, 0); - - err = 0; - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_addr_info *addr = data; - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev); - if (!cmd) { - err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - - conn = cmd->user_data; - - if (bacmp(&addr->bdaddr, &conn->dst) != 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, - MGMT_STATUS_INVALID_PARAMS); - goto unlock; - } - - pairing_complete(cmd, MGMT_STATUS_CANCELLED); - - err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, - addr, sizeof(*addr)); -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type, u16 mgmt_op, - u16 hci_op, __le32 passkey) -{ - struct pending_cmd *cmd; - struct hci_conn *conn; - int err; - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_POWERED); - goto done; - } - - if (type == MGMT_ADDR_BREDR) - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); - else - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); - - if (!conn) { - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_CONNECTED); - goto done; - } - - if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) { - /* Continue with pairing via SMP */ - err = smp_user_confirm_reply(conn, mgmt_op, passkey); - - if (!err) - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_SUCCESS); - else - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_FAILED); - - goto done; - } - - cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr)); - if (!cmd) { - err = -ENOMEM; - goto done; - } - - /* Continue with pairing via HCI */ - if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { - struct hci_cp_user_passkey_reply cp; - - bacpy(&cp.bdaddr, bdaddr); - cp.passkey = passkey; - err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); - } else - err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr); - - if (err < 0) - mgmt_pending_remove(cmd); - -done: - hci_dev_unlock(hdev); - return err; -} - -static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_user_confirm_reply *cp = data; - - BT_DBG(""); - - if (len != sizeof(*cp)) - return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY, - MGMT_STATUS_INVALID_PARAMS); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_CONFIRM_REPLY, - HCI_OP_USER_CONFIRM_REPLY, 0); -} - -static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_user_confirm_neg_reply *cp = data; - - BT_DBG(""); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_CONFIRM_NEG_REPLY, - HCI_OP_USER_CONFIRM_NEG_REPLY, 0); -} - -static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_user_passkey_reply *cp = data; - - BT_DBG(""); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_PASSKEY_REPLY, - HCI_OP_USER_PASSKEY_REPLY, cp->passkey); -} - -static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_user_passkey_neg_reply *cp = data; - - BT_DBG(""); - - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, - MGMT_OP_USER_PASSKEY_NEG_REPLY, - HCI_OP_USER_PASSKEY_NEG_REPLY, 0); -} - -static int update_name(struct hci_dev *hdev, const char *name) -{ - struct hci_cp_write_local_name cp; - - memcpy(cp.name, name, sizeof(cp.name)); - - return hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); -} - -static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_set_local_name *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG(""); - - hci_dev_lock(hdev); - - memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); - - if (!hdev_is_powered(hdev)) { - memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); - - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, - data, len); - if (err < 0) - goto failed; - - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len, - sk); - - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - err = update_name(hdev, cp->name); - if (err < 0) - mgmt_pending_remove(cmd); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 data_len) -{ - struct pending_cmd *cmd; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_NOT_POWERED); - goto unlock; - } - - if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { - err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_NOT_SUPPORTED); - goto unlock; - } - - if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - MGMT_STATUS_BUSY); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); - if (err < 0) - mgmt_pending_remove(cmd); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_add_remote_oob_data *cp = data; - u8 status; - int err; - - BT_DBG("%s ", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - - err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash, - cp->randomizer); - if (err < 0) - status = MGMT_STATUS_FAILED; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status, - &cp->addr, sizeof(cp->addr)); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_remove_remote_oob_data *cp = data; - u8 status; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_complete(sk, hdev->id, - MGMT_OP_REMOVE_REMOTE_OOB_DATA, - MGMT_STATUS_NOT_POWERED, &cp->addr, - sizeof(cp->addr)); - goto unlock; - } - - err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr); - if (err < 0) - status = MGMT_STATUS_INVALID_PARAMS; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - status, &cp->addr, sizeof(cp->addr)); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -int mgmt_interleaved_discovery(struct hci_dev *hdev) -{ - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE); - if (err < 0) - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - - hci_dev_unlock(hdev); - - return err; -} - -static int start_discovery(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_cp_start_discovery *cp = data; - struct pending_cmd *cmd; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_POWERED); - goto failed; - } - - if (hdev->discovery.state != DISCOVERY_STOPPED) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_BUSY); - goto failed; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - - hdev->discovery.type = cp->type; - - switch (hdev->discovery.type) { - case DISCOV_TYPE_BREDR: - if (lmp_bredr_capable(hdev)) - err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); - else - err = -ENOTSUPP; - break; - - case DISCOV_TYPE_LE: - if (lmp_host_le_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY); - else - err = -ENOTSUPP; - break; - - case DISCOV_TYPE_INTERLEAVED: - if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev)) - err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT, - LE_SCAN_WIN, - LE_SCAN_TIMEOUT_BREDR_LE); - else - err = -ENOTSUPP; - break; - - default: - err = -EINVAL; - } - - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STARTING); - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_stop_discovery *mgmt_cp = data; - struct pending_cmd *cmd; - struct hci_cp_remote_name_req_cancel cp; - struct inquiry_entry *e; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hci_discovery_active(hdev)) { - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_REJECTED, &mgmt_cp->type, - sizeof(mgmt_cp->type)); - goto unlock; - } - - if (hdev->discovery.type != mgmt_cp->type) { - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_INVALID_PARAMS, &mgmt_cp->type, - sizeof(mgmt_cp->type)); - goto unlock; - } - - cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto unlock; - } - - if (hdev->discovery.state == DISCOVERY_FINDING) { - err = hci_cancel_inquiry(hdev); - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STOPPING); - goto unlock; - } - - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); - if (!e) { - mgmt_pending_remove(cmd); - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0, - &mgmt_cp->type, sizeof(mgmt_cp->type)); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - goto unlock; - } - - bacpy(&cp.bdaddr, &e->data.bdaddr); - err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), - &cp); - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STOPPING); - -unlock: - hci_dev_unlock(hdev); - return err; -} - -static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_confirm_name *cp = data; - struct inquiry_entry *e; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - if (!hci_discovery_active(hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, - MGMT_STATUS_FAILED); - goto failed; - } - - e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr); - if (!e) { - err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME, - MGMT_STATUS_INVALID_PARAMS); - goto failed; - } - - if (cp->name_known) { - e->name_state = NAME_KNOWN; - list_del(&e->list); - } else { - e->name_state = NAME_NEEDED; - hci_inquiry_cache_update_resolve(hdev, e); - } - - err = 0; - -failed: - hci_dev_unlock(hdev); - return err; -} - -static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_block_device *cp = data; - u8 status; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) - status = MGMT_STATUS_FAILED; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, - &cp->addr, sizeof(cp->addr)); - - hci_dev_unlock(hdev); - - return err; -} - -static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, - u16 len) -{ - struct mgmt_cp_unblock_device *cp = data; - u8 status; - int err; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) - status = MGMT_STATUS_INVALID_PARAMS; - else - status = 0; - - err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, - &cp->addr, sizeof(cp->addr)); - - hci_dev_unlock(hdev); - - return err; -} - -static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, - void *data, u16 len) -{ - struct mgmt_mode *cp = data; - struct hci_cp_write_page_scan_activity acp; - u8 type; - int err; - - BT_DBG("%s", hdev->name); - - if (!hdev_is_powered(hdev)) - return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_NOT_POWERED); - - if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_REJECTED); - - hci_dev_lock(hdev); - - if (cp->val) { - type = PAGE_SCAN_TYPE_INTERLACED; - - /* 22.5 msec page scan interval */ - acp.interval = __constant_cpu_to_le16(0x0024); - } else { - type = PAGE_SCAN_TYPE_STANDARD; /* default */ - - /* default 1.28 sec page scan */ - acp.interval = __constant_cpu_to_le16(0x0800); - } - - /* default 11.25 msec page scan window */ - acp.window = __constant_cpu_to_le16(0x0012); - - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), - &acp); - if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); - goto done; - } - - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); - if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); - goto done; - } - - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0, - NULL, 0); -done: - hci_dev_unlock(hdev); - return err; -} - -static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, - void *cp_data, u16 len) -{ - struct mgmt_cp_load_long_term_keys *cp = cp_data; - u16 key_count, expected_len; - int i; - - key_count = get_unaligned_le16(&cp->key_count); - - expected_len = sizeof(*cp) + key_count * - sizeof(struct mgmt_ltk_info); - if (expected_len != len) { - BT_ERR("load_keys: expected %u bytes, got %u bytes", - len, expected_len); - return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, - EINVAL); - } - - BT_DBG("%s key_count %u", hdev->name, key_count); - - hci_dev_lock(hdev); - - hci_smp_ltks_clear(hdev); - - for (i = 0; i < key_count; i++) { - struct mgmt_ltk_info *key = &cp->keys[i]; - u8 type; - - if (key->master) - type = HCI_SMP_LTK; - else - type = HCI_SMP_LTK_SLAVE; - - hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type, - type, 0, key->authenticated, key->val, - key->enc_size, key->ediv, key->rand); - } - - hci_dev_unlock(hdev); - - return 0; -} - -struct mgmt_handler { - int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, - u16 data_len); - bool var_len; - size_t data_len; -} mgmt_handlers[] = { - { NULL }, /* 0x0000 (no command) */ - { read_version, false, MGMT_READ_VERSION_SIZE }, - { read_commands, false, MGMT_READ_COMMANDS_SIZE }, - { read_index_list, false, MGMT_READ_INDEX_LIST_SIZE }, - { read_controller_info, false, MGMT_READ_INFO_SIZE }, - { set_powered, false, MGMT_SETTING_SIZE }, - { set_discoverable, false, MGMT_SET_DISCOVERABLE_SIZE }, - { set_connectable, false, MGMT_SETTING_SIZE }, - { set_fast_connectable, false, MGMT_SETTING_SIZE }, - { set_pairable, false, MGMT_SETTING_SIZE }, - { set_link_security, false, MGMT_SETTING_SIZE }, - { set_ssp, false, MGMT_SETTING_SIZE }, - { set_hs, false, MGMT_SETTING_SIZE }, - { set_le, false, MGMT_SETTING_SIZE }, - { set_dev_class, false, MGMT_SET_DEV_CLASS_SIZE }, - { set_local_name, false, MGMT_SET_LOCAL_NAME_SIZE }, - { add_uuid, false, MGMT_ADD_UUID_SIZE }, - { remove_uuid, false, MGMT_REMOVE_UUID_SIZE }, - { load_link_keys, true, MGMT_LOAD_LINK_KEYS_SIZE }, - { load_long_term_keys, true, MGMT_LOAD_LONG_TERM_KEYS_SIZE }, - { disconnect, false, MGMT_DISCONNECT_SIZE }, - { get_connections, false, MGMT_GET_CONNECTIONS_SIZE }, - { pin_code_reply, false, MGMT_PIN_CODE_REPLY_SIZE }, - { pin_code_neg_reply, false, MGMT_PIN_CODE_NEG_REPLY_SIZE }, - { set_io_capability, false, MGMT_SET_IO_CAPABILITY_SIZE }, - { pair_device, false, MGMT_PAIR_DEVICE_SIZE }, - { cancel_pair_device, false, MGMT_CANCEL_PAIR_DEVICE_SIZE }, - { unpair_device, false, MGMT_UNPAIR_DEVICE_SIZE }, - { user_confirm_reply, false, MGMT_USER_CONFIRM_REPLY_SIZE }, - { user_confirm_neg_reply, false, MGMT_USER_CONFIRM_NEG_REPLY_SIZE }, - { user_passkey_reply, false, MGMT_USER_PASSKEY_REPLY_SIZE }, - { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, - { read_local_oob_data, false, MGMT_READ_LOCAL_OOB_DATA_SIZE }, - { add_remote_oob_data, false, MGMT_ADD_REMOTE_OOB_DATA_SIZE }, - { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, - { start_discovery, false, MGMT_START_DISCOVERY_SIZE }, - { stop_discovery, false, MGMT_STOP_DISCOVERY_SIZE }, - { confirm_name, false, MGMT_CONFIRM_NAME_SIZE }, - { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, - { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, -}; - - -int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) -{ - void *buf; - u8 *cp; - struct mgmt_hdr *hdr; - u16 opcode, index, len; - struct hci_dev *hdev = NULL; - struct mgmt_handler *handler; - int err; - - BT_DBG("got %zu bytes", msglen); - - if (msglen < sizeof(*hdr)) - return -EINVAL; - - buf = kmalloc(msglen, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) { - err = -EFAULT; - goto done; - } - - hdr = buf; - opcode = get_unaligned_le16(&hdr->opcode); - index = get_unaligned_le16(&hdr->index); - len = get_unaligned_le16(&hdr->len); - - if (len != msglen - sizeof(*hdr)) { - err = -EINVAL; - goto done; - } - - if (index != MGMT_INDEX_NONE) { - hdev = hci_dev_get(index); - if (!hdev) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - } - - if (opcode >= ARRAY_SIZE(mgmt_handlers) || - mgmt_handlers[opcode].func == NULL) { - BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, index, opcode, - MGMT_STATUS_UNKNOWN_COMMAND); - goto done; - } - - if ((hdev && opcode < MGMT_OP_READ_INFO) || - (!hdev && opcode >= MGMT_OP_READ_INFO)) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_INDEX); - goto done; - } - - handler = &mgmt_handlers[opcode]; - - if ((handler->var_len && len < handler->data_len) || - (!handler->var_len && len != handler->data_len)) { - err = cmd_status(sk, index, opcode, - MGMT_STATUS_INVALID_PARAMS); - goto done; - } - - if (hdev) - mgmt_init_hdev(sk, hdev); - - cp = buf + sizeof(*hdr); - - err = handler->func(sk, hdev, cp, len); - if (err < 0) - goto done; - - err = msglen; - -done: - if (hdev) - hci_dev_put(hdev); - - kfree(buf); - return err; -} - -static void cmd_status_rsp(struct pending_cmd *cmd, void *data) -{ - u8 *status = data; - - cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); - mgmt_pending_remove(cmd); -} - -int mgmt_index_added(struct hci_dev *hdev) -{ - return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); -} - -int mgmt_index_removed(struct hci_dev *hdev) -{ - u8 status = MGMT_STATUS_INVALID_INDEX; - - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - - return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); -} - -struct cmd_lookup { - struct sock *sk; - struct hci_dev *hdev; - u8 mgmt_status; -}; - -static void settings_rsp(struct pending_cmd *cmd, void *data) -{ - struct cmd_lookup *match = data; - - send_settings_rsp(cmd->sk, cmd->opcode, match->hdev); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - -int mgmt_powered(struct hci_dev *hdev, u8 powered) -{ - struct cmd_lookup match = { NULL, hdev }; - int err; - - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - return 0; - - mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); - - if (powered) { - u8 scan = 0; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - scan |= SCAN_PAGE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - if (scan) - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - - update_class(hdev); - update_name(hdev, hdev->dev_name); - update_eir(hdev); - } else { - u8 status = MGMT_STATUS_NOT_POWERED; - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - } - - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (discoverable) { - if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_connectable(struct hci_dev *hdev, u8 connectable) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (connectable) { - if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) -{ - u8 mgmt_err = mgmt_status(status); - - if (scan & SCAN_PAGE) - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, - cmd_status_rsp, &mgmt_err); - - if (scan & SCAN_INQUIRY) - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, - cmd_status_rsp, &mgmt_err); - - return 0; -} - -int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent) -{ - struct mgmt_ev_new_link_key ev; - - memset(&ev, 0, sizeof(ev)); - - ev.store_hint = persistent; - bacpy(&ev.key.addr.bdaddr, &key->bdaddr); - ev.key.addr.type = MGMT_ADDR_BREDR; - ev.key.type = key->type; - memcpy(ev.key.val, key->val, 16); - ev.key.pin_len = key->pin_len; - - return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) -{ - struct mgmt_ev_new_long_term_key ev; - - memset(&ev, 0, sizeof(ev)); - - ev.store_hint = persistent; - bacpy(&ev.key.addr.bdaddr, &key->bdaddr); - ev.key.addr.type = key->bdaddr_type; - ev.key.authenticated = key->authenticated; - ev.key.enc_size = key->enc_size; - ev.key.ediv = key->ediv; - - if (key->type == HCI_SMP_LTK) - ev.key.master = 1; - - memcpy(ev.key.rand, key->rand, sizeof(key->rand)); - memcpy(ev.key.val, key->val, sizeof(key->val)); - - return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), - NULL); -} - -int 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) -{ - 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_mgmt(link_type, addr_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); - - 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); - - put_unaligned_le16(eir_len, &ev->eir_len); - - return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, - sizeof(*ev) + eir_len, NULL); -} - -static void disconnect_rsp(struct pending_cmd *cmd, void *data) -{ - struct mgmt_cp_disconnect *cp = cmd->param; - struct sock **sk = data; - struct mgmt_rp_disconnect rp; - - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp, - sizeof(rp)); - - *sk = cmd->sk; - sock_hold(*sk); - - mgmt_pending_remove(cmd); -} - -static void unpair_device_rsp(struct pending_cmd *cmd, void *data) -{ - struct hci_dev *hdev = data; - struct mgmt_cp_unpair_device *cp = cmd->param; - struct mgmt_rp_unpair_device rp; - - memset(&rp, 0, sizeof(rp)); - bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); - rp.addr.type = cp->addr.type; - - device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); - - cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); -} - -int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type) -{ - struct mgmt_addr_info ev; - struct sock *sk = NULL; - int err; - - mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); - - bacpy(&ev.bdaddr, bdaddr); - ev.type = link_to_mgmt(link_type, addr_type); - - err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), - sk); - - if (sk) - sock_put(sk); - - mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); - - return err; -} - -int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - struct mgmt_rp_disconnect rp; - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = link_to_mgmt(link_type, addr_type); - - err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, - mgmt_status(status), &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, - hdev); - return err; -} - -int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) -{ - struct mgmt_ev_connect_failed ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.status = mgmt_status(status); - - return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) -{ - struct mgmt_ev_pin_code_request ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = MGMT_ADDR_BREDR; - ev.secure = secure; - - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), - NULL); -} - -int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) -{ - struct pending_cmd *cmd; - struct mgmt_rp_pin_code_reply rp; - int err; - - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = MGMT_ADDR_BREDR; - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - mgmt_status(status), &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) -{ - struct pending_cmd *cmd; - struct mgmt_rp_pin_code_reply rp; - int err; - - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = MGMT_ADDR_BREDR; - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - mgmt_status(status), &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, __le32 value, - u8 confirm_hint) -{ - struct mgmt_ev_user_confirm_request ev; - - BT_DBG("%s", hdev->name); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.confirm_hint = confirm_hint; - put_unaligned_le32(value, &ev.value); - - return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), - NULL); -} - -int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type) -{ - struct mgmt_ev_user_passkey_request ev; - - BT_DBG("%s", hdev->name); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - - return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), - NULL); -} - -static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status, - u8 opcode) -{ - struct pending_cmd *cmd; - struct mgmt_rp_user_confirm_reply rp; - int err; - - cmd = mgmt_pending_find(opcode, hdev); - if (!cmd) - return -ENOENT; - - bacpy(&rp.addr.bdaddr, bdaddr); - rp.addr.type = link_to_mgmt(link_type, addr_type); - err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status), - &rp, sizeof(rp)); - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_CONFIRM_REPLY); -} - -int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_CONFIRM_NEG_REPLY); -} - -int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_PASSKEY_REPLY); -} - -int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) -{ - return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, - status, MGMT_OP_USER_PASSKEY_NEG_REPLY); -} - -int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) -{ - struct mgmt_ev_auth_failed ev; - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = link_to_mgmt(link_type, addr_type); - ev.status = mgmt_status(status); - - return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, - cmd_status_rsp, &mgmt_err); - return 0; - } - - if (test_bit(HCI_AUTH, &hdev->flags)) { - if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -static int clear_eir(struct hci_dev *hdev) -{ - struct hci_cp_write_eir cp; - - if (!(hdev->features[6] & LMP_EXT_INQ)) - return 0; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - - memset(&cp, 0, sizeof(cp)); - - return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); -} - -int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - - if (enable && test_and_clear_bit(HCI_SSP_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); - - mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, - &mgmt_err); - - return err; - } - - if (enable) { - if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - update_eir(hdev); - else - clear_eir(hdev); - - return err; -} - -static void class_rsp(struct pending_cmd *cmd, void *data) -{ - struct cmd_lookup *match = data; - - cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status, - match->hdev->dev_class, 3); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - -int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, - u8 status) -{ - struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; - int err = 0; - - clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - - mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match); - mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match); - mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match); - - if (!status) - err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, - 3, NULL); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) -{ - struct pending_cmd *cmd; - struct mgmt_cp_set_local_name ev; - bool changed = false; - int err = 0; - - if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) { - memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); - changed = true; - } - - memset(&ev, 0, sizeof(ev)); - memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); - - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); - if (!cmd) - goto send_event; - - /* Always assume that either the short or the complete name has - * changed if there was a pending mgmt command */ - changed = true; - - if (status) { - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, - mgmt_status(status)); - goto failed; - } - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev, - sizeof(ev)); - if (err < 0) - goto failed; - -send_event: - if (changed) - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, - sizeof(ev), cmd ? cmd->sk : NULL); - - update_eir(hdev); - -failed: - if (cmd) - mgmt_pending_remove(cmd); - return err; -} - -int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status) -{ - struct pending_cmd *cmd; - int err; - - BT_DBG("%s status %u", hdev->name, status); - - cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); - if (!cmd) - return -ENOENT; - - if (status) { - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - mgmt_status(status)); - } else { - struct mgmt_rp_read_local_oob_data rp; - - memcpy(rp.hash, hash, sizeof(rp.hash)); - memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); - - err = cmd_complete(cmd->sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp, - sizeof(rp)); - } - - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - - if (enable && test_and_clear_bit(HCI_LE_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); - - mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, - cmd_status_rsp, &mgmt_err); - - return err; - } - - if (enable) { - if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; -} - -int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 - ssp, u8 *eir, u16 eir_len) -{ - char buf[512]; - struct mgmt_ev_device_found *ev = (void *) buf; - size_t ev_size; - - /* Leave 5 bytes for a potential CoD field */ - if (sizeof(*ev) + eir_len + 5 > sizeof(buf)) - return -EINVAL; - - memset(buf, 0, sizeof(buf)); - - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_mgmt(link_type, addr_type); - ev->rssi = rssi; - if (cfm_name) - ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME; - if (!ssp) - ev->flags[0] |= MGMT_DEV_FOUND_LEGACY_PAIRING; - - if (eir_len > 0) - memcpy(ev->eir, eir, eir_len); - - if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV)) - eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, - dev_class, 3); - - put_unaligned_le16(eir_len, &ev->eir_len); - - ev_size = sizeof(*ev) + eir_len; - - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); -} - -int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, s8 rssi, u8 *name, u8 name_len) -{ - struct mgmt_ev_device_found *ev; - char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; - u16 eir_len; - - ev = (struct mgmt_ev_device_found *) buf; - - memset(buf, 0, sizeof(buf)); - - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_mgmt(link_type, addr_type); - ev->rssi = rssi; - - eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, - name_len); - - put_unaligned_le16(eir_len, &ev->eir_len); - - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, - sizeof(*ev) + eir_len, NULL); -} - -int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) -{ - struct pending_cmd *cmd; - u8 type; - int err; - - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); - if (!cmd) - return -ENOENT; - - type = hdev->discovery.type; - - err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &type, sizeof(type)); - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) -{ - struct pending_cmd *cmd; - int err; - - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); - if (!cmd) - return -ENOENT; - - err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status), - &hdev->discovery.type, sizeof(hdev->discovery.type)); - mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_discovering(struct hci_dev *hdev, u8 discovering) -{ - struct mgmt_ev_discovering ev; - struct pending_cmd *cmd; - - BT_DBG("%s discovering %u", hdev->name, discovering); - - if (discovering) - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); - else - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); - - if (cmd != NULL) { - u8 type = hdev->discovery.type; - - cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type, - sizeof(type)); - mgmt_pending_remove(cmd); - } - - memset(&ev, 0, sizeof(ev)); - ev.type = hdev->discovery.type; - ev.discovering = discovering; - - return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); -} - -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_blocked ev; - - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_unblocked ev; - - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - -module_param(enable_hs, bool, 0644); -MODULE_PARM_DESC(enable_hs, "Enable High Speed support"); - -module_param(enable_le, bool, 0644); -MODULE_PARM_DESC(enable_le, "Enable Low Energy support"); diff --git a/net/bluetooth_tizen/rfcomm/Kconfig b/net/bluetooth_tizen/rfcomm/Kconfig deleted file mode 100644 index 22e718b..0000000 --- a/net/bluetooth_tizen/rfcomm/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -config BT_RFCOMM - tristate "RFCOMM protocol support" - depends on BT - help - RFCOMM provides connection oriented stream transport. RFCOMM - support is required for Dialup Networking, OBEX and other Bluetooth - applications. - - Say Y here to compile RFCOMM support into the kernel or say M to - compile it as module (rfcomm). - -config BT_RFCOMM_TTY - bool "RFCOMM TTY support" - depends on BT_RFCOMM - help - This option enables TTY emulation support for RFCOMM channels. - diff --git a/net/bluetooth_tizen/rfcomm/Makefile b/net/bluetooth_tizen/rfcomm/Makefile deleted file mode 100644 index fe07988..0000000 --- a/net/bluetooth_tizen/rfcomm/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the Linux Bluetooth RFCOMM layer. -# - -obj-$(CONFIG_BT_RFCOMM) += rfcomm.o - -rfcomm-y := core.o sock.o -rfcomm-$(CONFIG_BT_RFCOMM_TTY) += tty.o diff --git a/net/bluetooth_tizen/rfcomm/core.c b/net/bluetooth_tizen/rfcomm/core.c deleted file mode 100644 index 8a60238..0000000 --- a/net/bluetooth_tizen/rfcomm/core.c +++ /dev/null @@ -1,2245 +0,0 @@ -/* - RFCOMM implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* - * Bluetooth RFCOMM core. - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/init.h> -#include <linux/wait.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/net.h> -#include <linux/mutex.h> -#include <linux/kthread.h> -#include <linux/slab.h> - -#include <net/sock.h> -#include <linux/uaccess.h> -#include <asm/unaligned.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/rfcomm.h> - -#define VERSION "1.11" - -static bool disable_cfc; -static bool l2cap_ertm; -static int channel_mtu = -1; -static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; - -static struct task_struct *rfcomm_thread; - -static DEFINE_MUTEX(rfcomm_mutex); -#define rfcomm_lock() mutex_lock(&rfcomm_mutex) -#define rfcomm_unlock() mutex_unlock(&rfcomm_mutex) - - -static LIST_HEAD(session_list); - -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); -static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); -static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); -static int rfcomm_queue_disc(struct rfcomm_dlc *d); -static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type); -static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d); -static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig); -static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len); -static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits); -static void rfcomm_make_uih(struct sk_buff *skb, u8 addr); - -static void rfcomm_process_connect(struct rfcomm_session *s); - -static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, - bdaddr_t *dst, - u8 sec_level, - int *err); -static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); -static void rfcomm_session_del(struct rfcomm_session *s); - -/* ---- RFCOMM frame parsing macros ---- */ -#define __get_dlci(b) ((b & 0xfc) >> 2) -#define __get_channel(b) ((b & 0xf8) >> 3) -#define __get_dir(b) ((b & 0x04) >> 2) -#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 __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01) -#define __ctrl(type, pf) (((type & 0xef) | (pf << 4))) -#define __dlci(dir, chn) (((chn & 0x1f) << 1) | dir) -#define __srv_channel(dlci) (dlci >> 1) -#define __dir(dlci) (dlci & 0x01) - -#define __len8(len) (((len) << 1) | 1) -#define __len16(len) ((len) << 1) - -/* MCC macros */ -#define __mcc_type(cr, type) (((type << 2) | (cr << 1) | 0x01)) -#define __get_mcc_type(b) ((b & 0xfc) >> 2) -#define __get_mcc_len(b) ((b & 0xfe) >> 1) - -/* RPN macros */ -#define __rpn_line_settings(data, stop, parity) ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x7) << 3)) -#define __get_rpn_data_bits(line) ((line) & 0x3) -#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1) -#define __get_rpn_parity(line) (((line) >> 3) & 0x7) - -static inline void rfcomm_schedule(void) -{ - if (!rfcomm_thread) - return; - wake_up_process(rfcomm_thread); -} - -static inline void rfcomm_session_put(struct rfcomm_session *s) -{ - if (atomic_dec_and_test(&s->refcnt)) - rfcomm_session_del(s); -} - -/* ---- RFCOMM FCS computation ---- */ - -/* reversed, 8-bit, poly=0x07 */ -static unsigned char rfcomm_crc_table[256] = { - 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75, - 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b, - 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69, - 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67, - - 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d, - 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43, - 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51, - 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f, - - 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05, - 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b, - 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19, - 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17, - - 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d, - 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33, - 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21, - 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f, - - 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95, - 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b, - 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89, - 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87, - - 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad, - 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3, - 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1, - 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf, - - 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5, - 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb, - 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9, - 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7, - - 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd, - 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3, - 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1, - 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf -}; - -/* CRC on 2 bytes */ -#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]]) - -/* FCS on 2 bytes */ -static inline u8 __fcs(u8 *data) -{ - return 0xff - __crc(data); -} - -/* FCS on 3 bytes */ -static inline u8 __fcs2(u8 *data) -{ - return 0xff - rfcomm_crc_table[__crc(data) ^ data[2]]; -} - -/* Check FCS */ -static inline int __check_fcs(u8 *data, int type, u8 fcs) -{ - u8 f = __crc(data); - - if (type != RFCOMM_UIH) - f = rfcomm_crc_table[f ^ data[2]]; - - return rfcomm_crc_table[f ^ fcs] != 0xcf; -} - -/* ---- L2CAP callbacks ---- */ -static void rfcomm_l2state_change(struct sock *sk) -{ - BT_DBG("%p state %d", sk, sk->sk_state); - rfcomm_schedule(); -} - -static void rfcomm_l2data_ready(struct sock *sk, int bytes) -{ - BT_DBG("%p bytes %d", sk, bytes); - rfcomm_schedule(); -} - -static int rfcomm_l2sock_create(struct socket **sock) -{ - int err; - - BT_DBG(""); - - err = sock_create_kern(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock); - if (!err) { - struct sock *sk = (*sock)->sk; - sk->sk_data_ready = rfcomm_l2data_ready; - sk->sk_state_change = rfcomm_l2state_change; - } - return err; -} - -static inline int rfcomm_check_security(struct rfcomm_dlc *d) -{ - struct sock *sk = d->session->sock->sk; - struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; - - __u8 auth_type; - - switch (d->sec_level) { - case BT_SECURITY_HIGH: - auth_type = HCI_AT_GENERAL_BONDING_MITM; - break; - case BT_SECURITY_MEDIUM: - auth_type = HCI_AT_GENERAL_BONDING; - break; - default: - auth_type = HCI_AT_NO_BONDING; - break; - } - - return hci_conn_security(conn->hcon, d->sec_level, auth_type); -} - -static void rfcomm_session_timeout(unsigned long arg) -{ - struct rfcomm_session *s = (void *) arg; - - BT_DBG("session %p state %ld", s, s->state); - - set_bit(RFCOMM_TIMED_OUT, &s->flags); - rfcomm_schedule(); -} - -static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) -{ - BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); - - if (!mod_timer(&s->timer, jiffies + timeout)) - rfcomm_session_hold(s); -} - -static void rfcomm_session_clear_timer(struct rfcomm_session *s) -{ - BT_DBG("session %p state %ld", s, s->state); - - if (timer_pending(&s->timer) && del_timer(&s->timer)) - rfcomm_session_put(s); -} - -/* ---- RFCOMM DLCs ---- */ -static void rfcomm_dlc_timeout(unsigned long arg) -{ - struct rfcomm_dlc *d = (void *) arg; - - BT_DBG("dlc %p state %ld", d, d->state); - - set_bit(RFCOMM_TIMED_OUT, &d->flags); - rfcomm_dlc_put(d); - rfcomm_schedule(); -} - -static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout) -{ - BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout); - - if (!mod_timer(&d->timer, jiffies + timeout)) - rfcomm_dlc_hold(d); -} - -static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p state %ld", d, d->state); - - if (timer_pending(&d->timer) && del_timer(&d->timer)) - rfcomm_dlc_put(d); -} - -static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d) -{ - BT_DBG("%p", d); - - d->state = BT_OPEN; - d->flags = 0; - d->mscex = 0; - d->sec_level = BT_SECURITY_LOW; - d->mtu = RFCOMM_DEFAULT_MTU; - d->v24_sig = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV; - - d->cfc = RFCOMM_CFC_DISABLED; - d->rx_credits = RFCOMM_DEFAULT_CREDITS; -} - -struct rfcomm_dlc *rfcomm_dlc_alloc(gfp_t prio) -{ - struct rfcomm_dlc *d = kzalloc(sizeof(*d), prio); - - if (!d) - return NULL; - - setup_timer(&d->timer, rfcomm_dlc_timeout, (unsigned long)d); - - skb_queue_head_init(&d->tx_queue); - spin_lock_init(&d->lock); - atomic_set(&d->refcnt, 1); - - rfcomm_dlc_clear_state(d); - - BT_DBG("%p", d); - - return d; -} - -void rfcomm_dlc_free(struct rfcomm_dlc *d) -{ - BT_DBG("%p", d); - - skb_queue_purge(&d->tx_queue); - kfree(d); -} - -static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p session %p", d, s); - - rfcomm_session_hold(s); - - rfcomm_session_clear_timer(s); - rfcomm_dlc_hold(d); - list_add(&d->list, &s->dlcs); - d->session = s; -} - -static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) -{ - struct rfcomm_session *s = d->session; - - BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s); - - list_del(&d->list); - d->session = NULL; - rfcomm_dlc_put(d); - - if (list_empty(&s->dlcs)) - rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); - - rfcomm_session_put(s); -} - -static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_dlc *d; - - list_for_each_entry(d, &s->dlcs, list) - if (d->dlci == dlci) - return d; - - return NULL; -} - -static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) -{ - struct rfcomm_session *s; - int err = 0; - u8 dlci; - - BT_DBG("dlc %p state %ld %s %s channel %d", - d, d->state, batostr(src), batostr(dst), channel); - - if (channel < 1 || channel > 30) - return -EINVAL; - - if (d->state != BT_OPEN && d->state != BT_CLOSED) - return 0; - - s = rfcomm_session_get(src, dst); - if (!s) { - s = rfcomm_session_create(src, dst, d->sec_level, &err); - if (!s) - return err; - } - - dlci = __dlci(!s->initiator, channel); - - /* Check if DLCI already exists */ - if (rfcomm_dlc_get(s, dlci)) - return -EBUSY; - - rfcomm_dlc_clear_state(d); - - d->dlci = dlci; - d->addr = __addr(s->initiator, dlci); - d->priority = 7; - - d->state = BT_CONFIG; - rfcomm_dlc_link(s, d); - - d->out = 1; - - d->mtu = s->mtu; - d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc; - - if (s->state == BT_CONNECTED) { - if (rfcomm_check_security(d)) - rfcomm_send_pn(s, 1, d); - else - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - } - - rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); - - return 0; -} - -int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel) -{ - int r; - - rfcomm_lock(); - - r = __rfcomm_dlc_open(d, src, dst, channel); - - rfcomm_unlock(); - return r; -} - -static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) -{ - struct rfcomm_session *s = d->session; - if (!s) - return 0; - - BT_DBG("dlc %p state %ld dlci %d err %d session %p", - d, d->state, d->dlci, err, s); - - switch (d->state) { - case BT_CONNECT: - case BT_CONFIG: - if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - rfcomm_schedule(); - break; - } - /* Fall through */ - - case BT_CONNECTED: - d->state = BT_DISCONN; - if (skb_queue_empty(&d->tx_queue)) { - rfcomm_send_disc(s, d->dlci); - rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT); - } else { - rfcomm_queue_disc(d); - rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2); - } - break; - - case BT_OPEN: - case BT_CONNECT2: - if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - rfcomm_schedule(); - break; - } - /* Fall through */ - - default: - rfcomm_dlc_clear_timer(d); - - rfcomm_dlc_lock(d); - d->state = BT_CLOSED; - d->state_change(d, err); - rfcomm_dlc_unlock(d); - - skb_queue_purge(&d->tx_queue); - rfcomm_dlc_unlink(d); - } - - return 0; -} - -int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) -{ - int r; - - rfcomm_lock(); - - r = __rfcomm_dlc_close(d, err); - - rfcomm_unlock(); - return r; -} - -int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb) -{ - int len = skb->len; - - if (d->state != BT_CONNECTED) - return -ENOTCONN; - - BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len); - - if (len > d->mtu) - return -EINVAL; - - rfcomm_make_uih(skb, d->addr); - skb_queue_tail(&d->tx_queue, skb); - - if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags)) - rfcomm_schedule(); - return len; -} - -void __rfcomm_dlc_throttle(struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p state %ld", d, d->state); - - if (!d->cfc) { - d->v24_sig |= RFCOMM_V24_FC; - set_bit(RFCOMM_MSC_PENDING, &d->flags); - } - rfcomm_schedule(); -} - -void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d) -{ - BT_DBG("dlc %p state %ld", d, d->state); - - if (!d->cfc) { - d->v24_sig &= ~RFCOMM_V24_FC; - set_bit(RFCOMM_MSC_PENDING, &d->flags); - } - rfcomm_schedule(); -} - -/* - Set/get modem status functions use _local_ status i.e. what we report - to the other side. - Remote status is provided by dlc->modem_status() callback. - */ -int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig) -{ - BT_DBG("dlc %p state %ld v24_sig 0x%x", - d, d->state, v24_sig); - - if (test_bit(RFCOMM_RX_THROTTLED, &d->flags)) - v24_sig |= RFCOMM_V24_FC; - else - v24_sig &= ~RFCOMM_V24_FC; - - d->v24_sig = v24_sig; - - if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags)) - rfcomm_schedule(); - - return 0; -} - -int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig) -{ - BT_DBG("dlc %p state %ld v24_sig 0x%x", - d, d->state, d->v24_sig); - - *v24_sig = d->v24_sig; - return 0; -} - -/* ---- RFCOMM sessions ---- */ -static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) -{ - struct rfcomm_session *s = kzalloc(sizeof(*s), GFP_KERNEL); - - if (!s) - return NULL; - - BT_DBG("session %p sock %p", s, sock); - - setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s); - - INIT_LIST_HEAD(&s->dlcs); - s->state = state; - s->sock = sock; - - s->mtu = RFCOMM_DEFAULT_MTU; - s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN; - - /* Do not increment module usage count for listening sessions. - * Otherwise we won't be able to unload the module. */ - if (state != BT_LISTEN) - if (!try_module_get(THIS_MODULE)) { - kfree(s); - return NULL; - } - - list_add(&s->list, &session_list); - - return s; -} - -static void rfcomm_session_del(struct rfcomm_session *s) -{ - int state = s->state; - - BT_DBG("session %p state %ld", s, s->state); - - list_del(&s->list); - - if (state == BT_CONNECTED) - rfcomm_send_disc(s, 0); - - rfcomm_session_clear_timer(s); - sock_release(s->sock); - kfree(s); - - if (state != BT_LISTEN) - module_put(THIS_MODULE); -} - -static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) -{ - struct rfcomm_session *s; - struct list_head *p, *n; - struct bt_sock *sk; - list_for_each_safe(p, n, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - sk = bt_sk(s->sock->sk); - - if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && - !bacmp(&sk->dst, dst)) - return s; - } - return NULL; -} - -static void rfcomm_session_close(struct rfcomm_session *s, int err) -{ - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("session %p state %ld err %d", s, s->state, err); - - rfcomm_session_hold(s); - - s->state = BT_CLOSED; - - /* Close all dlcs */ - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, err); - } - - rfcomm_session_clear_timer(s); - rfcomm_session_put(s); -} - -static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, - bdaddr_t *dst, - u8 sec_level, - int *err) -{ - struct rfcomm_session *s = NULL; - struct sockaddr_l2 addr; - struct socket *sock; - struct sock *sk; - - BT_DBG("%s %s", batostr(src), batostr(dst)); - - *err = rfcomm_l2sock_create(&sock); - if (*err < 0) - return NULL; - - bacpy(&addr.l2_bdaddr, src); - addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = 0; - addr.l2_cid = 0; - *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); - if (*err < 0) - goto failed; - - /* Set L2CAP options */ - sk = sock->sk; - lock_sock(sk); - l2cap_pi(sk)->chan->imtu = l2cap_mtu; - l2cap_pi(sk)->chan->sec_level = sec_level; - if (l2cap_ertm) - l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM; - release_sock(sk); - - s = rfcomm_session_add(sock, BT_BOUND); - if (!s) { - *err = -ENOMEM; - goto failed; - } - - s->initiator = 1; - - bacpy(&addr.l2_bdaddr, dst); - addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); - addr.l2_cid = 0; - *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK); - if (*err == 0 || *err == -EINPROGRESS) - return s; - - rfcomm_session_del(s); - return NULL; - -failed: - sock_release(sock); - return NULL; -} - -void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst) -{ - struct sock *sk = s->sock->sk; - if (src) - bacpy(src, &bt_sk(sk)->src); - if (dst) - bacpy(dst, &bt_sk(sk)->dst); -} - -/* ---- RFCOMM frame sending ---- */ -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) -{ - struct kvec iv = { data, len }; - struct msghdr msg; - - BT_DBG("session %p len %d", s, len); - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(s->sock, &msg, &iv, 1, len); -} - -static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd) -{ - BT_DBG("%p cmd %u", s, cmd->ctrl); - - return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd)); -} - -static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_SABM, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(!s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_UA, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_DISC, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_queue_disc(struct rfcomm_dlc *d) -{ - struct rfcomm_cmd *cmd; - struct sk_buff *skb; - - BT_DBG("dlc %p dlci %d", d, d->dlci); - - skb = alloc_skb(sizeof(*cmd), GFP_KERNEL); - if (!skb) - return -ENOMEM; - - cmd = (void *) __skb_put(skb, sizeof(*cmd)); - cmd->addr = d->addr; - cmd->ctrl = __ctrl(RFCOMM_DISC, 1); - cmd->len = __len8(0); - cmd->fcs = __fcs2((u8 *) cmd); - - skb_queue_tail(&d->tx_queue, skb); - rfcomm_schedule(); - return 0; -} - -static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_cmd cmd; - - BT_DBG("%p dlci %d", s, dlci); - - cmd.addr = __addr(!s->initiator, dlci); - cmd.ctrl = __ctrl(RFCOMM_DM, 1); - cmd.len = __len8(0); - cmd.fcs = __fcs2((u8 *) &cmd); - - return rfcomm_send_cmd(s, &cmd); -} - -static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d type %d", s, cr, type); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + 1); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_NSC); - mcc->len = __len8(1); - - /* Type that we didn't like */ - *ptr = __mcc_type(cr, type); ptr++; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_pn *pn; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*pn)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_PN); - mcc->len = __len8(sizeof(*pn)); - - pn = (void *) ptr; ptr += sizeof(*pn); - pn->dlci = d->dlci; - pn->priority = d->priority; - pn->ack_timer = 0; - pn->max_retrans = 0; - - if (s->cfc) { - pn->flow_ctrl = cr ? 0xf0 : 0xe0; - pn->credits = RFCOMM_DEFAULT_CREDITS; - } else { - pn->flow_ctrl = 0; - pn->credits = 0; - } - - if (cr && channel_mtu >= 0) - pn->mtu = cpu_to_le16(channel_mtu); - else - pn->mtu = cpu_to_le16(d->mtu); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, - u8 bit_rate, u8 data_bits, u8 stop_bits, - u8 parity, u8 flow_ctrl_settings, - u8 xon_char, u8 xoff_char, u16 param_mask) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_rpn *rpn; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x" - " flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", - s, cr, dlci, bit_rate, data_bits, stop_bits, parity, - flow_ctrl_settings, xon_char, xoff_char, param_mask); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*rpn)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_RPN); - mcc->len = __len8(sizeof(*rpn)); - - rpn = (void *) ptr; ptr += sizeof(*rpn); - rpn->dlci = __addr(1, dlci); - rpn->bit_rate = bit_rate; - rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity); - rpn->flow_ctrl = flow_ctrl_settings; - rpn->xon_char = xon_char; - rpn->xoff_char = xoff_char; - rpn->param_mask = cpu_to_le16(param_mask); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_rls *rls; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d status 0x%x", s, cr, status); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*rls)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_RLS); - mcc->len = __len8(sizeof(*rls)); - - rls = (void *) ptr; ptr += sizeof(*rls); - rls->dlci = __addr(1, dlci); - rls->status = status; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - struct rfcomm_msc *msc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc) + sizeof(*msc)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_MSC); - mcc->len = __len8(sizeof(*msc)); - - msc = (void *) ptr; ptr += sizeof(*msc); - msc->dlci = __addr(1, dlci); - msc->v24_sig = v24_sig | 0x01; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d", s, cr); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_FCOFF); - mcc->len = __len8(0); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) -{ - struct rfcomm_hdr *hdr; - struct rfcomm_mcc *mcc; - u8 buf[16], *ptr = buf; - - BT_DBG("%p cr %d", s, cr); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = __addr(s->initiator, 0); - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - hdr->len = __len8(sizeof(*mcc)); - - mcc = (void *) ptr; ptr += sizeof(*mcc); - mcc->type = __mcc_type(cr, RFCOMM_FCON); - mcc->len = __len8(0); - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) -{ - struct socket *sock = s->sock; - struct kvec iv[3]; - struct msghdr msg; - unsigned char hdr[5], crc[1]; - - if (len > 125) - return -EINVAL; - - BT_DBG("%p cr %d", s, cr); - - hdr[0] = __addr(s->initiator, 0); - hdr[1] = __ctrl(RFCOMM_UIH, 0); - hdr[2] = 0x01 | ((len + 2) << 1); - hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2); - hdr[4] = 0x01 | (len << 1); - - crc[0] = __fcs(hdr); - - iv[0].iov_base = hdr; - iv[0].iov_len = 5; - iv[1].iov_base = pattern; - iv[1].iov_len = len; - iv[2].iov_base = crc; - iv[2].iov_len = 1; - - memset(&msg, 0, sizeof(msg)); - - return kernel_sendmsg(sock, &msg, iv, 3, 6 + len); -} - -static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) -{ - struct rfcomm_hdr *hdr; - u8 buf[16], *ptr = buf; - - BT_DBG("%p addr %d credits %d", s, addr, credits); - - hdr = (void *) ptr; ptr += sizeof(*hdr); - hdr->addr = addr; - hdr->ctrl = __ctrl(RFCOMM_UIH, 1); - hdr->len = __len8(0); - - *ptr = credits; ptr++; - - *ptr = __fcs(buf); ptr++; - - return rfcomm_send_frame(s, buf, ptr - buf); -} - -static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) -{ - struct rfcomm_hdr *hdr; - int len = skb->len; - u8 *crc; - - if (len > 127) { - hdr = (void *) skb_push(skb, 4); - put_unaligned(cpu_to_le16(__len16(len)), (__le16 *) &hdr->len); - } else { - hdr = (void *) skb_push(skb, 3); - hdr->len = __len8(len); - } - hdr->addr = addr; - hdr->ctrl = __ctrl(RFCOMM_UIH, 0); - - crc = skb_put(skb, 1); - *crc = __fcs((void *) hdr); -} - -/* ---- RFCOMM frame reception ---- */ -static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) -{ - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (dlci) { - /* Data channel */ - struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); - if (!d) { - rfcomm_send_dm(s, dlci); - return 0; - } - - switch (d->state) { - case BT_CONNECT: - rfcomm_dlc_clear_timer(d); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECTED; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - - rfcomm_send_msc(s, 1, dlci, d->v24_sig); - break; - - case BT_DISCONN: - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, 0); - - if (list_empty(&s->dlcs)) { - s->state = BT_DISCONN; - rfcomm_send_disc(s, 0); - rfcomm_session_clear_timer(s); - } - - break; - } - } else { - /* Control channel */ - switch (s->state) { - case BT_CONNECT: - s->state = BT_CONNECTED; - rfcomm_process_connect(s); - break; - - case BT_DISCONN: - /* rfcomm_session_put is called later so don't do - * anything here otherwise we will mess up the session - * reference counter: - * - * (a) when we are the initiator dlc_unlink will drive - * the reference counter to 0 (there is no initial put - * after session_add) - * - * (b) when we are not the initiator rfcomm_rx_process - * will explicitly call put to balance the initial hold - * done after session add. - */ - break; - } - } - return 0; -} - -static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) -{ - int err = 0; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (dlci) { - /* Data DLC */ - struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); - if (d) { - if (d->state == BT_CONNECT || d->state == BT_CONFIG) - err = ECONNREFUSED; - else - err = ECONNRESET; - - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, err); - } - } else { - if (s->state == BT_CONNECT) - err = ECONNREFUSED; - else - err = ECONNRESET; - - s->state = BT_CLOSED; - rfcomm_session_close(s, err); - } - return 0; -} - -static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) -{ - int err = 0; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (dlci) { - struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); - if (d) { - rfcomm_send_ua(s, dlci); - - if (d->state == BT_CONNECT || d->state == BT_CONFIG) - err = ECONNREFUSED; - else - err = ECONNRESET; - - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, err); - } else - rfcomm_send_dm(s, dlci); - - } else { - rfcomm_send_ua(s, 0); - - if (s->state == BT_CONNECT) - err = ECONNREFUSED; - else - err = ECONNRESET; - - s->state = BT_CLOSED; - rfcomm_session_close(s, err); - } - - return 0; -} - -void rfcomm_dlc_accept(struct rfcomm_dlc *d) -{ - struct sock *sk = d->session->sock->sk; - struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; - - BT_DBG("dlc %p", d); - - rfcomm_send_ua(d->session, d->dlci); - - rfcomm_dlc_clear_timer(d); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECTED; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - - if (d->role_switch) - hci_conn_switch_role(conn->hcon, 0x00); - - rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); -} - -static void rfcomm_check_accept(struct rfcomm_dlc *d) -{ - if (rfcomm_check_security(d)) { - if (d->defer_setup) { - set_bit(RFCOMM_DEFER_SETUP, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECT2; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - } else - rfcomm_dlc_accept(d); - } else { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } -} - -static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci) -{ - struct rfcomm_dlc *d; - u8 channel; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (!dlci) { - rfcomm_send_ua(s, 0); - - if (s->state == BT_OPEN) { - s->state = BT_CONNECTED; - rfcomm_process_connect(s); - } - return 0; - } - - /* Check if DLC exists */ - d = rfcomm_dlc_get(s, dlci); - if (d) { - if (d->state == BT_OPEN) { - /* DLC was previously opened by PN request */ - rfcomm_check_accept(d); - } - return 0; - } - - /* Notify socket layer about incoming connection */ - channel = __srv_channel(dlci); - if (rfcomm_connect_ind(s, channel, &d)) { - d->dlci = dlci; - d->addr = __addr(s->initiator, dlci); - rfcomm_dlc_link(s, d); - - rfcomm_check_accept(d); - } else { - rfcomm_send_dm(s, dlci); - } - - return 0; -} - -static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn) -{ - struct rfcomm_session *s = d->session; - - BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", - d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); - - if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) || - pn->flow_ctrl == 0xe0) { - d->cfc = RFCOMM_CFC_ENABLED; - d->tx_credits = pn->credits; - } else { - d->cfc = RFCOMM_CFC_DISABLED; - set_bit(RFCOMM_TX_THROTTLED, &d->flags); - } - - if (s->cfc == RFCOMM_CFC_UNKNOWN) - s->cfc = d->cfc; - - d->priority = pn->priority; - - d->mtu = __le16_to_cpu(pn->mtu); - - if (cr && d->mtu > s->mtu) - d->mtu = s->mtu; - - return 0; -} - -static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb) -{ - struct rfcomm_pn *pn = (void *) skb->data; - struct rfcomm_dlc *d; - u8 dlci = pn->dlci; - - BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); - - if (!dlci) - return 0; - - d = rfcomm_dlc_get(s, dlci); - if (d) { - if (cr) { - /* PN request */ - rfcomm_apply_pn(d, cr, pn); - rfcomm_send_pn(s, 0, d); - } else { - /* PN response */ - switch (d->state) { - case BT_CONFIG: - rfcomm_apply_pn(d, cr, pn); - - d->state = BT_CONNECT; - rfcomm_send_sabm(s, d->dlci); - break; - } - } - } else { - u8 channel = __srv_channel(dlci); - - if (!cr) - return 0; - - /* PN request for non existing DLC. - * Assume incoming connection. */ - if (rfcomm_connect_ind(s, channel, &d)) { - d->dlci = dlci; - d->addr = __addr(s->initiator, dlci); - rfcomm_dlc_link(s, d); - - rfcomm_apply_pn(d, cr, pn); - - d->state = BT_OPEN; - rfcomm_send_pn(s, 0, d); - } else { - rfcomm_send_dm(s, dlci); - } - } - return 0; -} - -static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb) -{ - struct rfcomm_rpn *rpn = (void *) skb->data; - u8 dlci = __get_dlci(rpn->dlci); - - u8 bit_rate = 0; - u8 data_bits = 0; - u8 stop_bits = 0; - u8 parity = 0; - u8 flow_ctrl = 0; - u8 xon_char = 0; - u8 xoff_char = 0; - u16 rpn_mask = RFCOMM_RPN_PM_ALL; - - BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", - dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl, - rpn->xon_char, rpn->xoff_char, rpn->param_mask); - - if (!cr) - return 0; - - if (len == 1) { - /* This is a request, return default (according to ETSI TS 07.10) settings */ - bit_rate = RFCOMM_RPN_BR_9600; - data_bits = RFCOMM_RPN_DATA_8; - stop_bits = RFCOMM_RPN_STOP_1; - parity = RFCOMM_RPN_PARITY_NONE; - flow_ctrl = RFCOMM_RPN_FLOW_NONE; - xon_char = RFCOMM_RPN_XON_CHAR; - xoff_char = RFCOMM_RPN_XOFF_CHAR; - goto rpn_out; - } - - /* Check for sane values, ignore/accept bit_rate, 8 bits, 1 stop bit, - * no parity, no flow control lines, normal XON/XOFF chars */ - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_BITRATE)) { - bit_rate = rpn->bit_rate; - if (bit_rate > RFCOMM_RPN_BR_230400) { - BT_DBG("RPN bit rate mismatch 0x%x", bit_rate); - bit_rate = RFCOMM_RPN_BR_9600; - rpn_mask ^= RFCOMM_RPN_PM_BITRATE; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_DATA)) { - data_bits = __get_rpn_data_bits(rpn->line_settings); - if (data_bits != RFCOMM_RPN_DATA_8) { - BT_DBG("RPN data bits mismatch 0x%x", data_bits); - data_bits = RFCOMM_RPN_DATA_8; - rpn_mask ^= RFCOMM_RPN_PM_DATA; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_STOP)) { - stop_bits = __get_rpn_stop_bits(rpn->line_settings); - if (stop_bits != RFCOMM_RPN_STOP_1) { - BT_DBG("RPN stop bits mismatch 0x%x", stop_bits); - stop_bits = RFCOMM_RPN_STOP_1; - rpn_mask ^= RFCOMM_RPN_PM_STOP; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_PARITY)) { - parity = __get_rpn_parity(rpn->line_settings); - if (parity != RFCOMM_RPN_PARITY_NONE) { - BT_DBG("RPN parity mismatch 0x%x", parity); - parity = RFCOMM_RPN_PARITY_NONE; - rpn_mask ^= RFCOMM_RPN_PM_PARITY; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_FLOW)) { - flow_ctrl = rpn->flow_ctrl; - if (flow_ctrl != RFCOMM_RPN_FLOW_NONE) { - BT_DBG("RPN flow ctrl mismatch 0x%x", flow_ctrl); - flow_ctrl = RFCOMM_RPN_FLOW_NONE; - rpn_mask ^= RFCOMM_RPN_PM_FLOW; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XON)) { - xon_char = rpn->xon_char; - if (xon_char != RFCOMM_RPN_XON_CHAR) { - BT_DBG("RPN XON char mismatch 0x%x", xon_char); - xon_char = RFCOMM_RPN_XON_CHAR; - rpn_mask ^= RFCOMM_RPN_PM_XON; - } - } - - if (rpn->param_mask & cpu_to_le16(RFCOMM_RPN_PM_XOFF)) { - xoff_char = rpn->xoff_char; - if (xoff_char != RFCOMM_RPN_XOFF_CHAR) { - BT_DBG("RPN XOFF char mismatch 0x%x", xoff_char); - xoff_char = RFCOMM_RPN_XOFF_CHAR; - rpn_mask ^= RFCOMM_RPN_PM_XOFF; - } - } - -rpn_out: - rfcomm_send_rpn(s, 0, dlci, bit_rate, data_bits, stop_bits, - parity, flow_ctrl, xon_char, xoff_char, rpn_mask); - - return 0; -} - -static int rfcomm_recv_rls(struct rfcomm_session *s, int cr, struct sk_buff *skb) -{ - struct rfcomm_rls *rls = (void *) skb->data; - u8 dlci = __get_dlci(rls->dlci); - - BT_DBG("dlci %d cr %d status 0x%x", dlci, cr, rls->status); - - if (!cr) - return 0; - - /* We should probably do something with this information here. But - * for now it's sufficient just to reply -- Bluetooth 1.1 says it's - * mandatory to recognise and respond to RLS */ - - rfcomm_send_rls(s, 0, dlci, rls->status); - - return 0; -} - -static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb) -{ - struct rfcomm_msc *msc = (void *) skb->data; - struct rfcomm_dlc *d; - u8 dlci = __get_dlci(msc->dlci); - - BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig); - - d = rfcomm_dlc_get(s, dlci); - if (!d) - return 0; - - if (cr) { - if (msc->v24_sig & RFCOMM_V24_FC && !d->cfc) - set_bit(RFCOMM_TX_THROTTLED, &d->flags); - else - clear_bit(RFCOMM_TX_THROTTLED, &d->flags); - - rfcomm_dlc_lock(d); - - d->remote_v24_sig = msc->v24_sig; - - if (d->modem_status) - d->modem_status(d, msc->v24_sig); - - rfcomm_dlc_unlock(d); - - rfcomm_send_msc(s, 0, dlci, msc->v24_sig); - - d->mscex |= RFCOMM_MSCEX_RX; - } else - d->mscex |= RFCOMM_MSCEX_TX; - - return 0; -} - -static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb) -{ - struct rfcomm_mcc *mcc = (void *) skb->data; - u8 type, cr, len; - - cr = __test_cr(mcc->type); - type = __get_mcc_type(mcc->type); - len = __get_mcc_len(mcc->len); - - BT_DBG("%p type 0x%x cr %d", s, type, cr); - - skb_pull(skb, 2); - - switch (type) { - case RFCOMM_PN: - rfcomm_recv_pn(s, cr, skb); - break; - - case RFCOMM_RPN: - rfcomm_recv_rpn(s, cr, len, skb); - break; - - case RFCOMM_RLS: - rfcomm_recv_rls(s, cr, skb); - break; - - case RFCOMM_MSC: - rfcomm_recv_msc(s, cr, skb); - break; - - case RFCOMM_FCOFF: - if (cr) { - set_bit(RFCOMM_TX_THROTTLED, &s->flags); - rfcomm_send_fcoff(s, 0); - } - break; - - case RFCOMM_FCON: - if (cr) { - clear_bit(RFCOMM_TX_THROTTLED, &s->flags); - rfcomm_send_fcon(s, 0); - } - break; - - case RFCOMM_TEST: - if (cr) - rfcomm_send_test(s, 0, skb->data, skb->len); - break; - - case RFCOMM_NSC: - break; - - default: - BT_ERR("Unknown control type 0x%02x", type); - rfcomm_send_nsc(s, cr, type); - break; - } - return 0; -} - -static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb) -{ - struct rfcomm_dlc *d; - - BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf); - - d = rfcomm_dlc_get(s, dlci); - if (!d) { - rfcomm_send_dm(s, dlci); - goto drop; - } - - if (pf && d->cfc) { - u8 credits = *(u8 *) skb->data; skb_pull(skb, 1); - - d->tx_credits += credits; - if (d->tx_credits) - clear_bit(RFCOMM_TX_THROTTLED, &d->flags); - } - - if (skb->len && d->state == BT_CONNECTED) { - rfcomm_dlc_lock(d); - d->rx_credits--; - d->data_ready(d, skb); - rfcomm_dlc_unlock(d); - return 0; - } - -drop: - kfree_skb(skb); - return 0; -} - -static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) -{ - struct rfcomm_hdr *hdr = (void *) skb->data; - u8 type, dlci, fcs; - - dlci = __get_dlci(hdr->addr); - type = __get_type(hdr->ctrl); - - /* Trim FCS */ - skb->len--; skb->tail--; - fcs = *(u8 *)skb_tail_pointer(skb); - - if (__check_fcs(skb->data, type, fcs)) { - BT_ERR("bad checksum in packet"); - kfree_skb(skb); - return -EILSEQ; - } - - if (__test_ea(hdr->len)) - skb_pull(skb, 3); - else - skb_pull(skb, 4); - - switch (type) { - case RFCOMM_SABM: - if (__test_pf(hdr->ctrl)) - rfcomm_recv_sabm(s, dlci); - break; - - case RFCOMM_DISC: - if (__test_pf(hdr->ctrl)) - rfcomm_recv_disc(s, dlci); - break; - - case RFCOMM_UA: - if (__test_pf(hdr->ctrl)) - rfcomm_recv_ua(s, dlci); - break; - - case RFCOMM_DM: - rfcomm_recv_dm(s, dlci); - break; - - case RFCOMM_UIH: - if (dlci) - return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); - - rfcomm_recv_mcc(s, skb); - break; - - default: - BT_ERR("Unknown packet type 0x%02x", type); - break; - } - kfree_skb(skb); - return 0; -} - -/* ---- Connection and data processing ---- */ - -static void rfcomm_process_connect(struct rfcomm_session *s) -{ - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("session %p state %ld", s, s->state); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - if (d->state == BT_CONFIG) { - d->mtu = s->mtu; - if (rfcomm_check_security(d)) { - rfcomm_send_pn(s, 1, d); - } else { - set_bit(RFCOMM_AUTH_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - } - } - } -} - -/* Send data queued for the DLC. - * Return number of frames left in the queue. - */ -static inline int rfcomm_process_tx(struct rfcomm_dlc *d) -{ - struct sk_buff *skb; - int err; - - BT_DBG("dlc %p state %ld cfc %d rx_credits %d tx_credits %d", - d, d->state, d->cfc, d->rx_credits, d->tx_credits); - - /* Send pending MSC */ - if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags)) - rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); - - if (d->cfc) { - /* CFC enabled. - * Give them some credits */ - if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) && - d->rx_credits <= (d->cfc >> 2)) { - rfcomm_send_credits(d->session, d->addr, d->cfc - d->rx_credits); - d->rx_credits = d->cfc; - } - } else { - /* CFC disabled. - * Give ourselves some credits */ - d->tx_credits = 5; - } - - if (test_bit(RFCOMM_TX_THROTTLED, &d->flags)) - return skb_queue_len(&d->tx_queue); - - while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { - err = rfcomm_send_frame(d->session, skb->data, skb->len); - if (err < 0) { - skb_queue_head(&d->tx_queue, skb); - break; - } - kfree_skb(skb); - d->tx_credits--; - } - - if (d->cfc && !d->tx_credits) { - /* We're out of TX credits. - * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */ - set_bit(RFCOMM_TX_THROTTLED, &d->flags); - } - - return skb_queue_len(&d->tx_queue); -} - -static inline void rfcomm_process_dlcs(struct rfcomm_session *s) -{ - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("session %p state %ld", s, s->state); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - - if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) { - __rfcomm_dlc_close(d, ETIMEDOUT); - continue; - } - - if (test_bit(RFCOMM_ENC_DROP, &d->flags)) { - __rfcomm_dlc_close(d, ECONNREFUSED); - continue; - } - - if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) { - rfcomm_dlc_clear_timer(d); - if (d->out) { - rfcomm_send_pn(s, 1, d); - rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT); - } else { - if (d->defer_setup) { - set_bit(RFCOMM_DEFER_SETUP, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - - rfcomm_dlc_lock(d); - d->state = BT_CONNECT2; - d->state_change(d, 0); - rfcomm_dlc_unlock(d); - } else - rfcomm_dlc_accept(d); - } - continue; - } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) { - rfcomm_dlc_clear_timer(d); - if (!d->out) - rfcomm_send_dm(s, d->dlci); - else - d->state = BT_CLOSED; - __rfcomm_dlc_close(d, ECONNREFUSED); - continue; - } - - if (test_bit(RFCOMM_SEC_PENDING, &d->flags)) - continue; - - if (test_bit(RFCOMM_TX_THROTTLED, &s->flags)) - continue; - - if ((d->state == BT_CONNECTED || d->state == BT_DISCONN) && - d->mscex == RFCOMM_MSCEX_OK) - rfcomm_process_tx(d); - } -} - -static inline void rfcomm_process_rx(struct rfcomm_session *s) -{ - struct socket *sock = s->sock; - struct sock *sk = sock->sk; - struct sk_buff *skb; - - BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->sk_receive_queue)); - - /* Get data directly from socket receive queue without copying it. */ - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - if (!skb_linearize(skb)) - rfcomm_recv_frame(s, skb); - else - kfree_skb(skb); - } - - if (sk->sk_state == BT_CLOSED) { - if (!s->initiator) - rfcomm_session_put(s); - - rfcomm_session_close(s, sk->sk_err); - } -} - -static inline void rfcomm_accept_connection(struct rfcomm_session *s) -{ - struct socket *sock = s->sock, *nsock; - int err; - - /* Fast check for a new connection. - * Avoids unnesesary socket allocations. */ - if (list_empty(&bt_sk(sock->sk)->accept_q)) - return; - - BT_DBG("session %p", s); - - err = kernel_accept(sock, &nsock, O_NONBLOCK); - if (err < 0) - return; - - /* Set our callbacks */ - nsock->sk->sk_data_ready = rfcomm_l2data_ready; - nsock->sk->sk_state_change = rfcomm_l2state_change; - - s = rfcomm_session_add(nsock, BT_OPEN); - if (s) { - rfcomm_session_hold(s); - - /* We should adjust MTU on incoming sessions. - * L2CAP MTU minus UIH header and FCS. */ - s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, - l2cap_pi(nsock->sk)->chan->imtu) - 5; - - rfcomm_schedule(); - } else - sock_release(nsock); -} - -static inline void rfcomm_check_connection(struct rfcomm_session *s) -{ - struct sock *sk = s->sock->sk; - - BT_DBG("%p state %ld", s, s->state); - - switch (sk->sk_state) { - case BT_CONNECTED: - s->state = BT_CONNECT; - - /* We can adjust MTU on outgoing sessions. - * L2CAP MTU minus UIH header and FCS. */ - s->mtu = min(l2cap_pi(sk)->chan->omtu, l2cap_pi(sk)->chan->imtu) - 5; - - rfcomm_send_sabm(s, 0); - break; - - case BT_CLOSED: - s->state = BT_CLOSED; - rfcomm_session_close(s, sk->sk_err); - break; - } -} - -static inline void rfcomm_process_sessions(void) -{ - struct list_head *p, *n; - - rfcomm_lock(); - - list_for_each_safe(p, n, &session_list) { - struct rfcomm_session *s; - s = list_entry(p, struct rfcomm_session, list); - - if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { - s->state = BT_DISCONN; - rfcomm_send_disc(s, 0); - rfcomm_session_put(s); - continue; - } - - if (s->state == BT_LISTEN) { - rfcomm_accept_connection(s); - continue; - } - - rfcomm_session_hold(s); - - switch (s->state) { - case BT_BOUND: - rfcomm_check_connection(s); - break; - - default: - rfcomm_process_rx(s); - break; - } - - rfcomm_process_dlcs(s); - - rfcomm_session_put(s); - } - - rfcomm_unlock(); -} - -static int rfcomm_add_listener(bdaddr_t *ba) -{ - struct sockaddr_l2 addr; - struct socket *sock; - struct sock *sk; - struct rfcomm_session *s; - int err = 0; - - /* Create socket */ - err = rfcomm_l2sock_create(&sock); - if (err < 0) { - BT_ERR("Create socket failed %d", err); - return err; - } - - /* Bind socket */ - bacpy(&addr.l2_bdaddr, ba); - addr.l2_family = AF_BLUETOOTH; - addr.l2_psm = cpu_to_le16(RFCOMM_PSM); - addr.l2_cid = 0; - err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr)); - if (err < 0) { - BT_ERR("Bind failed %d", err); - goto failed; - } - - /* Set L2CAP options */ - sk = sock->sk; - lock_sock(sk); - l2cap_pi(sk)->chan->imtu = l2cap_mtu; - release_sock(sk); - - /* Start listening on the socket */ - err = kernel_listen(sock, 10); - if (err) { - BT_ERR("Listen failed %d", err); - goto failed; - } - - /* Add listening session */ - s = rfcomm_session_add(sock, BT_LISTEN); - if (!s) - goto failed; - - rfcomm_session_hold(s); - return 0; -failed: - sock_release(sock); - return err; -} - -static void rfcomm_kill_listener(void) -{ - struct rfcomm_session *s; - struct list_head *p, *n; - - BT_DBG(""); - - list_for_each_safe(p, n, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - rfcomm_session_del(s); - } -} - -static int rfcomm_run(void *unused) -{ - BT_DBG(""); - - set_user_nice(current, -10); - - rfcomm_add_listener(BDADDR_ANY); - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (kthread_should_stop()) - break; - - /* Process stuff */ - rfcomm_process_sessions(); - - schedule(); - } - __set_current_state(TASK_RUNNING); - - rfcomm_kill_listener(); - - return 0; -} - -static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) -{ - struct rfcomm_session *s; - struct rfcomm_dlc *d; - struct list_head *p, *n; - - BT_DBG("conn %p status 0x%02x encrypt 0x%02x", conn, status, encrypt); - - s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst); - if (!s) - return; - - rfcomm_session_hold(s); - - list_for_each_safe(p, n, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); - - if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { - rfcomm_dlc_clear_timer(d); - if (status || encrypt == 0x00) { - set_bit(RFCOMM_ENC_DROP, &d->flags); - continue; - } - } - - if (d->state == BT_CONNECTED && !status && encrypt == 0x00) { - if (d->sec_level == BT_SECURITY_MEDIUM) { - set_bit(RFCOMM_SEC_PENDING, &d->flags); - rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); - continue; - } else if (d->sec_level == BT_SECURITY_HIGH) { - set_bit(RFCOMM_ENC_DROP, &d->flags); - continue; - } - } - - if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags)) - continue; - - if (!status && hci_conn_check_secure(conn, d->sec_level)) - set_bit(RFCOMM_AUTH_ACCEPT, &d->flags); - else - set_bit(RFCOMM_AUTH_REJECT, &d->flags); - } - - rfcomm_session_put(s); - - rfcomm_schedule(); -} - -static struct hci_cb rfcomm_cb = { - .name = "RFCOMM", - .security_cfm = rfcomm_security_cfm -}; - -static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) -{ - struct rfcomm_session *s; - - rfcomm_lock(); - - list_for_each_entry(s, &session_list, list) { - struct rfcomm_dlc *d; - list_for_each_entry(d, &s->dlcs, list) { - struct sock *sk = s->sock->sk; - - seq_printf(f, "%s %s %ld %d %d %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - d->state, d->dlci, d->mtu, - d->rx_credits, d->tx_credits); - } - } - - rfcomm_unlock(); - - return 0; -} - -static int rfcomm_dlc_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, rfcomm_dlc_debugfs_show, inode->i_private); -} - -static const struct file_operations rfcomm_dlc_debugfs_fops = { - .open = rfcomm_dlc_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *rfcomm_dlc_debugfs; - -/* ---- Initialization ---- */ -static int __init rfcomm_init(void) -{ - int err; - - hci_register_cb(&rfcomm_cb); - - rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); - if (IS_ERR(rfcomm_thread)) { - err = PTR_ERR(rfcomm_thread); - goto unregister; - } - - if (bt_debugfs) { - rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, - bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops); - if (!rfcomm_dlc_debugfs) - BT_ERR("Failed to create RFCOMM debug file"); - } - - err = rfcomm_init_ttys(); - if (err < 0) - goto stop; - - err = rfcomm_init_sockets(); - if (err < 0) - goto cleanup; - - BT_INFO("RFCOMM ver %s", VERSION); - - return 0; - -cleanup: - rfcomm_cleanup_ttys(); - -stop: - kthread_stop(rfcomm_thread); - -unregister: - hci_unregister_cb(&rfcomm_cb); - - return err; -} - -static void __exit rfcomm_exit(void) -{ - debugfs_remove(rfcomm_dlc_debugfs); - - hci_unregister_cb(&rfcomm_cb); - - kthread_stop(rfcomm_thread); - - rfcomm_cleanup_ttys(); - - rfcomm_cleanup_sockets(); -} - -module_init(rfcomm_init); -module_exit(rfcomm_exit); - -module_param(disable_cfc, bool, 0644); -MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); - -module_param(channel_mtu, int, 0644); -MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); - -module_param(l2cap_mtu, uint, 0644); -MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); - -module_param(l2cap_ertm, bool, 0644); -MODULE_PARM_DESC(l2cap_ertm, "Use L2CAP ERTM mode for connection"); - -MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); -MODULE_DESCRIPTION("Bluetooth RFCOMM ver " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("bt-proto-3"); diff --git a/net/bluetooth_tizen/rfcomm/sock.c b/net/bluetooth_tizen/rfcomm/sock.c deleted file mode 100644 index 22169c3..0000000 --- a/net/bluetooth_tizen/rfcomm/sock.c +++ /dev/null @@ -1,1078 +0,0 @@ -/* - RFCOMM implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* - * RFCOMM sockets. - */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/socket.h> -#include <linux/skbuff.h> -#include <linux/list.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/security.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/rfcomm.h> - -static const struct proto_ops rfcomm_sock_ops; - -static struct bt_sock_list rfcomm_sk_list = { - .lock = __RW_LOCK_UNLOCKED(rfcomm_sk_list.lock) -}; - -static void rfcomm_sock_close(struct sock *sk); -static void rfcomm_sock_kill(struct sock *sk); - -/* ---- DLC callbacks ---- - * - * called under rfcomm_dlc_lock() - */ -static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb) -{ - struct sock *sk = d->owner; - if (!sk) - return; - - atomic_add(skb->len, &sk->sk_rmem_alloc); - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_data_ready(sk, skb->len); - - if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf) - rfcomm_dlc_throttle(d); -} - -static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) -{ - struct sock *sk = d->owner, *parent; - unsigned long flags; - - if (!sk) - return; - - BT_DBG("dlc %p state %ld err %d", d, d->state, err); - - local_irq_save(flags); - bh_lock_sock(sk); - - if (err) - sk->sk_err = err; - - sk->sk_state = d->state; - - parent = bt_sk(sk)->parent; - if (parent) { - if (d->state == BT_CLOSED) { - sock_set_flag(sk, SOCK_ZAPPED); - bt_accept_unlink(sk); - } - parent->sk_data_ready(parent, 0); - } else { - if (d->state == BT_CONNECTED) - rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL); - sk->sk_state_change(sk); - } - - bh_unlock_sock(sk); - local_irq_restore(flags); - - if (parent && sock_flag(sk, SOCK_ZAPPED)) { - /* We have to drop DLC lock here, otherwise - * rfcomm_sock_destruct() will dead lock. */ - rfcomm_dlc_unlock(d); - rfcomm_sock_kill(sk); - rfcomm_dlc_lock(d); - } -} - -/* ---- Socket functions ---- */ -static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src) -{ - struct sock *sk = NULL; - struct hlist_node *node; - - sk_for_each(sk, node, &rfcomm_sk_list.head) { - if (rfcomm_pi(sk)->channel == channel && - !bacmp(&bt_sk(sk)->src, src)) - break; - } - - return node ? sk : NULL; -} - -/* Find socket with channel and source bdaddr. - * Returns closest match. - */ -static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src) -{ - struct sock *sk = NULL, *sk1 = NULL; - struct hlist_node *node; - - read_lock(&rfcomm_sk_list.lock); - - sk_for_each(sk, node, &rfcomm_sk_list.head) { - if (state && sk->sk_state != state) - continue; - - if (rfcomm_pi(sk)->channel == channel) { - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) - break; - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - sk1 = sk; - } - } - - read_unlock(&rfcomm_sk_list.lock); - - return node ? sk : sk1; -} - -static void rfcomm_sock_destruct(struct sock *sk) -{ - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - - BT_DBG("sk %p dlc %p", sk, d); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); - - rfcomm_dlc_lock(d); - rfcomm_pi(sk)->dlc = NULL; - - /* Detach DLC if it's owned by this socket */ - if (d->owner == sk) - d->owner = NULL; - rfcomm_dlc_unlock(d); - - rfcomm_dlc_put(d); -} - -static void rfcomm_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted dlcs */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - rfcomm_sock_close(sk); - rfcomm_sock_kill(sk); - } - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -static void rfcomm_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) - return; - - BT_DBG("sk %p state %d refcnt %d", sk, sk->sk_state, atomic_read(&sk->sk_refcnt)); - - /* Kill poor orphan */ - bt_sock_unlink(&rfcomm_sk_list, sk); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -static void __rfcomm_sock_close(struct sock *sk) -{ - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - rfcomm_sock_cleanup_listen(sk); - break; - - case BT_CONNECT: - case BT_CONNECT2: - case BT_CONFIG: - case BT_CONNECTED: - rfcomm_dlc_close(d, 0); - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - -/* Close socket. - * Must be called on unlocked socket. - */ -static void rfcomm_sock_close(struct sock *sk) -{ - lock_sock(sk); - __rfcomm_sock_close(sk); - release_sock(sk); -} - -static void rfcomm_sock_init(struct sock *sk, struct sock *parent) -{ - struct rfcomm_pinfo *pi = rfcomm_pi(sk); - - BT_DBG("sk %p", sk); - - if (parent) { - sk->sk_type = parent->sk_type; - pi->dlc->defer_setup = bt_sk(parent)->defer_setup; - - pi->sec_level = rfcomm_pi(parent)->sec_level; - pi->role_switch = rfcomm_pi(parent)->role_switch; - - security_sk_clone(parent, sk); - } else { - pi->dlc->defer_setup = 0; - - pi->sec_level = BT_SECURITY_LOW; - pi->role_switch = 0; - } - - pi->dlc->sec_level = pi->sec_level; - pi->dlc->role_switch = pi->role_switch; -} - -static struct proto rfcomm_proto = { - .name = "RFCOMM", - .owner = THIS_MODULE, - .obj_size = sizeof(struct rfcomm_pinfo) -}; - -static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct rfcomm_dlc *d; - struct sock *sk; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - d = rfcomm_dlc_alloc(prio); - if (!d) { - sk_free(sk); - return NULL; - } - - d->data_ready = rfcomm_sk_data_ready; - d->state_change = rfcomm_sk_state_change; - - rfcomm_pi(sk)->dlc = d; - d->owner = sk; - - sk->sk_destruct = rfcomm_sock_destruct; - sk->sk_sndtimeo = RFCOMM_CONN_TIMEOUT; - - sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; - sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - bt_sock_link(&rfcomm_sk_list, sk); - - BT_DBG("sk %p", sk); - return sk; -} - -static int rfcomm_sock_create(struct net *net, struct socket *sock, - int protocol, int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW) - return -ESOCKTNOSUPPORT; - - sock->ops = &rfcomm_sock_ops; - - sk = rfcomm_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - rfcomm_sock_init(sk, NULL); - return 0; -} - -static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -{ - struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr)); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - write_lock(&rfcomm_sk_list.lock); - - if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr); - rfcomm_pi(sk)->channel = sa->rc_channel; - sk->sk_state = BT_BOUND; - } - - write_unlock(&rfcomm_sk_list.lock); - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; - struct sock *sk = sock->sk; - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - int err = 0; - - BT_DBG("sk %p", sk); - - if (alen < sizeof(struct sockaddr_rc) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - sk->sk_state = BT_CONNECT; - bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); - rfcomm_pi(sk)->channel = sa->rc_channel; - - d->sec_level = rfcomm_pi(sk)->sec_level; - d->role_switch = rfcomm_pi(sk)->role_switch; - - err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); - if (!err) - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if (sk->sk_state != BT_BOUND) { - err = -EBADFD; - goto done; - } - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - if (!rfcomm_pi(sk)->channel) { - bdaddr_t *src = &bt_sk(sk)->src; - u8 channel; - - err = -EINVAL; - - write_lock(&rfcomm_sk_list.lock); - - for (channel = 1; channel < 31; channel++) - if (!__rfcomm_get_sock_by_addr(channel, src)) { - rfcomm_pi(sk)->channel = channel; - err = 0; - break; - } - - write_unlock(&rfcomm_sk_list.lock); - - if (err < 0) - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *nsk; - long timeo; - int err = 0; - - lock_sock(sk); - - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - goto done; - } - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - nsk = bt_accept_dequeue(sk, newsock); - if (nsk) - break; - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", nsk); - -done: - release_sock(sk); - return err; -} - -static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_rc *sa = (struct sockaddr_rc *) addr; - struct sock *sk = sock->sk; - - BT_DBG("sock %p, sk %p", sock, sk); - - sa->rc_family = AF_BLUETOOTH; - sa->rc_channel = rfcomm_pi(sk)->channel; - if (peer) - bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst); - else - bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src); - - *len = sizeof(struct sockaddr_rc); - return 0; -} - -static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - struct sk_buff *skb; - int sent = 0; - - if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) - return -ENOTCONN; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - if (sk->sk_shutdown & SEND_SHUTDOWN) - return -EPIPE; - - BT_DBG("sock %p, sk %p", sock, sk); - - lock_sock(sk); - - while (len) { - size_t size = min_t(size_t, len, d->mtu); - int err; - - skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) { - if (sent == 0) - sent = err; - break; - } - skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); - - err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); - if (err) { - kfree_skb(skb); - if (sent == 0) - sent = err; - break; - } - - skb->priority = sk->sk_priority; - - err = rfcomm_dlc_send(d, skb); - if (err < 0) { - kfree_skb(skb); - if (sent == 0) - sent = err; - break; - } - - sent += size; - len -= size; - } - - release_sock(sk); - - return sent; -} - -static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; - int len; - - if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) { - rfcomm_dlc_accept(d); - return 0; - } - - len = bt_sock_stream_recvmsg(iocb, sock, msg, size, flags); - - lock_sock(sk); - if (!(flags & MSG_PEEK) && len > 0) - atomic_sub(len, &sk->sk_rmem_alloc); - - if (atomic_read(&sk->sk_rmem_alloc) <= (sk->sk_rcvbuf >> 2)) - rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc); - release_sock(sk); - - return len; -} - -static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - int err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - case RFCOMM_LM: - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - if (opt & RFCOMM_LM_AUTH) - rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW; - if (opt & RFCOMM_LM_ENCRYPT) - rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM; - if (opt & RFCOMM_LM_SECURE) - rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH; - - rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER); - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int err = 0; - size_t len; - u32 opt; - - BT_DBG("sk %p", sk); - - if (level == SOL_RFCOMM) - return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - break; - } - - sec.level = BT_SECURITY_LOW; - - len = min_t(unsigned int, sizeof(sec), optlen); - if (copy_from_user((char *) &sec, optval, len)) { - err = -EFAULT; - break; - } - - if (sec.level > BT_SECURITY_HIGH) { - err = -EINVAL; - break; - } - - rfcomm_pi(sk)->sec_level = sec.level; - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (get_user(opt, (u32 __user *) optval)) { - err = -EFAULT; - break; - } - - bt_sk(sk)->defer_setup = opt; - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct rfcomm_conninfo cinfo; - struct l2cap_conn *conn = l2cap_pi(sk)->chan->conn; - int len, err = 0; - u32 opt; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case RFCOMM_LM: - switch (rfcomm_pi(sk)->sec_level) { - case BT_SECURITY_LOW: - opt = RFCOMM_LM_AUTH; - break; - case BT_SECURITY_MEDIUM: - opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT; - break; - case BT_SECURITY_HIGH: - opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT | - RFCOMM_LM_SECURE; - break; - default: - opt = 0; - break; - } - - if (rfcomm_pi(sk)->role_switch) - opt |= RFCOMM_LM_MASTER; - - if (put_user(opt, (u32 __user *) optval)) - err = -EFAULT; - break; - - case RFCOMM_CONNINFO: - if (sk->sk_state != BT_CONNECTED && - !rfcomm_pi(sk)->dlc->defer_setup) { - err = -ENOTCONN; - break; - } - - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = conn->hcon->handle; - memcpy(cinfo.dev_class, conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *) &cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct bt_security sec; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_RFCOMM) - return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen); - - if (level != SOL_BLUETOOTH) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case BT_SECURITY: - if (sk->sk_type != SOCK_STREAM) { - err = -EINVAL; - break; - } - - sec.level = rfcomm_pi(sk)->sec_level; - - len = min_t(unsigned int, len, sizeof(sec)); - if (copy_to_user(optval, (char *) &sec, len)) - err = -EFAULT; - - break; - - case BT_DEFER_SETUP: - if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) { - err = -EINVAL; - break; - } - - if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk __maybe_unused = sock->sk; - int err; - - BT_DBG("sk %p cmd %x arg %lx", sk, cmd, arg); - - err = bt_sock_ioctl(sock, cmd, arg); - - if (err == -ENOIOCTLCMD) { -#ifdef CONFIG_BT_RFCOMM_TTY - lock_sock(sk); - err = rfcomm_dev_ioctl(sk, cmd, (void __user *) arg); - release_sock(sk); -#else - err = -EOPNOTSUPP; -#endif - } - - return err; -} - -static int rfcomm_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - lock_sock(sk); - if (!sk->sk_shutdown) { - sk->sk_shutdown = SHUTDOWN_MASK; - __rfcomm_sock_close(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); - } - release_sock(sk); - return err; -} - -static int rfcomm_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - err = rfcomm_sock_shutdown(sock, 2); - - sock_orphan(sk); - rfcomm_sock_kill(sk); - return err; -} - -/* ---- RFCOMM core layer callbacks ---- - * - * called under rfcomm_lock() - */ -int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d) -{ - struct sock *sk, *parent; - bdaddr_t src, dst; - int result = 0; - - BT_DBG("session %p channel %d", s, channel); - - rfcomm_session_getaddr(s, &src, &dst); - - /* Check if we have socket listening on channel */ - parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &src); - if (!parent) - return 0; - - bh_lock_sock(parent); - - /* Check for backlog size */ - if (sk_acceptq_is_full(parent)) { - BT_DBG("backlog full %d", parent->sk_ack_backlog); - goto done; - } - - sk = rfcomm_sock_alloc(sock_net(parent), NULL, BTPROTO_RFCOMM, GFP_ATOMIC); - if (!sk) - goto done; - - bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM); - - rfcomm_sock_init(sk, parent); - bacpy(&bt_sk(sk)->src, &src); - bacpy(&bt_sk(sk)->dst, &dst); - rfcomm_pi(sk)->channel = channel; - - sk->sk_state = BT_CONFIG; - bt_accept_enqueue(parent, sk); - - /* Accept connection and return socket DLC */ - *d = rfcomm_pi(sk)->dlc; - result = 1; - -done: - bh_unlock_sock(parent); - - if (bt_sk(parent)->defer_setup) - parent->sk_state_change(parent); - - return result; -} - -static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p) -{ - struct sock *sk; - struct hlist_node *node; - - read_lock(&rfcomm_sk_list.lock); - - sk_for_each(sk, node, &rfcomm_sk_list.head) { - seq_printf(f, "%s %s %d %d\n", - batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), - sk->sk_state, rfcomm_pi(sk)->channel); - } - - read_unlock(&rfcomm_sk_list.lock); - - return 0; -} - -static int rfcomm_sock_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, rfcomm_sock_debugfs_show, inode->i_private); -} - -static const struct file_operations rfcomm_sock_debugfs_fops = { - .open = rfcomm_sock_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *rfcomm_sock_debugfs; - -static const struct proto_ops rfcomm_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = rfcomm_sock_release, - .bind = rfcomm_sock_bind, - .connect = rfcomm_sock_connect, - .listen = rfcomm_sock_listen, - .accept = rfcomm_sock_accept, - .getname = rfcomm_sock_getname, - .sendmsg = rfcomm_sock_sendmsg, - .recvmsg = rfcomm_sock_recvmsg, - .shutdown = rfcomm_sock_shutdown, - .setsockopt = rfcomm_sock_setsockopt, - .getsockopt = rfcomm_sock_getsockopt, - .ioctl = rfcomm_sock_ioctl, - .poll = bt_sock_poll, - .socketpair = sock_no_socketpair, - .mmap = sock_no_mmap -}; - -static const struct net_proto_family rfcomm_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = rfcomm_sock_create -}; - -int __init rfcomm_init_sockets(void) -{ - int err; - - err = proto_register(&rfcomm_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops); - if (err < 0) - goto error; - - if (bt_debugfs) { - rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444, - bt_debugfs, NULL, &rfcomm_sock_debugfs_fops); - if (!rfcomm_sock_debugfs) - BT_ERR("Failed to create RFCOMM debug file"); - } - - BT_INFO("RFCOMM socket layer initialized"); - - return 0; - -error: - BT_ERR("RFCOMM socket layer registration failed"); - proto_unregister(&rfcomm_proto); - return err; -} - -void __exit rfcomm_cleanup_sockets(void) -{ - debugfs_remove(rfcomm_sock_debugfs); - - if (bt_sock_unregister(BTPROTO_RFCOMM) < 0) - BT_ERR("RFCOMM socket layer unregistration failed"); - - proto_unregister(&rfcomm_proto); -} diff --git a/net/bluetooth_tizen/rfcomm/tty.c b/net/bluetooth_tizen/rfcomm/tty.c deleted file mode 100644 index 4bf54b3..0000000 --- a/net/bluetooth_tizen/rfcomm/tty.c +++ /dev/null @@ -1,1188 +0,0 @@ -/* - RFCOMM implementation for Linux Bluetooth stack (BlueZ). - Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com> - Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org> - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* - * RFCOMM TTY. - */ - -#include <linux/module.h> - -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> - -#include <linux/capability.h> -#include <linux/slab.h> -#include <linux/skbuff.h> -#include <linux/workqueue.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/rfcomm.h> - -#define RFCOMM_TTY_MAGIC 0x6d02 /* magic number for rfcomm struct */ -#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV /* whole lotta rfcomm devices */ -#define RFCOMM_TTY_MAJOR 216 /* device node major id of the usb/bluetooth.c driver */ -#define RFCOMM_TTY_MINOR 0 - -static struct tty_driver *rfcomm_tty_driver; - -struct rfcomm_dev { - struct list_head list; - atomic_t refcnt; - - char name[12]; - int id; - unsigned long flags; - atomic_t opened; - int err; - - bdaddr_t src; - bdaddr_t dst; - u8 channel; - - uint modem_status; - - struct rfcomm_dlc *dlc; - struct tty_struct *tty; - wait_queue_head_t wait; - struct work_struct wakeup_task; - - struct device *tty_dev; - - atomic_t wmem_alloc; - - struct sk_buff_head pending; -}; - -static LIST_HEAD(rfcomm_dev_list); -static DEFINE_SPINLOCK(rfcomm_dev_lock); - -static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); -static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); -static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); - -static void rfcomm_tty_wakeup(struct work_struct *work); - -/* ---- Device functions ---- */ -static void rfcomm_dev_destruct(struct rfcomm_dev *dev) -{ - struct rfcomm_dlc *dlc = dev->dlc; - - BT_DBG("dev %p dlc %p", dev, dlc); - - /* Refcount should only hit zero when called from rfcomm_dev_del() - which will have taken us off the list. Everything else are - refcounting bugs. */ - BUG_ON(!list_empty(&dev->list)); - - rfcomm_dlc_lock(dlc); - /* Detach DLC if it's owned by this dev */ - if (dlc->owner == dev) - dlc->owner = NULL; - rfcomm_dlc_unlock(dlc); - - rfcomm_dlc_put(dlc); - - tty_unregister_device(rfcomm_tty_driver, dev->id); - - kfree(dev); - - /* It's safe to call module_put() here because socket still - holds reference to this module. */ - module_put(THIS_MODULE); -} - -static inline void rfcomm_dev_hold(struct rfcomm_dev *dev) -{ - atomic_inc(&dev->refcnt); -} - -static inline void rfcomm_dev_put(struct rfcomm_dev *dev) -{ - /* The reason this isn't actually a race, as you no - doubt have a little voice screaming at you in your - head, is that the refcount should never actually - reach zero unless the device has already been taken - off the list, in rfcomm_dev_del(). And if that's not - true, we'll hit the BUG() in rfcomm_dev_destruct() - anyway. */ - if (atomic_dec_and_test(&dev->refcnt)) - rfcomm_dev_destruct(dev); -} - -static struct rfcomm_dev *__rfcomm_dev_get(int id) -{ - struct rfcomm_dev *dev; - - list_for_each_entry(dev, &rfcomm_dev_list, list) - if (dev->id == id) - return dev; - - return NULL; -} - -static inline struct rfcomm_dev *rfcomm_dev_get(int id) -{ - struct rfcomm_dev *dev; - - spin_lock(&rfcomm_dev_lock); - - dev = __rfcomm_dev_get(id); - - if (dev) { - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) - dev = NULL; - else - rfcomm_dev_hold(dev); - } - - spin_unlock(&rfcomm_dev_lock); - - return dev; -} - -static struct device *rfcomm_get_device(struct rfcomm_dev *dev) -{ - struct hci_dev *hdev; - struct hci_conn *conn; - - hdev = hci_get_route(&dev->dst, &dev->src); - if (!hdev) - return NULL; - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst); - - hci_dev_put(hdev); - - return conn ? &conn->dev : NULL; -} - -static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf) -{ - struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); - return sprintf(buf, "%s\n", batostr(&dev->dst)); -} - -static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) -{ - struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); - return sprintf(buf, "%d\n", dev->channel); -} - -static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); -static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); - -static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) -{ - struct rfcomm_dev *dev, *entry; - struct list_head *head = &rfcomm_dev_list; - int err = 0; - - BT_DBG("id %d channel %d", req->dev_id, req->channel); - - dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - spin_lock(&rfcomm_dev_lock); - - if (req->dev_id < 0) { - dev->id = 0; - - list_for_each_entry(entry, &rfcomm_dev_list, list) { - if (entry->id != dev->id) - break; - - dev->id++; - head = &entry->list; - } - } else { - dev->id = req->dev_id; - - list_for_each_entry(entry, &rfcomm_dev_list, list) { - if (entry->id == dev->id) { - err = -EADDRINUSE; - goto out; - } - - if (entry->id > dev->id - 1) - break; - - head = &entry->list; - } - } - - if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) { - err = -ENFILE; - goto out; - } - - sprintf(dev->name, "rfcomm%d", dev->id); - - list_add(&dev->list, head); - atomic_set(&dev->refcnt, 1); - - bacpy(&dev->src, &req->src); - bacpy(&dev->dst, &req->dst); - dev->channel = req->channel; - - dev->flags = req->flags & - ((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC)); - - atomic_set(&dev->opened, 0); - - init_waitqueue_head(&dev->wait); - INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup); - - skb_queue_head_init(&dev->pending); - - rfcomm_dlc_lock(dlc); - - if (req->flags & (1 << RFCOMM_REUSE_DLC)) { - struct sock *sk = dlc->owner; - struct sk_buff *skb; - - BUG_ON(!sk); - - rfcomm_dlc_throttle(dlc); - - while ((skb = skb_dequeue(&sk->sk_receive_queue))) { - skb_orphan(skb); - skb_queue_tail(&dev->pending, skb); - atomic_sub(skb->len, &sk->sk_rmem_alloc); - } - } - - dlc->data_ready = rfcomm_dev_data_ready; - dlc->state_change = rfcomm_dev_state_change; - dlc->modem_status = rfcomm_dev_modem_status; - - dlc->owner = dev; - dev->dlc = dlc; - - rfcomm_dev_modem_status(dlc, dlc->remote_v24_sig); - - rfcomm_dlc_unlock(dlc); - - /* It's safe to call __module_get() here because socket already - holds reference to this module. */ - __module_get(THIS_MODULE); - -out: - spin_unlock(&rfcomm_dev_lock); - - if (err < 0) - goto free; - - dev->tty_dev = tty_register_device(rfcomm_tty_driver, dev->id, NULL); - - if (IS_ERR(dev->tty_dev)) { - err = PTR_ERR(dev->tty_dev); - list_del(&dev->list); - goto free; - } - - dev_set_drvdata(dev->tty_dev, dev); - - if (device_create_file(dev->tty_dev, &dev_attr_address) < 0) - BT_ERR("Failed to create address attribute"); - - if (device_create_file(dev->tty_dev, &dev_attr_channel) < 0) - BT_ERR("Failed to create channel attribute"); - - return dev->id; - -free: - kfree(dev); - return err; -} - -static void rfcomm_dev_del(struct rfcomm_dev *dev) -{ - BT_DBG("dev %p", dev); - - BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags)); - - if (atomic_read(&dev->opened) > 0) - return; - - spin_lock(&rfcomm_dev_lock); - list_del_init(&dev->list); - spin_unlock(&rfcomm_dev_lock); - - rfcomm_dev_put(dev); -} - -/* ---- Send buffer ---- */ -static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc) -{ - /* We can't let it be zero, because we don't get a callback - when tx_credits becomes nonzero, hence we'd never wake up */ - return dlc->mtu * (dlc->tx_credits?:1); -} - -static void rfcomm_wfree(struct sk_buff *skb) -{ - struct rfcomm_dev *dev = (void *) skb->sk; - atomic_sub(skb->truesize, &dev->wmem_alloc); - if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) - queue_work(system_nrt_wq, &dev->wakeup_task); - rfcomm_dev_put(dev); -} - -static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev) -{ - rfcomm_dev_hold(dev); - atomic_add(skb->truesize, &dev->wmem_alloc); - skb->sk = (void *) dev; - skb->destructor = rfcomm_wfree; -} - -static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority) -{ - if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) { - struct sk_buff *skb = alloc_skb(size, priority); - if (skb) { - rfcomm_set_owner_w(skb, dev); - return skb; - } - } - return NULL; -} - -/* ---- Device IOCTLs ---- */ - -#define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP)) - -static int rfcomm_create_dev(struct sock *sk, void __user *arg) -{ - struct rfcomm_dev_req req; - struct rfcomm_dlc *dlc; - int id; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - BT_DBG("sk %p dev_id %d flags 0x%x", sk, req.dev_id, req.flags); - - if (req.flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) - return -EPERM; - - if (req.flags & (1 << RFCOMM_REUSE_DLC)) { - /* Socket must be connected */ - if (sk->sk_state != BT_CONNECTED) - return -EBADFD; - - dlc = rfcomm_pi(sk)->dlc; - rfcomm_dlc_hold(dlc); - } else { - dlc = rfcomm_dlc_alloc(GFP_KERNEL); - if (!dlc) - return -ENOMEM; - } - - id = rfcomm_dev_add(&req, dlc); - if (id < 0) { - rfcomm_dlc_put(dlc); - return id; - } - - if (req.flags & (1 << RFCOMM_REUSE_DLC)) { - /* DLC is now used by device. - * Socket must be disconnected */ - sk->sk_state = BT_CLOSED; - } - - return id; -} - -static int rfcomm_release_dev(void __user *arg) -{ - struct rfcomm_dev_req req; - struct rfcomm_dev *dev; - - if (copy_from_user(&req, arg, sizeof(req))) - return -EFAULT; - - BT_DBG("dev_id %d flags 0x%x", req.dev_id, req.flags); - - dev = rfcomm_dev_get(req.dev_id); - if (!dev) - return -ENODEV; - - if (dev->flags != NOCAP_FLAGS && !capable(CAP_NET_ADMIN)) { - rfcomm_dev_put(dev); - return -EPERM; - } - - if (req.flags & (1 << RFCOMM_HANGUP_NOW)) - rfcomm_dlc_close(dev->dlc, 0); - - /* Shut down TTY synchronously before freeing rfcomm_dev */ - if (dev->tty) - tty_vhangup(dev->tty); - - if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); - return 0; -} - -static int rfcomm_get_dev_list(void __user *arg) -{ - struct rfcomm_dev *dev; - struct rfcomm_dev_list_req *dl; - struct rfcomm_dev_info *di; - int n = 0, size, err; - u16 dev_num; - - BT_DBG(""); - - if (get_user(dev_num, (u16 __user *) arg)) - return -EFAULT; - - if (!dev_num || dev_num > (PAGE_SIZE * 4) / sizeof(*di)) - return -EINVAL; - - size = sizeof(*dl) + dev_num * sizeof(*di); - - dl = kmalloc(size, GFP_KERNEL); - if (!dl) - return -ENOMEM; - - di = dl->dev_info; - - spin_lock(&rfcomm_dev_lock); - - list_for_each_entry(dev, &rfcomm_dev_list, list) { - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) - continue; - (di + n)->id = dev->id; - (di + n)->flags = dev->flags; - (di + n)->state = dev->dlc->state; - (di + n)->channel = dev->channel; - bacpy(&(di + n)->src, &dev->src); - bacpy(&(di + n)->dst, &dev->dst); - if (++n >= dev_num) - break; - } - - spin_unlock(&rfcomm_dev_lock); - - dl->dev_num = n; - size = sizeof(*dl) + n * sizeof(*di); - - err = copy_to_user(arg, dl, size); - kfree(dl); - - return err ? -EFAULT : 0; -} - -static int rfcomm_get_dev_info(void __user *arg) -{ - struct rfcomm_dev *dev; - struct rfcomm_dev_info di; - int err = 0; - - BT_DBG(""); - - if (copy_from_user(&di, arg, sizeof(di))) - return -EFAULT; - - dev = rfcomm_dev_get(di.id); - if (!dev) - return -ENODEV; - - di.flags = dev->flags; - di.channel = dev->channel; - di.state = dev->dlc->state; - bacpy(&di.src, &dev->src); - bacpy(&di.dst, &dev->dst); - - if (copy_to_user(arg, &di, sizeof(di))) - err = -EFAULT; - - rfcomm_dev_put(dev); - return err; -} - -int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg) -{ - BT_DBG("cmd %d arg %p", cmd, arg); - - switch (cmd) { - case RFCOMMCREATEDEV: - return rfcomm_create_dev(sk, arg); - - case RFCOMMRELEASEDEV: - return rfcomm_release_dev(arg); - - case RFCOMMGETDEVLIST: - return rfcomm_get_dev_list(arg); - - case RFCOMMGETDEVINFO: - return rfcomm_get_dev_info(arg); - } - - return -EINVAL; -} - -/* ---- DLC callbacks ---- */ -static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb) -{ - struct rfcomm_dev *dev = dlc->owner; - struct tty_struct *tty; - - if (!dev) { - kfree_skb(skb); - return; - } - - tty = dev->tty; - if (!tty || !skb_queue_empty(&dev->pending)) { - skb_queue_tail(&dev->pending, skb); - return; - } - - BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); - - tty_insert_flip_string(tty, skb->data, skb->len); - tty_flip_buffer_push(tty); - - kfree_skb(skb); -} - -static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err) -{ - struct rfcomm_dev *dev = dlc->owner; - if (!dev) - return; - - BT_DBG("dlc %p dev %p err %d", dlc, dev, err); - - dev->err = err; - wake_up_interruptible(&dev->wait); - - if (dlc->state == BT_CLOSED) { - if (!dev->tty) { - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { - /* Drop DLC lock here to avoid deadlock - * 1. rfcomm_dev_get will take rfcomm_dev_lock - * but in rfcomm_dev_add there's lock order: - * rfcomm_dev_lock -> dlc lock - * 2. rfcomm_dev_put will deadlock if it's - * the last reference - */ - rfcomm_dlc_unlock(dlc); - if (rfcomm_dev_get(dev->id) == NULL) { - rfcomm_dlc_lock(dlc); - return; - } - - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); - rfcomm_dlc_lock(dlc); - } - } else - tty_hangup(dev->tty); - } -} - -static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) -{ - struct rfcomm_dev *dev = dlc->owner; - if (!dev) - return; - - BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig); - - if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) { - if (dev->tty && !C_CLOCAL(dev->tty)) - tty_hangup(dev->tty); - } - - dev->modem_status = - ((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) | - ((v24_sig & RFCOMM_V24_RTR) ? (TIOCM_RTS | TIOCM_CTS) : 0) | - ((v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0) | - ((v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0); -} - -/* ---- TTY functions ---- */ -static void rfcomm_tty_wakeup(struct work_struct *work) -{ - struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev, - wakeup_task); - struct tty_struct *tty = dev->tty; - if (!tty) - return; - - BT_DBG("dev %p tty %p", dev, tty); - tty_wakeup(tty); -} - -static void rfcomm_tty_copy_pending(struct rfcomm_dev *dev) -{ - struct tty_struct *tty = dev->tty; - struct sk_buff *skb; - int inserted = 0; - - if (!tty) - return; - - BT_DBG("dev %p tty %p", dev, tty); - - rfcomm_dlc_lock(dev->dlc); - - while ((skb = skb_dequeue(&dev->pending))) { - inserted += tty_insert_flip_string(tty, skb->data, skb->len); - kfree_skb(skb); - } - - rfcomm_dlc_unlock(dev->dlc); - - if (inserted > 0) - tty_flip_buffer_push(tty); -} - -static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp) -{ - DECLARE_WAITQUEUE(wait, current); - struct rfcomm_dev *dev; - struct rfcomm_dlc *dlc; - int err, id; - - id = tty->index; - - BT_DBG("tty %p id %d", tty, id); - - /* We don't leak this refcount. For reasons which are not entirely - clear, the TTY layer will call our ->close() method even if the - open fails. We decrease the refcount there, and decreasing it - here too would cause breakage. */ - dev = rfcomm_dev_get(id); - if (!dev) - return -ENODEV; - - BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), - dev->channel, atomic_read(&dev->opened)); - - if (atomic_inc_return(&dev->opened) > 1) - return 0; - - dlc = dev->dlc; - - /* Attach TTY and open DLC */ - - rfcomm_dlc_lock(dlc); - tty->driver_data = dev; - dev->tty = tty; - rfcomm_dlc_unlock(dlc); - set_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - - err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel); - if (err < 0) - return err; - - /* Wait for DLC to connect */ - add_wait_queue(&dev->wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (dlc->state == BT_CLOSED) { - err = -dev->err; - break; - } - - if (dlc->state == BT_CONNECTED) - break; - - if (signal_pending(current)) { - err = -EINTR; - break; - } - - tty_unlock(); - schedule(); - tty_lock(); - } - set_current_state(TASK_RUNNING); - remove_wait_queue(&dev->wait, &wait); - - if (err == 0) - device_move(dev->tty_dev, rfcomm_get_device(dev), - DPM_ORDER_DEV_AFTER_PARENT); - - rfcomm_tty_copy_pending(dev); - - rfcomm_dlc_unthrottle(dev->dlc); - - return err; -} - -static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - if (!dev) - return; - - BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, - atomic_read(&dev->opened)); - - if (atomic_dec_and_test(&dev->opened)) { - if (dev->tty_dev->parent) - device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST); - - /* Close DLC and dettach TTY */ - rfcomm_dlc_close(dev->dlc, 0); - - clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - cancel_work_sync(&dev->wakeup_task); - - rfcomm_dlc_lock(dev->dlc); - tty->driver_data = NULL; - dev->tty = NULL; - rfcomm_dlc_unlock(dev->dlc); - - if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) { - spin_lock(&rfcomm_dev_lock); - list_del_init(&dev->list); - spin_unlock(&rfcomm_dev_lock); - - rfcomm_dev_put(dev); - } - } - - rfcomm_dev_put(dev); -} - -static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - struct rfcomm_dlc *dlc = dev->dlc; - struct sk_buff *skb; - int err = 0, sent = 0, size; - - BT_DBG("tty %p count %d", tty, count); - - while (count) { - size = min_t(uint, count, dlc->mtu); - - skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC); - - if (!skb) - break; - - skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE); - - memcpy(skb_put(skb, size), buf + sent, size); - - err = rfcomm_dlc_send(dlc, skb); - if (err < 0) { - kfree_skb(skb); - break; - } - - sent += size; - count -= size; - } - - return sent ? sent : err; -} - -static int rfcomm_tty_write_room(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - int room; - - BT_DBG("tty %p", tty); - - if (!dev || !dev->dlc) - return 0; - - room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc); - if (room < 0) - room = 0; - - return room; -} - -static int rfcomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) -{ - BT_DBG("tty %p cmd 0x%02x", tty, cmd); - - switch (cmd) { - case TCGETS: - BT_DBG("TCGETS is not supported"); - return -ENOIOCTLCMD; - - case TCSETS: - BT_DBG("TCSETS is not supported"); - return -ENOIOCTLCMD; - - case TIOCMIWAIT: - BT_DBG("TIOCMIWAIT"); - break; - - case TIOCGSERIAL: - BT_ERR("TIOCGSERIAL is not supported"); - return -ENOIOCTLCMD; - - case TIOCSSERIAL: - BT_ERR("TIOCSSERIAL is not supported"); - return -ENOIOCTLCMD; - - case TIOCSERGSTRUCT: - BT_ERR("TIOCSERGSTRUCT is not supported"); - return -ENOIOCTLCMD; - - case TIOCSERGETLSR: - BT_ERR("TIOCSERGETLSR is not supported"); - return -ENOIOCTLCMD; - - case TIOCSERCONFIG: - BT_ERR("TIOCSERCONFIG is not supported"); - return -ENOIOCTLCMD; - - default: - return -ENOIOCTLCMD; /* ioctls which we must ignore */ - - } - - return -ENOIOCTLCMD; -} - -static void rfcomm_tty_set_termios(struct tty_struct *tty, struct ktermios *old) -{ - struct ktermios *new = tty->termios; - int old_baud_rate = tty_termios_baud_rate(old); - int new_baud_rate = tty_termios_baud_rate(new); - - u8 baud, data_bits, stop_bits, parity, x_on, x_off; - u16 changes = 0; - - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p termios %p", tty, old); - - if (!dev || !dev->dlc || !dev->dlc->session) - return; - - /* Handle turning off CRTSCTS */ - if ((old->c_cflag & CRTSCTS) && !(new->c_cflag & CRTSCTS)) - BT_DBG("Turning off CRTSCTS unsupported"); - - /* Parity on/off and when on, odd/even */ - if (((old->c_cflag & PARENB) != (new->c_cflag & PARENB)) || - ((old->c_cflag & PARODD) != (new->c_cflag & PARODD))) { - changes |= RFCOMM_RPN_PM_PARITY; - BT_DBG("Parity change detected."); - } - - /* Mark and space parity are not supported! */ - if (new->c_cflag & PARENB) { - if (new->c_cflag & PARODD) { - BT_DBG("Parity is ODD"); - parity = RFCOMM_RPN_PARITY_ODD; - } else { - BT_DBG("Parity is EVEN"); - parity = RFCOMM_RPN_PARITY_EVEN; - } - } else { - BT_DBG("Parity is OFF"); - parity = RFCOMM_RPN_PARITY_NONE; - } - - /* Setting the x_on / x_off characters */ - if (old->c_cc[VSTOP] != new->c_cc[VSTOP]) { - BT_DBG("XOFF custom"); - x_on = new->c_cc[VSTOP]; - changes |= RFCOMM_RPN_PM_XON; - } else { - BT_DBG("XOFF default"); - x_on = RFCOMM_RPN_XON_CHAR; - } - - if (old->c_cc[VSTART] != new->c_cc[VSTART]) { - BT_DBG("XON custom"); - x_off = new->c_cc[VSTART]; - changes |= RFCOMM_RPN_PM_XOFF; - } else { - BT_DBG("XON default"); - x_off = RFCOMM_RPN_XOFF_CHAR; - } - - /* Handle setting of stop bits */ - if ((old->c_cflag & CSTOPB) != (new->c_cflag & CSTOPB)) - changes |= RFCOMM_RPN_PM_STOP; - - /* POSIX does not support 1.5 stop bits and RFCOMM does not - * support 2 stop bits. So a request for 2 stop bits gets - * translated to 1.5 stop bits */ - if (new->c_cflag & CSTOPB) - stop_bits = RFCOMM_RPN_STOP_15; - else - stop_bits = RFCOMM_RPN_STOP_1; - - /* Handle number of data bits [5-8] */ - if ((old->c_cflag & CSIZE) != (new->c_cflag & CSIZE)) - changes |= RFCOMM_RPN_PM_DATA; - - switch (new->c_cflag & CSIZE) { - case CS5: - data_bits = RFCOMM_RPN_DATA_5; - break; - case CS6: - data_bits = RFCOMM_RPN_DATA_6; - break; - case CS7: - data_bits = RFCOMM_RPN_DATA_7; - break; - case CS8: - data_bits = RFCOMM_RPN_DATA_8; - break; - default: - data_bits = RFCOMM_RPN_DATA_8; - break; - } - - /* Handle baudrate settings */ - if (old_baud_rate != new_baud_rate) - changes |= RFCOMM_RPN_PM_BITRATE; - - switch (new_baud_rate) { - case 2400: - baud = RFCOMM_RPN_BR_2400; - break; - case 4800: - baud = RFCOMM_RPN_BR_4800; - break; - case 7200: - baud = RFCOMM_RPN_BR_7200; - break; - case 9600: - baud = RFCOMM_RPN_BR_9600; - break; - case 19200: - baud = RFCOMM_RPN_BR_19200; - break; - case 38400: - baud = RFCOMM_RPN_BR_38400; - break; - case 57600: - baud = RFCOMM_RPN_BR_57600; - break; - case 115200: - baud = RFCOMM_RPN_BR_115200; - break; - case 230400: - baud = RFCOMM_RPN_BR_230400; - break; - default: - /* 9600 is standard accordinag to the RFCOMM specification */ - baud = RFCOMM_RPN_BR_9600; - break; - - } - - if (changes) - rfcomm_send_rpn(dev->dlc->session, 1, dev->dlc->dlci, baud, - data_bits, stop_bits, parity, - RFCOMM_RPN_FLOW_NONE, x_on, x_off, changes); -} - -static void rfcomm_tty_throttle(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - rfcomm_dlc_throttle(dev->dlc); -} - -static void rfcomm_tty_unthrottle(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - rfcomm_dlc_unthrottle(dev->dlc); -} - -static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - if (!dev || !dev->dlc) - return 0; - - if (!skb_queue_empty(&dev->dlc->tx_queue)) - return dev->dlc->mtu; - - return 0; -} - -static void rfcomm_tty_flush_buffer(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - if (!dev || !dev->dlc) - return; - - skb_queue_purge(&dev->dlc->tx_queue); - tty_wakeup(tty); -} - -static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch) -{ - BT_DBG("tty %p ch %c", tty, ch); -} - -static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - BT_DBG("tty %p timeout %d", tty, timeout); -} - -static void rfcomm_tty_hangup(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - if (!dev) - return; - - rfcomm_tty_flush_buffer(tty); - - if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) { - if (rfcomm_dev_get(dev->id) == NULL) - return; - rfcomm_dev_del(dev); - rfcomm_dev_put(dev); - } -} - -static int rfcomm_tty_tiocmget(struct tty_struct *tty) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - - BT_DBG("tty %p dev %p", tty, dev); - - return dev->modem_status; -} - -static int rfcomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) -{ - struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data; - struct rfcomm_dlc *dlc = dev->dlc; - u8 v24_sig; - - BT_DBG("tty %p dev %p set 0x%02x clear 0x%02x", tty, dev, set, clear); - - rfcomm_dlc_get_modem_status(dlc, &v24_sig); - - if (set & TIOCM_DSR || set & TIOCM_DTR) - v24_sig |= RFCOMM_V24_RTC; - if (set & TIOCM_RTS || set & TIOCM_CTS) - v24_sig |= RFCOMM_V24_RTR; - if (set & TIOCM_RI) - v24_sig |= RFCOMM_V24_IC; - if (set & TIOCM_CD) - v24_sig |= RFCOMM_V24_DV; - - if (clear & TIOCM_DSR || clear & TIOCM_DTR) - v24_sig &= ~RFCOMM_V24_RTC; - if (clear & TIOCM_RTS || clear & TIOCM_CTS) - v24_sig &= ~RFCOMM_V24_RTR; - if (clear & TIOCM_RI) - v24_sig &= ~RFCOMM_V24_IC; - if (clear & TIOCM_CD) - v24_sig &= ~RFCOMM_V24_DV; - - rfcomm_dlc_set_modem_status(dlc, v24_sig); - - return 0; -} - -/* ---- TTY structure ---- */ - -static const struct tty_operations rfcomm_ops = { - .open = rfcomm_tty_open, - .close = rfcomm_tty_close, - .write = rfcomm_tty_write, - .write_room = rfcomm_tty_write_room, - .chars_in_buffer = rfcomm_tty_chars_in_buffer, - .flush_buffer = rfcomm_tty_flush_buffer, - .ioctl = rfcomm_tty_ioctl, - .throttle = rfcomm_tty_throttle, - .unthrottle = rfcomm_tty_unthrottle, - .set_termios = rfcomm_tty_set_termios, - .send_xchar = rfcomm_tty_send_xchar, - .hangup = rfcomm_tty_hangup, - .wait_until_sent = rfcomm_tty_wait_until_sent, - .tiocmget = rfcomm_tty_tiocmget, - .tiocmset = rfcomm_tty_tiocmset, -}; - -int __init rfcomm_init_ttys(void) -{ - int error; - - rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); - if (!rfcomm_tty_driver) - return -ENOMEM; - - rfcomm_tty_driver->driver_name = "rfcomm"; - rfcomm_tty_driver->name = "rfcomm"; - rfcomm_tty_driver->major = RFCOMM_TTY_MAJOR; - rfcomm_tty_driver->minor_start = RFCOMM_TTY_MINOR; - rfcomm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; - rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL; - rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - rfcomm_tty_driver->init_termios = tty_std_termios; - rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; - tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); - - error = tty_register_driver(rfcomm_tty_driver); - if (error) { - BT_ERR("Can't register RFCOMM TTY driver"); - put_tty_driver(rfcomm_tty_driver); - return error; - } - - BT_INFO("RFCOMM TTY layer initialized"); - - return 0; -} - -void rfcomm_cleanup_ttys(void) -{ - tty_unregister_driver(rfcomm_tty_driver); - put_tty_driver(rfcomm_tty_driver); -} diff --git a/net/bluetooth_tizen/sco.c b/net/bluetooth_tizen/sco.c deleted file mode 100644 index 8bf26d1..0000000 --- a/net/bluetooth_tizen/sco.c +++ /dev/null @@ -1,1061 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2000-2001 Qualcomm Incorporated - - Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -/* Bluetooth SCO sockets. */ - -#include <linux/module.h> - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/fcntl.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/socket.h> -#include <linux/skbuff.h> -#include <linux/device.h> -#include <linux/debugfs.h> -#include <linux/seq_file.h> -#include <linux/list.h> -#include <linux/security.h> -#include <net/sock.h> - -#include <asm/system.h> -#include <linux/uaccess.h> - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/sco.h> - -static bool disable_esco; - -static const struct proto_ops sco_sock_ops; - -static struct bt_sock_list sco_sk_list = { - .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock) -}; - -static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); -static void sco_chan_del(struct sock *sk, int err); - -static int sco_conn_del(struct hci_conn *conn, int err); - -static void sco_sock_close(struct sock *sk); -static void sco_sock_kill(struct sock *sk); - -/* ---- SCO timers ---- */ -static void sco_sock_timeout(unsigned long arg) -{ - struct sock *sk = (struct sock *) arg; - - BT_DBG("sock %p state %d", sk, sk->sk_state); - - bh_lock_sock(sk); - sk->sk_err = ETIMEDOUT; - sk->sk_state_change(sk); - bh_unlock_sock(sk); - - sco_sock_kill(sk); - sock_put(sk); -} - -static void sco_sock_set_timer(struct sock *sk, long timeout) -{ - BT_DBG("sock %p state %d timeout %ld", sk, sk->sk_state, timeout); - sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); -} - -static void sco_sock_clear_timer(struct sock *sk) -{ - BT_DBG("sock %p state %d", sk, sk->sk_state); - sk_stop_timer(sk, &sk->sk_timer); -} - -/* ---- SCO connections ---- */ -static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) -{ - struct hci_dev *hdev = hcon->hdev; - struct sco_conn *conn = hcon->sco_data; - - if (conn || status) - return conn; - - conn = kzalloc(sizeof(struct sco_conn), GFP_ATOMIC); - if (!conn) - return NULL; - - spin_lock_init(&conn->lock); - - hcon->sco_data = conn; - conn->hcon = hcon; - - conn->src = &hdev->bdaddr; - conn->dst = &hcon->dst; - - if (hdev->sco_mtu > 0) - conn->mtu = hdev->sco_mtu; - else - conn->mtu = 60; - - BT_DBG("hcon %p conn %p", hcon, conn); - - return conn; -} - -static inline struct sock *sco_chan_get(struct sco_conn *conn) -{ - struct sock *sk = NULL; - sco_conn_lock(conn); - sk = conn->sk; - sco_conn_unlock(conn); - return sk; -} - -static int sco_conn_del(struct hci_conn *hcon, int err) -{ - struct sco_conn *conn = hcon->sco_data; - struct sock *sk; - - if (!conn) - return 0; - - BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); - - /* Kill socket */ - sk = sco_chan_get(conn); - if (sk) { - bh_lock_sock(sk); - sco_sock_clear_timer(sk); - sco_chan_del(sk, err); - bh_unlock_sock(sk); - sco_sock_kill(sk); - } - - hcon->sco_data = NULL; - kfree(conn); - return 0; -} - -static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -{ - int err = 0; - - sco_conn_lock(conn); - if (conn->sk) - err = -EBUSY; - else - __sco_chan_add(conn, sk, parent); - - sco_conn_unlock(conn); - return err; -} - -static int sco_connect(struct sock *sk) -{ - bdaddr_t *src = &bt_sk(sk)->src; - bdaddr_t *dst = &bt_sk(sk)->dst; - struct sco_conn *conn; - struct hci_conn *hcon; - struct hci_dev *hdev; - int err, type; - - BT_DBG("%s -> %s", batostr(src), batostr(dst)); - - hdev = hci_get_route(dst, src); - if (!hdev) - return -EHOSTUNREACH; - - hci_dev_lock(hdev); - - if (lmp_esco_capable(hdev) && !disable_esco) - type = ESCO_LINK; - else - type = SCO_LINK; - - hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING); - if (IS_ERR(hcon)) { - err = PTR_ERR(hcon); - goto done; - } - - conn = sco_conn_add(hcon, 0); - if (!conn) { - hci_conn_put(hcon); - err = -ENOMEM; - goto done; - } - - /* Update source addr of the socket */ - bacpy(src, conn->src); - - err = sco_chan_add(conn, sk, NULL); - if (err) - goto done; - - if (hcon->state == BT_CONNECTED) { - sco_sock_clear_timer(sk); - sk->sk_state = BT_CONNECTED; - } else { - sk->sk_state = BT_CONNECT; - sco_sock_set_timer(sk, sk->sk_sndtimeo); - } - -done: - hci_dev_unlock(hdev); - hci_dev_put(hdev); - return err; -} - -static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) -{ - struct sco_conn *conn = sco_pi(sk)->conn; - struct sk_buff *skb; - int err, count; - - /* Check outgoing MTU */ - if (len > conn->mtu) - return -EINVAL; - - BT_DBG("sk %p len %d", sk, len); - - count = min_t(unsigned int, conn->mtu, len); - skb = bt_skb_send_alloc(sk, count, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - return err; - - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { - kfree_skb(skb); - return -EFAULT; - } - - hci_send_sco(conn->hcon, skb); - - return count; -} - -static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb) -{ - struct sock *sk = sco_chan_get(conn); - - if (!sk) - goto drop; - - BT_DBG("sk %p len %d", sk, skb->len); - - if (sk->sk_state != BT_CONNECTED) - goto drop; - - if (!sock_queue_rcv_skb(sk, skb)) - return; - -drop: - kfree_skb(skb); -} - -/* -------- Socket interface ---------- */ -static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba) -{ - struct sock *sk; - struct hlist_node *node; - - sk_for_each(sk, node, &sco_sk_list.head) - if (!bacmp(&bt_sk(sk)->src, ba)) - goto found; - sk = NULL; -found: - return sk; -} - -/* Find socket listening on source bdaddr. - * Returns closest match. - */ -static struct sock *sco_get_sock_listen(bdaddr_t *src) -{ - struct sock *sk = NULL, *sk1 = NULL; - struct hlist_node *node; - - read_lock(&sco_sk_list.lock); - - sk_for_each(sk, node, &sco_sk_list.head) { - if (sk->sk_state != BT_LISTEN) - continue; - - /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) - break; - - /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) - sk1 = sk; - } - - read_unlock(&sco_sk_list.lock); - - return node ? sk : sk1; -} - -static void sco_sock_destruct(struct sock *sk) -{ - BT_DBG("sk %p", sk); - - skb_queue_purge(&sk->sk_receive_queue); - skb_queue_purge(&sk->sk_write_queue); -} - -static void sco_sock_cleanup_listen(struct sock *parent) -{ - struct sock *sk; - - BT_DBG("parent %p", parent); - - /* Close not yet accepted channels */ - while ((sk = bt_accept_dequeue(parent, NULL))) { - sco_sock_close(sk); - sco_sock_kill(sk); - } - - parent->sk_state = BT_CLOSED; - sock_set_flag(parent, SOCK_ZAPPED); -} - -/* Kill socket (only if zapped and orphan) - * Must be called on unlocked socket. - */ -static void sco_sock_kill(struct sock *sk) -{ - if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) - return; - - BT_DBG("sk %p state %d", sk, sk->sk_state); - - /* Kill poor orphan */ - bt_sock_unlink(&sco_sk_list, sk); - sock_set_flag(sk, SOCK_DEAD); - sock_put(sk); -} - -static void __sco_sock_close(struct sock *sk) -{ - BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); - - switch (sk->sk_state) { - case BT_LISTEN: - sco_sock_cleanup_listen(sk); - break; - - case BT_CONNECTED: - case BT_CONFIG: - if (sco_pi(sk)->conn) { - sk->sk_state = BT_DISCONN; - sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT); - hci_conn_put(sco_pi(sk)->conn->hcon); - sco_pi(sk)->conn->hcon = NULL; - } else - sco_chan_del(sk, ECONNRESET); - break; - - case BT_CONNECT: - case BT_DISCONN: - sco_chan_del(sk, ECONNRESET); - break; - - default: - sock_set_flag(sk, SOCK_ZAPPED); - break; - } -} - -/* Must be called on unlocked socket. */ -static void sco_sock_close(struct sock *sk) -{ - sco_sock_clear_timer(sk); - lock_sock(sk); - __sco_sock_close(sk); - release_sock(sk); - sco_sock_kill(sk); -} - -static void sco_sock_init(struct sock *sk, struct sock *parent) -{ - BT_DBG("sk %p", sk); - - if (parent) { - sk->sk_type = parent->sk_type; - security_sk_clone(parent, sk); - } -} - -static struct proto sco_proto = { - .name = "SCO", - .owner = THIS_MODULE, - .obj_size = sizeof(struct sco_pinfo) -}; - -static struct sock *sco_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio) -{ - struct sock *sk; - - sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto); - if (!sk) - return NULL; - - sock_init_data(sock, sk); - INIT_LIST_HEAD(&bt_sk(sk)->accept_q); - - sk->sk_destruct = sco_sock_destruct; - sk->sk_sndtimeo = SCO_CONN_TIMEOUT; - - sock_reset_flag(sk, SOCK_ZAPPED); - - sk->sk_protocol = proto; - sk->sk_state = BT_OPEN; - - setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk); - - bt_sock_link(&sco_sk_list, sk); - return sk; -} - -static int sco_sock_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - - BT_DBG("sock %p", sock); - - sock->state = SS_UNCONNECTED; - - if (sock->type != SOCK_SEQPACKET) - return -ESOCKTNOSUPPORT; - - sock->ops = &sco_sock_ops; - - sk = sco_sock_alloc(net, sock, protocol, GFP_ATOMIC); - if (!sk) - return -ENOMEM; - - sco_sock_init(sk, NULL); - return 0; -} - -static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) -{ - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; - struct sock *sk = sock->sk; - bdaddr_t *src = &sa->sco_bdaddr; - int err = 0; - - BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr)); - - if (!addr || addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_state != BT_OPEN) { - err = -EBADFD; - goto done; - } - - write_lock(&sco_sk_list.lock); - - if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) { - err = -EADDRINUSE; - } else { - /* Save source address */ - bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); - sk->sk_state = BT_BOUND; - } - - write_unlock(&sco_sk_list.lock); - -done: - release_sock(sk); - return err; -} - -static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) -{ - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; - struct sock *sk = sock->sk; - int err = 0; - - - BT_DBG("sk %p", sk); - - if (alen < sizeof(struct sockaddr_sco) || - addr->sa_family != AF_BLUETOOTH) - return -EINVAL; - - if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) - return -EBADFD; - - if (sk->sk_type != SOCK_SEQPACKET) - return -EINVAL; - - lock_sock(sk); - - /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); - - err = sco_connect(sk); - if (err) - goto done; - - err = bt_sock_wait_state(sk, BT_CONNECTED, - sock_sndtimeo(sk, flags & O_NONBLOCK)); - -done: - release_sock(sk); - return err; -} - -static int sco_sock_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p backlog %d", sk, backlog); - - lock_sock(sk); - - if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) { - err = -EBADFD; - goto done; - } - - sk->sk_max_ack_backlog = backlog; - sk->sk_ack_backlog = 0; - sk->sk_state = BT_LISTEN; - -done: - release_sock(sk); - return err; -} - -static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags) -{ - DECLARE_WAITQUEUE(wait, current); - struct sock *sk = sock->sk, *ch; - long timeo; - int err = 0; - - lock_sock(sk); - - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); - - BT_DBG("sk %p timeo %ld", sk, timeo); - - /* Wait for an incoming connection. (wake-one). */ - add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - break; - } - - ch = bt_accept_dequeue(sk, newsock); - if (ch) - break; - - if (!timeo) { - err = -EAGAIN; - break; - } - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - - if (err) - goto done; - - newsock->state = SS_CONNECTED; - - BT_DBG("new socket %p", ch); - -done: - release_sock(sk); - return err; -} - -static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) -{ - struct sockaddr_sco *sa = (struct sockaddr_sco *) addr; - struct sock *sk = sock->sk; - - BT_DBG("sock %p, sk %p", sock, sk); - - addr->sa_family = AF_BLUETOOTH; - *len = sizeof(struct sockaddr_sco); - - if (peer) - bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); - else - bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); - - return 0; -} - -static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - int err; - - BT_DBG("sock %p, sk %p", sock, sk); - - err = sock_error(sk); - if (err) - return err; - - if (msg->msg_flags & MSG_OOB) - return -EOPNOTSUPP; - - lock_sock(sk); - - if (sk->sk_state == BT_CONNECTED) - err = sco_send_frame(sk, msg, len); - else - err = -ENOTCONN; - - release_sock(sk); - return err; -} - -static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sk %p", sk); - - lock_sock(sk); - - switch (optname) { - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct sco_options opts; - struct sco_conninfo cinfo; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - case SCO_OPTIONS: - if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - break; - } - - opts.mtu = sco_pi(sk)->conn->mtu; - - BT_DBG("mtu %d", opts.mtu); - - len = min_t(unsigned int, len, sizeof(opts)); - if (copy_to_user(optval, (char *)&opts, len)) - err = -EFAULT; - - break; - - case SCO_CONNINFO: - if (sk->sk_state != BT_CONNECTED) { - err = -ENOTCONN; - break; - } - - memset(&cinfo, 0, sizeof(cinfo)); - cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle; - memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3); - - len = min_t(unsigned int, len, sizeof(cinfo)); - if (copy_to_user(optval, (char *)&cinfo, len)) - err = -EFAULT; - - break; - - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - int len, err = 0; - - BT_DBG("sk %p", sk); - - if (level == SOL_SCO) - return sco_sock_getsockopt_old(sock, optname, optval, optlen); - - if (get_user(len, optlen)) - return -EFAULT; - - lock_sock(sk); - - switch (optname) { - default: - err = -ENOPROTOOPT; - break; - } - - release_sock(sk); - return err; -} - -static int sco_sock_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - lock_sock(sk); - if (!sk->sk_shutdown) { - sk->sk_shutdown = SHUTDOWN_MASK; - sco_sock_clear_timer(sk); - __sco_sock_close(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) - err = bt_sock_wait_state(sk, BT_CLOSED, - sk->sk_lingertime); - } - release_sock(sk); - return err; -} - -static int sco_sock_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - int err = 0; - - BT_DBG("sock %p, sk %p", sock, sk); - - if (!sk) - return 0; - - sco_sock_close(sk); - - if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) { - lock_sock(sk); - err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); - release_sock(sk); - } - - sock_orphan(sk); - sco_sock_kill(sk); - return err; -} - -static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -{ - BT_DBG("conn %p", conn); - - sco_pi(sk)->conn = conn; - conn->sk = sk; - - if (parent) - bt_accept_enqueue(parent, sk); -} - -/* Delete channel. - * Must be called on the locked socket. */ -static void sco_chan_del(struct sock *sk, int err) -{ - struct sco_conn *conn; - - conn = sco_pi(sk)->conn; - - BT_DBG("sk %p, conn %p, err %d", sk, conn, err); - - if (conn) { - sco_conn_lock(conn); - conn->sk = NULL; - sco_pi(sk)->conn = NULL; - sco_conn_unlock(conn); - - if (conn->hcon) - hci_conn_put(conn->hcon); - } - - sk->sk_state = BT_CLOSED; - sk->sk_err = err; - sk->sk_state_change(sk); - - sock_set_flag(sk, SOCK_ZAPPED); -} - -static void sco_conn_ready(struct sco_conn *conn) -{ - struct sock *parent; - struct sock *sk = conn->sk; - - BT_DBG("conn %p", conn); - - sco_conn_lock(conn); - - if (sk) { - sco_sock_clear_timer(sk); - bh_lock_sock(sk); - sk->sk_state = BT_CONNECTED; - sk->sk_state_change(sk); - bh_unlock_sock(sk); - } else { - parent = sco_get_sock_listen(conn->src); - if (!parent) - goto done; - - bh_lock_sock(parent); - - sk = sco_sock_alloc(sock_net(parent), NULL, - BTPROTO_SCO, GFP_ATOMIC); - if (!sk) { - bh_unlock_sock(parent); - goto done; - } - - sco_sock_init(sk, parent); - - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); - - hci_conn_hold(conn->hcon); - __sco_chan_add(conn, sk, parent); - - sk->sk_state = BT_CONNECTED; - - /* Wake up parent */ - parent->sk_data_ready(parent, 1); - - bh_unlock_sock(parent); - } - -done: - sco_conn_unlock(conn); -} - -/* ----- SCO interface with lower layer (HCI) ----- */ -int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) -{ - register struct sock *sk; - struct hlist_node *node; - int lm = 0; - - BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr)); - - /* Find listening sockets */ - read_lock(&sco_sk_list.lock); - sk_for_each(sk, node, &sco_sk_list.head) { - if (sk->sk_state != BT_LISTEN) - continue; - - if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || - !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { - lm |= HCI_LM_ACCEPT; - break; - } - } - read_unlock(&sco_sk_list.lock); - - return lm; -} - -int sco_connect_cfm(struct hci_conn *hcon, __u8 status) -{ - BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); - if (!status) { - struct sco_conn *conn; - - conn = sco_conn_add(hcon, status); - if (conn) - sco_conn_ready(conn); - } else - sco_conn_del(hcon, bt_to_errno(status)); - - return 0; -} - -int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason) -{ - BT_DBG("hcon %p reason %d", hcon, reason); - - sco_conn_del(hcon, bt_to_errno(reason)); - return 0; -} - -int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb) -{ - struct sco_conn *conn = hcon->sco_data; - - if (!conn) - goto drop; - - BT_DBG("conn %p len %d", conn, skb->len); - - if (skb->len) { - sco_recv_frame(conn, skb); - return 0; - } - -drop: - kfree_skb(skb); - return 0; -} - -static int sco_debugfs_show(struct seq_file *f, void *p) -{ - struct sock *sk; - struct hlist_node *node; - - read_lock(&sco_sk_list.lock); - - sk_for_each(sk, node, &sco_sk_list.head) { - seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src), - batostr(&bt_sk(sk)->dst), sk->sk_state); - } - - read_unlock(&sco_sk_list.lock); - - return 0; -} - -static int sco_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, sco_debugfs_show, inode->i_private); -} - -static const struct file_operations sco_debugfs_fops = { - .open = sco_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static struct dentry *sco_debugfs; - -static const struct proto_ops sco_sock_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .release = sco_sock_release, - .bind = sco_sock_bind, - .connect = sco_sock_connect, - .listen = sco_sock_listen, - .accept = sco_sock_accept, - .getname = sco_sock_getname, - .sendmsg = sco_sock_sendmsg, - .recvmsg = bt_sock_recvmsg, - .poll = bt_sock_poll, - .ioctl = bt_sock_ioctl, - .mmap = sock_no_mmap, - .socketpair = sock_no_socketpair, - .shutdown = sco_sock_shutdown, - .setsockopt = sco_sock_setsockopt, - .getsockopt = sco_sock_getsockopt -}; - -static const struct net_proto_family sco_sock_family_ops = { - .family = PF_BLUETOOTH, - .owner = THIS_MODULE, - .create = sco_sock_create, -}; - -int __init sco_init(void) -{ - int err; - - err = proto_register(&sco_proto, 0); - if (err < 0) - return err; - - err = bt_sock_register(BTPROTO_SCO, &sco_sock_family_ops); - if (err < 0) { - BT_ERR("SCO socket registration failed"); - goto error; - } - - if (bt_debugfs) { - sco_debugfs = debugfs_create_file("sco", 0444, - bt_debugfs, NULL, &sco_debugfs_fops); - if (!sco_debugfs) - BT_ERR("Failed to create SCO debug file"); - } - - BT_INFO("SCO socket layer initialized"); - - return 0; - -error: - proto_unregister(&sco_proto); - return err; -} - -void __exit sco_exit(void) -{ - debugfs_remove(sco_debugfs); - - if (bt_sock_unregister(BTPROTO_SCO) < 0) - BT_ERR("SCO socket unregistration failed"); - - proto_unregister(&sco_proto); -} - -module_param(disable_esco, bool, 0644); -MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation"); diff --git a/net/bluetooth_tizen/smp.c b/net/bluetooth_tizen/smp.c deleted file mode 100644 index deb1198..0000000 --- a/net/bluetooth_tizen/smp.c +++ /dev/null @@ -1,1003 +0,0 @@ -/* - BlueZ - Bluetooth protocol stack for Linux - Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). - - 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; - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. - IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY - CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, - COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS - SOFTWARE IS DISCLAIMED. -*/ - -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/l2cap.h> -#include <net/bluetooth/mgmt.h> -#include <net/bluetooth/smp.h> -#include <linux/crypto.h> -#include <linux/scatterlist.h> -#include <crypto/b128ops.h> - -#define SMP_TIMEOUT msecs_to_jiffies(30000) - -static inline void swap128(u8 src[16], u8 dst[16]) -{ - int i; - for (i = 0; i < 16; i++) - dst[15 - i] = src[i]; -} - -static inline void swap56(u8 src[7], u8 dst[7]) -{ - int i; - for (i = 0; i < 7; i++) - dst[6 - i] = src[i]; -} - -static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) -{ - struct blkcipher_desc desc; - struct scatterlist sg; - int err, iv_len; - unsigned char iv[128]; - - if (tfm == NULL) { - BT_ERR("tfm %p", tfm); - return -EINVAL; - } - - desc.tfm = tfm; - desc.flags = 0; - - err = crypto_blkcipher_setkey(tfm, k, 16); - if (err) { - BT_ERR("cipher setkey failed: %d", err); - return err; - } - - sg_init_one(&sg, r, 16); - - iv_len = crypto_blkcipher_ivsize(tfm); - if (iv_len) { - memset(&iv, 0xff, iv_len); - crypto_blkcipher_set_iv(tfm, iv, iv_len); - } - - err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16); - if (err) - BT_ERR("Encrypt data error %d", err); - - return err; -} - -static int smp_c1(struct crypto_blkcipher *tfm, 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]) -{ - u8 p1[16], p2[16]; - int err; - - memset(p1, 0, 16); - - /* p1 = pres || preq || _rat || _iat */ - swap56(pres, p1); - swap56(preq, p1 + 7); - p1[14] = _rat; - p1[15] = _iat; - - memset(p2, 0, 16); - - /* p2 = padding || ia || ra */ - baswap((bdaddr_t *) (p2 + 4), ia); - baswap((bdaddr_t *) (p2 + 10), ra); - - /* res = r XOR p1 */ - u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); - - /* res = e(k, res) */ - err = smp_e(tfm, k, res); - if (err) { - BT_ERR("Encrypt data error"); - return err; - } - - /* res = res XOR p2 */ - u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); - - /* res = e(k, res) */ - err = smp_e(tfm, k, res); - if (err) - BT_ERR("Encrypt data error"); - - return err; -} - -static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], - u8 r1[16], u8 r2[16], u8 _r[16]) -{ - int err; - - /* Just least significant octets from r1 and r2 are considered */ - memcpy(_r, r1 + 8, 8); - memcpy(_r + 8, r2 + 8, 8); - - err = smp_e(tfm, k, _r); - if (err) - BT_ERR("Encrypt data error"); - - return err; -} - -static int smp_rand(u8 *buf) -{ - get_random_bytes(buf, 16); - - return 0; -} - -static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, - u16 dlen, void *data) -{ - struct sk_buff *skb; - struct l2cap_hdr *lh; - int len; - - len = L2CAP_HDR_SIZE + sizeof(code) + dlen; - - if (len > conn->mtu) - return NULL; - - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) - return NULL; - - lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); - lh->len = cpu_to_le16(sizeof(code) + dlen); - lh->cid = cpu_to_le16(L2CAP_CID_SMP); - - memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code)); - - memcpy(skb_put(skb, dlen), data, dlen); - - return skb; -} - -static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) -{ - struct sk_buff *skb = smp_build_cmd(conn, code, len, data); - - BT_DBG("code 0x%2.2x", code); - - if (!skb) - return; - - skb->priority = HCI_PRIO_MAX; - hci_send_acl(conn->hchan, skb, 0); - - cancel_delayed_work_sync(&conn->security_timer); - schedule_delayed_work(&conn->security_timer, SMP_TIMEOUT); -} - -static __u8 authreq_to_seclevel(__u8 authreq) -{ - if (authreq & SMP_AUTH_MITM) - return BT_SECURITY_HIGH; - else - return BT_SECURITY_MEDIUM; -} - -static __u8 seclevel_to_authreq(__u8 sec_level) -{ - switch (sec_level) { - case BT_SECURITY_HIGH: - return SMP_AUTH_MITM | SMP_AUTH_BONDING; - case BT_SECURITY_MEDIUM: - return SMP_AUTH_BONDING; - default: - return SMP_AUTH_NONE; - } -} - -static void build_pairing_cmd(struct l2cap_conn *conn, - struct smp_cmd_pairing *req, - struct smp_cmd_pairing *rsp, - __u8 authreq) -{ - u8 dist_keys = 0; - - if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) { - dist_keys = SMP_DIST_ENC_KEY; - authreq |= SMP_AUTH_BONDING; - } else { - authreq &= ~SMP_AUTH_BONDING; - } - - if (rsp == NULL) { - req->io_capability = conn->hcon->io_capability; - req->oob_flag = SMP_OOB_NOT_PRESENT; - req->max_key_size = SMP_MAX_ENC_KEY_SIZE; - req->init_key_dist = 0; - req->resp_key_dist = dist_keys; - req->auth_req = authreq; - return; - } - - rsp->io_capability = conn->hcon->io_capability; - rsp->oob_flag = SMP_OOB_NOT_PRESENT; - rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE; - rsp->init_key_dist = 0; - rsp->resp_key_dist = req->resp_key_dist & dist_keys; - rsp->auth_req = authreq; -} - -static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) -{ - struct smp_chan *smp = conn->smp_chan; - - if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || - (max_key_size < SMP_MIN_ENC_KEY_SIZE)) - return SMP_ENC_KEY_SIZE; - - smp->enc_key_size = max_key_size; - - return 0; -} - -static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) -{ - struct hci_conn *hcon = conn->hcon; - - if (send) - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); - - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); - mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, - hcon->dst_type, reason); - - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) { - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } -} - -#define JUST_WORKS 0x00 -#define JUST_CFM 0x01 -#define REQ_PASSKEY 0x02 -#define CFM_PASSKEY 0x03 -#define REQ_OOB 0x04 -#define OVERLAP 0xFF - -static const u8 gen_method[5][5] = { - { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, - { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY }, - { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY }, - { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM }, - { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, -}; - -static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, - u8 local_io, u8 remote_io) -{ - struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp = conn->smp_chan; - u8 method; - u32 passkey = 0; - int ret = 0; - - /* Initialize key for JUST WORKS */ - memset(smp->tk, 0, sizeof(smp->tk)); - clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); - - BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); - - /* If neither side wants MITM, use JUST WORKS */ - /* If either side has unknown io_caps, use JUST WORKS */ - /* Otherwise, look up method from the table */ - if (!(auth & SMP_AUTH_MITM) || - local_io > SMP_IO_KEYBOARD_DISPLAY || - remote_io > SMP_IO_KEYBOARD_DISPLAY) - method = JUST_WORKS; - else - method = gen_method[remote_io][local_io]; - - /* If not bonding, don't ask user to confirm a Zero TK */ - if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) - method = JUST_WORKS; - - /* If Just Works, Continue with Zero TK */ - if (method == JUST_WORKS) { - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); - return 0; - } - - /* Not Just Works/Confirm results in MITM Authentication */ - if (method != JUST_CFM) - set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags); - - /* If both devices have Keyoard-Display I/O, the master - * Confirms and the slave Enters the passkey. - */ - if (method == OVERLAP) { - if (hcon->link_mode & HCI_LM_MASTER) - method = CFM_PASSKEY; - else - method = REQ_PASSKEY; - } - - /* Generate random passkey. Not valid until confirmed. */ - if (method == CFM_PASSKEY) { - u8 key[16]; - - memset(key, 0, sizeof(key)); - get_random_bytes(&passkey, sizeof(passkey)); - passkey %= 1000000; - put_unaligned_le32(passkey, key); - swap128(key, smp->tk); - BT_DBG("PassKey: %d", passkey); - } - - hci_dev_lock(hcon->hdev); - - if (method == REQ_PASSKEY) - ret = mgmt_user_passkey_request(hcon->hdev, conn->dst, - hcon->type, hcon->dst_type); - else - ret = mgmt_user_confirm_request(hcon->hdev, conn->dst, - hcon->type, hcon->dst_type, - cpu_to_le32(passkey), 0); - - hci_dev_unlock(hcon->hdev); - - return ret; -} - -static void confirm_work(struct work_struct *work) -{ - struct smp_chan *smp = container_of(work, struct smp_chan, confirm); - struct l2cap_conn *conn = smp->conn; - struct crypto_blkcipher *tfm; - struct smp_cmd_pairing_confirm cp; - int ret; - u8 res[16], reason; - - BT_DBG("conn %p", conn); - - tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(tfm)) { - reason = SMP_UNSPECIFIED; - goto error; - } - - smp->tfm = tfm; - - if (conn->hcon->out) - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, res); - else - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); - if (ret) { - reason = SMP_UNSPECIFIED; - goto error; - } - - clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); - - swap128(res, cp.confirm_val); - smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); - - return; - -error: - smp_failure(conn, reason, 1); -} - -static void random_work(struct work_struct *work) -{ - struct smp_chan *smp = container_of(work, struct smp_chan, random); - struct l2cap_conn *conn = smp->conn; - struct hci_conn *hcon = conn->hcon; - struct crypto_blkcipher *tfm = smp->tfm; - u8 reason, confirm[16], res[16], key[16]; - int ret; - - if (IS_ERR_OR_NULL(tfm)) { - reason = SMP_UNSPECIFIED; - goto error; - } - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - if (hcon->out) - ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, - conn->src, hcon->dst_type, conn->dst, res); - else - ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, - hcon->dst_type, conn->dst, 0, conn->src, res); - if (ret) { - reason = SMP_UNSPECIFIED; - goto error; - } - - swap128(res, confirm); - - if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { - BT_ERR("Pairing failed (confirmation values mismatch)"); - reason = SMP_CONFIRM_FAILED; - goto error; - } - - if (hcon->out) { - u8 stk[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; - - smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); - swap128(key, stk); - - memset(stk + smp->enc_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) { - reason = SMP_UNSPECIFIED; - goto error; - } - - hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = smp->enc_key_size; - } else { - u8 stk[16], r[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; - - swap128(smp->prnd, r); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); - - smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); - swap128(key, stk); - - memset(stk + smp->enc_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - - hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size, - ediv, rand); - } - - return; - -error: - smp_failure(conn, reason, 1); -} - -static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) -{ - struct smp_chan *smp; - - smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); - if (!smp) - return NULL; - - INIT_WORK(&smp->confirm, confirm_work); - INIT_WORK(&smp->random, random_work); - - smp->conn = conn; - conn->smp_chan = smp; - conn->hcon->smp_conn = conn; - - hci_conn_hold(conn->hcon); - - return smp; -} - -void smp_chan_destroy(struct l2cap_conn *conn) -{ - struct smp_chan *smp = conn->smp_chan; - - BUG_ON(!smp); - - if (smp->tfm) - crypto_free_blkcipher(smp->tfm); - - kfree(smp); - conn->smp_chan = NULL; - conn->hcon->smp_conn = NULL; - hci_conn_put(conn->hcon); -} - -int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) -{ - struct l2cap_conn *conn = hcon->smp_conn; - struct smp_chan *smp; - u32 value; - u8 key[16]; - - BT_DBG(""); - - if (!conn) - return -ENOTCONN; - - smp = conn->smp_chan; - - switch (mgmt_op) { - case MGMT_OP_USER_PASSKEY_REPLY: - value = le32_to_cpu(passkey); - memset(key, 0, sizeof(key)); - BT_DBG("PassKey: %d", value); - put_unaligned_le32(value, key); - swap128(key, smp->tk); - /* Fall Through */ - case MGMT_OP_USER_CONFIRM_REPLY: - set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags); - break; - case MGMT_OP_USER_PASSKEY_NEG_REPLY: - case MGMT_OP_USER_CONFIRM_NEG_REPLY: - smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1); - return 0; - default: - smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1); - return -EOPNOTSUPP; - } - - /* If it is our turn to send Pairing Confirm, do so now */ - if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags)) - queue_work(hcon->hdev->workqueue, &smp->confirm); - - return 0; -} - -static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_pairing rsp, *req = (void *) skb->data; - struct smp_chan *smp; - u8 key_size; - u8 auth = SMP_AUTH_NONE; - int ret; - - BT_DBG("conn %p", conn); - - if (conn->hcon->link_mode & HCI_LM_MASTER) - return SMP_CMD_NOTSUPP; - - if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) - smp = smp_chan_create(conn); - - smp = conn->smp_chan; - - smp->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&smp->preq[1], req, sizeof(*req)); - skb_pull(skb, sizeof(*req)); - - /* We didn't start the pairing, so match remote */ - if (req->auth_req & SMP_AUTH_BONDING) - auth = req->auth_req; - - conn->hcon->pending_sec_level = authreq_to_seclevel(auth); - - build_pairing_cmd(conn, req, &rsp, auth); - - key_size = min(req->max_key_size, rsp.max_key_size); - if (check_enc_key_size(conn, key_size)) - return SMP_ENC_KEY_SIZE; - - ret = smp_rand(smp->prnd); - if (ret) - return SMP_UNSPECIFIED; - - smp->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); - - smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); - - /* Request setup of TK */ - ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability); - if (ret) - return SMP_UNSPECIFIED; - - return 0; -} - -static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_pairing *req, *rsp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - u8 key_size, auth = SMP_AUTH_NONE; - int ret; - - BT_DBG("conn %p", conn); - - if (!(conn->hcon->link_mode & HCI_LM_MASTER)) - return SMP_CMD_NOTSUPP; - - skb_pull(skb, sizeof(*rsp)); - - req = (void *) &smp->preq[1]; - - key_size = min(req->max_key_size, rsp->max_key_size); - if (check_enc_key_size(conn, key_size)) - return SMP_ENC_KEY_SIZE; - - ret = smp_rand(smp->prnd); - if (ret) - return SMP_UNSPECIFIED; - - smp->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - - if ((req->auth_req & SMP_AUTH_BONDING) && - (rsp->auth_req & SMP_AUTH_BONDING)) - auth = SMP_AUTH_BONDING; - - auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM; - - ret = tk_request(conn, 0, auth, rsp->io_capability, req->io_capability); - if (ret) - return SMP_UNSPECIFIED; - - set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); - - /* Can't compose response until we have been confirmed */ - if (!test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) - return 0; - - queue_work(hdev->workqueue, &smp->confirm); - - return 0; -} - -static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); - skb_pull(skb, sizeof(smp->pcnf)); - - if (conn->hcon->out) { - u8 random[16]; - - swap128(smp->prnd, random); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), - random); - } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) { - queue_work(hdev->workqueue, &smp->confirm); - } else { - set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags); - } - - return 0; -} - -static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - - BT_DBG("conn %p", conn); - - swap128(skb->data, smp->rrnd); - skb_pull(skb, sizeof(smp->rrnd)); - - queue_work(hdev->workqueue, &smp->random); - - return 0; -} - -static u8 smp_ltk_encrypt(struct l2cap_conn *conn) -{ - struct smp_ltk *key; - struct hci_conn *hcon = conn->hcon; - - key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type); - if (!key) - return 0; - - if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) - return 1; - - hci_le_start_enc(hcon, key->ediv, key->rand, key->val); - hcon->enc_key_size = key->enc_size; - - return 1; - -} -static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_security_req *rp = (void *) skb->data; - struct smp_cmd_pairing cp; - struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp; - - BT_DBG("conn %p", conn); - - hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req); - - if (smp_ltk_encrypt(conn)) - return 0; - - if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) - return 0; - - smp = smp_chan_create(conn); - - skb_pull(skb, sizeof(*rp)); - - memset(&cp, 0, sizeof(cp)); - build_pairing_cmd(conn, &cp, NULL, rp->auth_req); - - smp->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&smp->preq[1], &cp, sizeof(cp)); - - smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - - return 0; -} - -int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) -{ - struct hci_conn *hcon = conn->hcon; - struct smp_chan *smp = conn->smp_chan; - __u8 authreq; - - BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); - - if (!lmp_host_le_capable(hcon->hdev)) - return 1; - - if (sec_level == BT_SECURITY_LOW) - return 1; - - if (hcon->sec_level >= sec_level) - return 1; - - if (hcon->link_mode & HCI_LM_MASTER) - if (smp_ltk_encrypt(conn)) - goto done; - - if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) - return 0; - - smp = smp_chan_create(conn); - if (!smp) - return 1; - - authreq = seclevel_to_authreq(sec_level); - - if (hcon->link_mode & HCI_LM_MASTER) { - struct smp_cmd_pairing cp; - - build_pairing_cmd(conn, &cp, NULL, authreq); - smp->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&smp->preq[1], &cp, sizeof(cp)); - - smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - } else { - struct smp_cmd_security_req cp; - cp.auth_req = authreq; - smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); - } - -done: - hcon->pending_sec_level = sec_level; - - return 0; -} - -static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_encrypt_info *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; - - skb_pull(skb, sizeof(*rp)); - - memcpy(smp->tk, rp->ltk, sizeof(smp->tk)); - - return 0; -} - -static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) -{ - struct smp_cmd_master_ident *rp = (void *) skb->data; - struct smp_chan *smp = conn->smp_chan; - struct hci_dev *hdev = conn->hcon->hdev; - struct hci_conn *hcon = conn->hcon; - u8 authenticated; - - skb_pull(skb, sizeof(*rp)); - - hci_dev_lock(hdev); - authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH); - hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size, - rp->ediv, rp->rand); - smp_distribute_keys(conn, 1); - hci_dev_unlock(hdev); - - return 0; -} - -int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) -{ - __u8 code = skb->data[0]; - __u8 reason; - int err = 0; - - if (!lmp_host_le_capable(conn->hcon->hdev)) { - err = -ENOTSUPP; - reason = SMP_PAIRING_NOTSUPP; - goto done; - } - - skb_pull(skb, sizeof(code)); - - switch (code) { - case SMP_CMD_PAIRING_REQ: - reason = smp_cmd_pairing_req(conn, skb); - break; - - case SMP_CMD_PAIRING_FAIL: - smp_failure(conn, skb->data[0], 0); - reason = 0; - err = -EPERM; - break; - - case SMP_CMD_PAIRING_RSP: - reason = smp_cmd_pairing_rsp(conn, skb); - break; - - case SMP_CMD_SECURITY_REQ: - reason = smp_cmd_security_req(conn, skb); - break; - - case SMP_CMD_PAIRING_CONFIRM: - reason = smp_cmd_pairing_confirm(conn, skb); - break; - - case SMP_CMD_PAIRING_RANDOM: - reason = smp_cmd_pairing_random(conn, skb); - break; - - case SMP_CMD_ENCRYPT_INFO: - reason = smp_cmd_encrypt_info(conn, skb); - break; - - case SMP_CMD_MASTER_IDENT: - reason = smp_cmd_master_ident(conn, skb); - break; - - case SMP_CMD_IDENT_INFO: - case SMP_CMD_IDENT_ADDR_INFO: - case SMP_CMD_SIGN_INFO: - /* Just ignored */ - reason = 0; - break; - - default: - BT_DBG("Unknown command code 0x%2.2x", code); - - reason = SMP_CMD_NOTSUPP; - err = -EOPNOTSUPP; - goto done; - } - -done: - if (reason) - smp_failure(conn, reason, 1); - - kfree_skb(skb); - return err; -} - -int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) -{ - struct smp_cmd_pairing *req, *rsp; - struct smp_chan *smp = conn->smp_chan; - __u8 *keydist; - - BT_DBG("conn %p force %d", conn, force); - - if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) - return 0; - - rsp = (void *) &smp->prsp[1]; - - /* The responder sends its keys first */ - if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07)) - return 0; - - req = (void *) &smp->preq[1]; - - if (conn->hcon->out) { - keydist = &rsp->init_key_dist; - *keydist &= req->init_key_dist; - } else { - keydist = &rsp->resp_key_dist; - *keydist &= req->resp_key_dist; - } - - - BT_DBG("keydist 0x%x", *keydist); - - if (*keydist & SMP_DIST_ENC_KEY) { - struct smp_cmd_encrypt_info enc; - struct smp_cmd_master_ident ident; - struct hci_conn *hcon = conn->hcon; - u8 authenticated; - __le16 ediv; - - get_random_bytes(enc.ltk, sizeof(enc.ltk)); - get_random_bytes(&ediv, sizeof(ediv)); - get_random_bytes(ident.rand, sizeof(ident.rand)); - - smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - - authenticated = hcon->sec_level == BT_SECURITY_HIGH; - hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK_SLAVE, 1, authenticated, - enc.ltk, smp->enc_key_size, ediv, ident.rand); - - ident.ediv = cpu_to_le16(ediv); - - smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident); - - *keydist &= ~SMP_DIST_ENC_KEY; - } - - if (*keydist & SMP_DIST_ID_KEY) { - struct smp_cmd_ident_addr_info addrinfo; - struct smp_cmd_ident_info idinfo; - - /* Send a dummy key */ - get_random_bytes(idinfo.irk, sizeof(idinfo.irk)); - - smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo); - - /* Just public address */ - memset(&addrinfo, 0, sizeof(addrinfo)); - bacpy(&addrinfo.bdaddr, conn->src); - - smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), - &addrinfo); - - *keydist &= ~SMP_DIST_ID_KEY; - } - - if (*keydist & SMP_DIST_SIGN) { - struct smp_cmd_sign_info sign; - - /* Send a dummy key */ - get_random_bytes(sign.csrk, sizeof(sign.csrk)); - - smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign); - - *keydist &= ~SMP_DIST_SIGN; - } - - if (conn->hcon->out || force) { - clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags); - cancel_delayed_work_sync(&conn->security_timer); - smp_chan_destroy(conn); - } - - return 0; -} diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index dac6a21..ff3ed60 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -38,17 +38,16 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) } #endif - BR_INPUT_SKB_CB(skb)->brdev = dev; - - skb_reset_mac_header(skb); - skb_pull(skb, ETH_HLEN); - u64_stats_update_begin(&brstats->syncp); brstats->tx_packets++; - /* Exclude ETH_HLEN from byte stats for consistency with Rx chain */ brstats->tx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); + BR_INPUT_SKB_CB(skb)->brdev = dev; + + skb_reset_mac_header(skb); + skb_pull(skb, ETH_HLEN); + rcu_read_lock(); if (is_broadcast_ether_addr(dest)) br_flood_deliver(br, skb); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 065a971..4b40a3b 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -334,7 +334,6 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async) ieee80211_sta_debugfs_add(sta); rate_control_add_sta_debugfs(sta); - memset(&sinfo, 0, sizeof(sinfo)); sinfo.filled = 0; sinfo.generation = local->sta_generation; cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index 91734cd..c6fffd9 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -68,10 +68,8 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol, struct phonet_protocol *pnp; int err; -#if !defined(CONFIG_SAMSUNG_PHONE_SVNET) if (!capable(CAP_SYS_ADMIN)) return -EPERM; -#endif if (protocol == 0) { /* Default protocol selection */ diff --git a/net/phonet/socket.c b/net/phonet/socket.c index b24bf54..ab07711 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -506,7 +506,7 @@ int pn_sock_get_port(struct sock *sk, unsigned short sport) phonet_get_local_port_range(&pmin, &pmax); for (port = pmin; port <= pmax; port++) { - port_cur += PN_HASHSIZE; + port_cur++; if (port_cur < pmin || port_cur > pmax) port_cur = pmin; diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c index 2f9858c..cea1c7d 100644 --- a/net/phonet/sysctl.c +++ b/net/phonet/sysctl.c @@ -28,7 +28,7 @@ #include <linux/init.h> #define DYNAMIC_PORT_MIN 0x40 -#define DYNAMIC_PORT_MAX 0xff +#define DYNAMIC_PORT_MAX 0x7f static DEFINE_SEQLOCK(local_port_range_lock); static int local_port_range_min[2] = {0, 0}; diff --git a/net/wireless_ath/Makefile b/net/wireless_ath/Makefile deleted file mode 100755 index 2c35c62..0000000 --- a/net/wireless_ath/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -NOSTDINC_FLAGS := -I$(srctree)/include/compat/ \ - -include $(srctree)/include/compat/linux/compat-2.6.h \ - $(CFLAGS) - -obj-$(CONFIG_CFG80211) += cfg80211.o - -obj-$(CONFIG_WEXT_CORE) += wext-core.o -obj-$(CONFIG_WEXT_PROC) += wext-proc.o -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 -cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o -cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o - -ccflags-y += -D__CHECK_ENDIAN__ - -$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk - @$(AWK) -f $(src)/genregdb.awk < $< > $@ - -clean-files := regdb.c diff --git a/net/wireless_ath/Makefile.bk b/net/wireless_ath/Makefile.bk deleted file mode 100755 index 9b12455..0000000 --- a/net/wireless_ath/Makefile.bk +++ /dev/null @@ -1,18 +0,0 @@ -obj-$(CONFIG_CFG80211) += cfg80211.o -obj-$(CONFIG_LIB80211) += lib80211.o -obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o -obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o -obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.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 -cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o -cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o -cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o - -ccflags-y += -D__CHECK_ENDIAN__ - -$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk - @$(AWK) -f $(src)/genregdb.awk < $< > $@ - -clean-files := regdb.c diff --git a/net/wireless_ath/chan.c b/net/wireless_ath/chan.c deleted file mode 100755 index 17cd0c0..0000000 --- a/net/wireless_ath/chan.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file contains helper code to handle channel - * settings and keeping track of what is possible at - * any point in time. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <net/cfg80211.h> -#include "core.h" - -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *chan; - struct ieee80211_sta_ht_cap *ht_cap; - - chan = ieee80211_get_channel(&rdev->wiphy, freq); - - /* Primary channel not allowed */ - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) - return NULL; - - if (channel_type == NL80211_CHAN_HT40MINUS && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) - return NULL; - else if (channel_type == NL80211_CHAN_HT40PLUS && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - return NULL; - - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; - - if (channel_type != NL80211_CHAN_NO_HT) { - if (!ht_cap->ht_supported) - return NULL; - - if (channel_type != NL80211_CHAN_HT20 && - (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) - return NULL; - } - - return chan; -} - -static bool can_beacon_sec_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *sec_chan; - int diff; - - switch (channel_type) { - case NL80211_CHAN_HT40PLUS: - diff = 20; - break; - case NL80211_CHAN_HT40MINUS: - diff = -20; - break; - default: - return false; - } - - sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); - if (!sec_chan) - return false; - - /* we'll need a DFS capability later */ - if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_PASSIVE_SCAN | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR)) - return false; - - return true; -} - -int cfg80211_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type) -{ - struct ieee80211_channel *chan; - int result; - - if (wdev && wdev->iftype == NL80211_IFTYPE_MONITOR) - wdev = NULL; - - if (wdev) { - ASSERT_WDEV_LOCK(wdev); - - if (!netif_running(wdev->netdev)) - return -ENETDOWN; - } - - if (!rdev->ops->set_channel) - return -EOPNOTSUPP; - - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (!chan) - return -EINVAL; - - /* Both channels should be able to initiate communication */ - if (wdev && (wdev->iftype == NL80211_IFTYPE_ADHOC || - wdev->iftype == NL80211_IFTYPE_AP || - wdev->iftype == NL80211_IFTYPE_AP_VLAN || - wdev->iftype == NL80211_IFTYPE_MESH_POINT || - wdev->iftype == NL80211_IFTYPE_P2P_GO)) { - switch (channel_type) { - case NL80211_CHAN_HT40PLUS: - case NL80211_CHAN_HT40MINUS: - if (!can_beacon_sec_chan(&rdev->wiphy, chan, - channel_type)) { - printk(KERN_DEBUG - "cfg80211: Secondary channel not " - "allowed to initiate communication\n"); - return -EINVAL; - } - break; - default: - break; - } - } - - result = rdev->ops->set_channel(&rdev->wiphy, - wdev ? wdev->netdev : NULL, - chan, channel_type); - if (result) - return result; - - if (wdev) - wdev->channel = chan; - - return 0; -} diff --git a/net/wireless_ath/core.c b/net/wireless_ath/core.c deleted file mode 100755 index 2c715e6..0000000 --- a/net/wireless_ath/core.c +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * This is the linux wireless configuration interface. - * - * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - */ - -//#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/if.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/list.h> -#include <linux/slab.h> -#include <linux/nl80211.h> -#include <linux/debugfs.h> -#include <linux/notifier.h> -#include <linux/device.h> -#include <linux/etherdevice.h> -#include <linux/rtnetlink.h> -#include <linux/sched.h> -#include <net/genetlink.h> -#include <net/cfg80211.h> -#include "nl80211.h" -#include "core.h" -#include "sysfs.h" -#include "debugfs.h" -#include "wext-compat.h" -#include "ethtool.h" - -/* name for sysfs, %d is appended */ -#define PHY_NAME "phy" - -MODULE_AUTHOR("Johannes Berg"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("wireless configuration support"); - -/* RCU-protected (and cfg80211_mutex for writers) */ -LIST_HEAD(cfg80211_rdev_list); -int cfg80211_rdev_list_generation; - -DEFINE_MUTEX(cfg80211_mutex); - -/* for debugfs */ -static struct dentry *ieee80211_debugfs_dir; - -/* for the cleanup, scan and event works */ -struct workqueue_struct *cfg80211_wq; - -static bool cfg80211_disable_40mhz_24ghz; -module_param(cfg80211_disable_40mhz_24ghz, bool, 0644); -MODULE_PARM_DESC(cfg80211_disable_40mhz_24ghz, - "Disable 40MHz support in the 2.4GHz band"); - -/* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) -{ - struct cfg80211_registered_device *result = NULL, *rdev; - - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - - assert_cfg80211_lock(); - - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (rdev->wiphy_idx == wiphy_idx) { - result = rdev; - break; - } - } - - return result; -} - -int get_wiphy_idx(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev; - if (!wiphy) - return WIPHY_IDX_STALE; - rdev = wiphy_to_dev(wiphy); - return rdev->wiphy_idx; -} - -/* requires cfg80211_rdev_mutex to be held! */ -struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) -{ - struct cfg80211_registered_device *rdev; - - if (!wiphy_idx_valid(wiphy_idx)) - return NULL; - - assert_cfg80211_lock(); - - rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); - if (!rdev) - return NULL; - return &rdev->wiphy; -} - -/* requires cfg80211_mutex to be held! */ -struct cfg80211_registered_device * -__cfg80211_rdev_from_info(struct genl_info *info) -{ - int ifindex; - struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL; - struct net_device *dev; - int err = -EINVAL; - - assert_cfg80211_lock(); - - if (info->attrs[NL80211_ATTR_WIPHY]) { - bywiphyidx = cfg80211_rdev_by_wiphy_idx( - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); - err = -ENODEV; - } - - if (info->attrs[NL80211_ATTR_IFINDEX]) { - ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - dev = dev_get_by_index(genl_info_net(info), ifindex); - if (dev) { - if (dev->ieee80211_ptr) - byifidx = - wiphy_to_dev(dev->ieee80211_ptr->wiphy); - dev_put(dev); - } - err = -ENODEV; - } - - if (bywiphyidx && byifidx) { - if (bywiphyidx != byifidx) - return ERR_PTR(-EINVAL); - else - return bywiphyidx; /* == byifidx */ - } - if (bywiphyidx) - return bywiphyidx; - - if (byifidx) - return byifidx; - - return ERR_PTR(err); -} - -struct cfg80211_registered_device * -cfg80211_get_dev_from_info(struct genl_info *info) -{ - struct cfg80211_registered_device *rdev; - - mutex_lock(&cfg80211_mutex); - rdev = __cfg80211_rdev_from_info(info); - - /* if it is not an error we grab the lock on - * it to assure it won't be going away while - * we operate on it */ - if (!IS_ERR(rdev)) - mutex_lock(&rdev->mtx); - - mutex_unlock(&cfg80211_mutex); - - return rdev; -} - -struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) -{ - struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); - struct net_device *dev; - - mutex_lock(&cfg80211_mutex); - dev = dev_get_by_index(net, ifindex); - if (!dev) - goto out; - if (dev->ieee80211_ptr) { - rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else - rdev = ERR_PTR(-ENODEV); - dev_put(dev); - out: - mutex_unlock(&cfg80211_mutex); - return rdev; -} - -/* requires cfg80211_mutex to be held */ -int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname) -{ - struct cfg80211_registered_device *rdev2; - int wiphy_idx, taken = -1, result, digits; - - assert_cfg80211_lock(); - - /* prohibit calling the thing phy%d when %d is not its number */ - sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken); - if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) { - /* count number of places needed to print wiphy_idx */ - digits = 1; - while (wiphy_idx /= 10) - digits++; - /* - * deny the name if it is phy<idx> where <idx> is printed - * without leading zeroes. taken == strlen(newname) here - */ - if (taken == strlen(PHY_NAME) + digits) - return -EINVAL; - } - - - /* Ignore nop renames */ - if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 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 = device_rename(&rdev->wiphy.dev, newname); - if (result) - return result; - - if (rdev->wiphy.debugfsdir && - !debugfs_rename(rdev->wiphy.debugfsdir->d_parent, - rdev->wiphy.debugfsdir, - rdev->wiphy.debugfsdir->d_parent, - newname)) - pr_err("failed to rename debugfs dir to %s!\n", newname); - - nl80211_notify_dev_rename(rdev); - - return 0; -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) -int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, - struct net *net) -{ - struct wireless_dev *wdev; - int err = 0; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) - return -EOPNOTSUPP; - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; - err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); - if (err) - break; - wdev->netdev->features |= NETIF_F_NETNS_LOCAL; - } - - if (err) { - /* failed -- clean up to old netns */ - net = wiphy_net(&rdev->wiphy); - - list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, - list) { - wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; - err = dev_change_net_namespace(wdev->netdev, net, - "wlan%d"); - WARN_ON(err); - wdev->netdev->features |= NETIF_F_NETNS_LOCAL; - } - - return err; - } - - wiphy_net_set(&rdev->wiphy, net); - - err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); - WARN_ON(err); - - return 0; -} -#endif - -static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) -{ - struct cfg80211_registered_device *rdev = data; - - rdev->ops->rfkill_poll(&rdev->wiphy); -} - -static int cfg80211_rfkill_set_block(void *data, bool blocked) -{ - struct cfg80211_registered_device *rdev = data; - struct wireless_dev *wdev; - - if (!blocked) - return 0; - - rtnl_lock(); - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) - dev_close(wdev->netdev); - - mutex_unlock(&rdev->devlist_mtx); - rtnl_unlock(); - - return 0; -} - -static void cfg80211_rfkill_sync_work(struct work_struct *work) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(work, struct cfg80211_registered_device, rfkill_sync); - cfg80211_rfkill_set_block(rdev, rfkill_blocked(rdev->rfkill)); -} - -static void cfg80211_event_work(struct work_struct *work) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(work, struct cfg80211_registered_device, - event_work); - - rtnl_lock(); - cfg80211_lock_rdev(rdev); - - cfg80211_process_rdev_events(rdev); - cfg80211_unlock_rdev(rdev); - rtnl_unlock(); -} - -/* exported functions */ - -struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) -{ - static int wiphy_counter; - - struct cfg80211_registered_device *rdev; - int alloc_size; - - /* - * Make sure the padding is >= the rest of the struct so that we - * always keep it large enough to pad out the entire original - * kernel's struct. We really only need to make sure it's larger - * than the kernel compat is compiled against, but since it'll - * only increase in size make sure it's larger than the current - * version of it. Subtract since it's included. - */ - BUILD_BUG_ON(WIPHY_COMPAT_PAD_SIZE < - sizeof(struct wiphy) - WIPHY_COMPAT_PAD_SIZE); - - WARN_ON(ops->add_key && (!ops->del_key || !ops->set_default_key)); - WARN_ON(ops->auth && (!ops->assoc || !ops->deauth || !ops->disassoc)); - WARN_ON(ops->connect && !ops->disconnect); - WARN_ON(ops->join_ibss && !ops->leave_ibss); - WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); - WARN_ON(ops->add_station && !ops->del_station); - WARN_ON(ops->add_mpath && !ops->del_mpath); - WARN_ON(ops->join_mesh && !ops->leave_mesh); - - alloc_size = sizeof(*rdev) + sizeof_priv; - - rdev = kzalloc(alloc_size, GFP_KERNEL); - if (!rdev) - return NULL; - - rdev->ops = ops; - - mutex_lock(&cfg80211_mutex); - - rdev->wiphy_idx = wiphy_counter++; - - if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { - wiphy_counter--; - mutex_unlock(&cfg80211_mutex); - /* ugh, wrapped! */ - kfree(rdev); - return NULL; - } - - mutex_unlock(&cfg80211_mutex); - - /* give it a proper name */ - dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx); - - mutex_init(&rdev->mtx); - mutex_init(&rdev->devlist_mtx); - mutex_init(&rdev->sched_scan_mtx); - INIT_LIST_HEAD(&rdev->netdev_list); - spin_lock_init(&rdev->bss_lock); - INIT_LIST_HEAD(&rdev->bss_list); - INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); - INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results); - device_initialize(&rdev->wiphy.dev); - rdev->wiphy.dev.class = &ieee80211_class; - rdev->wiphy.dev.platform_data = rdev; - -#ifdef CONFIG_CFG80211_DEFAULT_PS - rdev->wiphy.flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; -#endif - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - wiphy_net_set(&rdev->wiphy, &init_net); -#endif - - rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; - rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), - &rdev->wiphy.dev, RFKILL_TYPE_WLAN, - &rdev->rfkill_ops, rdev); - - if (!rdev->rfkill) { - kfree(rdev); - return NULL; - } - - INIT_WORK(&rdev->rfkill_sync, cfg80211_rfkill_sync_work); - INIT_WORK(&rdev->conn_work, cfg80211_conn_work); - INIT_WORK(&rdev->event_work, cfg80211_event_work); - - init_waitqueue_head(&rdev->dev_wait); - - /* - * Initialize wiphy parameters to IEEE 802.11 MIB default values. - * Fragmentation and RTS threshold are disabled by default with the - * special -1 value. - */ - rdev->wiphy.retry_short = 7; - rdev->wiphy.retry_long = 4; - rdev->wiphy.frag_threshold = (u32) -1; - rdev->wiphy.rts_threshold = (u32) -1; - rdev->wiphy.coverage_class = 0; - - return &rdev->wiphy; -} -EXPORT_SYMBOL(wiphy_new); - -static int wiphy_verify_combinations(struct wiphy *wiphy) -{ - const struct ieee80211_iface_combination *c; - int i, j; - - /* If we have combinations enforce them */ - if (wiphy->n_iface_combinations) - wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; - - for (i = 0; i < wiphy->n_iface_combinations; i++) { - u32 cnt = 0; - u16 all_iftypes = 0; - - c = &wiphy->iface_combinations[i]; - - /* Combinations with just one interface aren't real */ - if (WARN_ON(c->max_interfaces < 2)) - return -EINVAL; - - /* Need at least one channel */ - if (WARN_ON(!c->num_different_channels)) - return -EINVAL; - - if (WARN_ON(!c->n_limits)) - return -EINVAL; - - for (j = 0; j < c->n_limits; j++) { - u16 types = c->limits[j].types; - - /* - * interface types shouldn't overlap, this is - * used in cfg80211_can_change_interface() - */ - if (WARN_ON(types & all_iftypes)) - return -EINVAL; - all_iftypes |= types; - - if (WARN_ON(!c->limits[j].max)) - return -EINVAL; - - /* Shouldn't list software iftypes in combinations! */ - if (WARN_ON(wiphy->software_iftypes & types)) - return -EINVAL; - - cnt += c->limits[j].max; - /* - * Don't advertise an unsupported type - * in a combination. - */ - if (WARN_ON((wiphy->interface_modes & types) != types)) - return -EINVAL; - } - - /* You can't even choose that many! */ - if (WARN_ON(cnt < c->max_interfaces)) - return -EINVAL; - } - - return 0; -} - -int wiphy_register(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - int res; - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - bool have_band = false; - int i; - u16 ifmodes = wiphy->interface_modes; - - if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && - !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) - return -EINVAL; - - if (WARN_ON(wiphy->ap_sme_capa && - !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) - return -EINVAL; - - if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) - return -EINVAL; - - if (WARN_ON(wiphy->addresses && - !is_zero_ether_addr(wiphy->perm_addr) && - memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, - ETH_ALEN))) - return -EINVAL; - - if (wiphy->addresses) - memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); - - /* sanity check ifmodes */ - WARN_ON(!ifmodes); - ifmodes &= ((1 << NUM_NL80211_IFTYPES) - 1) & ~1; - if (WARN_ON(ifmodes != wiphy->interface_modes)) - wiphy->interface_modes = ifmodes; - - res = wiphy_verify_combinations(wiphy); - if (res) - return res; - - /* sanity check supported bands/channels */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - if (!sband) - continue; - - sband->band = band; - - if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) - return -EINVAL; - - /* - * Since cfg80211_disable_40mhz_24ghz is global, we can - * modify the sband's ht data even if the driver uses a - * global structure for that. - */ - if (cfg80211_disable_40mhz_24ghz && - band == IEEE80211_BAND_2GHZ && - sband->ht_cap.ht_supported) { - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SGI_40; - } - - /* - * Since we use a u32 for rate bitmaps in - * ieee80211_get_response_rate, we cannot - * have more than 32 legacy rates. - */ - if (WARN_ON(sband->n_bitrates > 32)) - return -EINVAL; - - for (i = 0; i < sband->n_channels; i++) { - sband->channels[i].orig_flags = - sband->channels[i].flags; - sband->channels[i].orig_mag = - sband->channels[i].max_antenna_gain; - sband->channels[i].orig_mpwr = - sband->channels[i].max_power; - sband->channels[i].band = band; - } - - have_band = true; - } - - if (!have_band) { - WARN_ON(1); - return -EINVAL; - } - - if (rdev->wiphy.wowlan.n_patterns) { - if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || - rdev->wiphy.wowlan.pattern_min_len > - rdev->wiphy.wowlan.pattern_max_len)) - return -EINVAL; - } - - /* check and set up bitrates */ - ieee80211_set_bitrate_flags(wiphy); - - mutex_lock(&cfg80211_mutex); - - res = device_add(&rdev->wiphy.dev); - if (res) { - mutex_unlock(&cfg80211_mutex); - return res; - } - - /* set up regulatory info */ - regulatory_update(wiphy, NL80211_REGDOM_SET_BY_CORE); - - list_add_rcu(&rdev->list, &cfg80211_rdev_list); - cfg80211_rdev_list_generation++; - - /* add to debugfs */ - rdev->wiphy.debugfsdir = - debugfs_create_dir(wiphy_name(&rdev->wiphy), - ieee80211_debugfs_dir); - if (IS_ERR(rdev->wiphy.debugfsdir)) - rdev->wiphy.debugfsdir = NULL; - - if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - struct regulatory_request request; - - request.wiphy_idx = get_wiphy_idx(wiphy); - request.initiator = NL80211_REGDOM_SET_BY_DRIVER; - request.alpha2[0] = '9'; - request.alpha2[1] = '9'; - - nl80211_send_reg_change_event(&request); - } - - cfg80211_debugfs_rdev_add(rdev); - mutex_unlock(&cfg80211_mutex); - - /* - * due to a locking dependency this has to be outside of the - * cfg80211_mutex lock - */ - res = rfkill_register(rdev->rfkill); - if (res) - goto out_rm_dev; - - rtnl_lock(); - rdev->wiphy.registered = true; - rtnl_unlock(); - return 0; - -out_rm_dev: - device_del(&rdev->wiphy.dev); - return res; -} -EXPORT_SYMBOL(wiphy_register); - -void wiphy_rfkill_start_polling(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (!rdev->ops->rfkill_poll) - return; - rdev->rfkill_ops.poll = cfg80211_rfkill_poll; - rfkill_resume_polling(rdev->rfkill); -} -EXPORT_SYMBOL(wiphy_rfkill_start_polling); - -void wiphy_rfkill_stop_polling(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - rfkill_pause_polling(rdev->rfkill); -} -EXPORT_SYMBOL(wiphy_rfkill_stop_polling); - -void wiphy_unregister(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - rtnl_lock(); - rdev->wiphy.registered = false; - rtnl_unlock(); - - rfkill_unregister(rdev->rfkill); - - /* protect the device list */ - mutex_lock(&cfg80211_mutex); - - wait_event(rdev->dev_wait, ({ - int __count; - mutex_lock(&rdev->devlist_mtx); - __count = rdev->opencount; - mutex_unlock(&rdev->devlist_mtx); - __count == 0;})); - - mutex_lock(&rdev->devlist_mtx); - BUG_ON(!list_empty(&rdev->netdev_list)); - mutex_unlock(&rdev->devlist_mtx); - - /* - * First remove the hardware from everywhere, this makes - * it impossible to find from userspace. - */ - debugfs_remove_recursive(rdev->wiphy.debugfsdir); - list_del_rcu(&rdev->list); - synchronize_rcu(); - - /* - * Try to grab rdev->mtx. If a command is still in progress, - * hopefully the driver will refuse it since it's tearing - * down the device already. We wait for this command to complete - * before unlinking the item from the list. - * Note: as codified by the BUG_ON above we cannot get here if - * a virtual interface is still present. Hence, we can only get - * to lock contention here if userspace issues a command that - * identified the hardware by wiphy index. - */ - cfg80211_lock_rdev(rdev); - /* nothing */ - cfg80211_unlock_rdev(rdev); - - /* If this device got a regulatory hint tell core its - * free to listen now to a new shiny device regulatory hint */ - reg_device_remove(wiphy); - - cfg80211_rdev_list_generation++; - device_del(&rdev->wiphy.dev); - - mutex_unlock(&cfg80211_mutex); - - flush_work(&rdev->scan_done_wk); - cancel_work_sync(&rdev->conn_work); - flush_work(&rdev->event_work); -} -EXPORT_SYMBOL(wiphy_unregister); - -void cfg80211_dev_free(struct cfg80211_registered_device *rdev) -{ - struct cfg80211_internal_bss *scan, *tmp; - rfkill_destroy(rdev->rfkill); - mutex_destroy(&rdev->mtx); - mutex_destroy(&rdev->devlist_mtx); - mutex_destroy(&rdev->sched_scan_mtx); - list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) - cfg80211_put_bss(&scan->pub); - cfg80211_rdev_free_wowlan(rdev); - kfree(rdev); -} - -void wiphy_free(struct wiphy *wiphy) -{ - put_device(&wiphy->dev); -} -EXPORT_SYMBOL(wiphy_free); - -void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (rfkill_set_hw_state(rdev->rfkill, blocked)) - schedule_work(&rdev->rfkill_sync); -} -EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); - -static void wdev_cleanup_work(struct work_struct *work) -{ - struct wireless_dev *wdev; - struct cfg80211_registered_device *rdev; - - wdev = container_of(work, struct wireless_dev, cleanup_work); - rdev = wiphy_to_dev(wdev->wiphy); - - cfg80211_lock_rdev(rdev); - - if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev, true); - } - - cfg80211_unlock_rdev(rdev); - - mutex_lock(&rdev->sched_scan_mtx); - - if (WARN_ON(rdev->sched_scan_req && - rdev->sched_scan_req->dev == wdev->netdev)) { - __cfg80211_stop_sched_scan(rdev, false); - } - - mutex_unlock(&rdev->sched_scan_mtx); - - mutex_lock(&rdev->devlist_mtx); - rdev->opencount--; - mutex_unlock(&rdev->devlist_mtx); - wake_up(&rdev->dev_wait); - - dev_put(wdev->netdev); -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) -static struct device_type wiphy_type = { - .name = "wlan", -}; -#endif - -static int cfg80211_netdev_notifier_call(struct notifier_block * nb, - unsigned long state, - void *ndev) -{ - struct net_device *dev = ndev; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev; - int ret; - - if (!wdev) - return NOTIFY_DONE; - - rdev = wiphy_to_dev(wdev->wiphy); - - WARN_ON(wdev->iftype == NL80211_IFTYPE_UNSPECIFIED); - - switch (state) { - case NETDEV_POST_INIT: - SET_NETDEV_DEVTYPE(dev, &wiphy_type); - break; - case NETDEV_REGISTER: - /* - * NB: cannot take rdev->mtx here because this may be - * called within code protected by it when interfaces - * are added with nl80211. - */ - mutex_init(&wdev->mtx); - INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); - INIT_LIST_HEAD(&wdev->event_list); - spin_lock_init(&wdev->event_lock); - INIT_LIST_HEAD(&wdev->mgmt_registrations); - spin_lock_init(&wdev->mgmt_registrations_lock); - - mutex_lock(&rdev->devlist_mtx); - list_add_rcu(&wdev->list, &rdev->netdev_list); - rdev->devlist_generation++; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - /* can only change netns with wiphy */ - dev->features |= NETIF_F_NETNS_LOCAL; -#endif - - if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, - "phy80211")) { - pr_err("failed to add phy80211 symlink to netdev!\n"); - } - wdev->netdev = dev; - wdev->sme_state = CFG80211_SME_IDLE; - mutex_unlock(&rdev->devlist_mtx); -#ifdef CONFIG_CFG80211_WEXT -#ifdef CONFIG_WIRELESS_EXT - if (!dev->wireless_handlers) - dev->wireless_handlers = &cfg80211_wext_handler; -#else -#ifdef CONFIG_MACH_PX - dev->ieee80211_ptr->wiphy->wext = &cfg80211_wext_handler; - printk_once(KERN_WARNING "cfg80211: wext will work even though " - "kernel was compiled with CONFIG_WIRELESS_EXT=n.\n"); -#else - printk_once(KERN_WARNING "cfg80211: wext will not work because " - "kernel was compiled with CONFIG_WIRELESS_EXT=n. " - "Tools using wext interface, like iwconfig will " - "not work.\n"); -#endif -#endif - wdev->wext.default_key = -1; - wdev->wext.default_mgmt_key = -1; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; -#endif - - if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) - wdev->ps = true; - else - wdev->ps = false; - /* allow mac80211 to determine the timeout */ - wdev->ps_timeout = -1; - - if (!dev->ethtool_ops) - dev->ethtool_ops = &cfg80211_ethtool_ops; - - if ((wdev->iftype == NL80211_IFTYPE_STATION || - wdev->iftype == NL80211_IFTYPE_P2P_CLIENT || - wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr) - dev->priv_flags |= IFF_DONT_BRIDGE; - break; - case NETDEV_GOING_DOWN: - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, true); - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - mutex_lock(&rdev->sched_scan_mtx); - __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); - - wdev_lock(wdev); -#ifdef CONFIG_CFG80211_WEXT - kfree(wdev->wext.ie); - wdev->wext.ie = NULL; - wdev->wext.ie_len = 0; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; -#endif - __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); - cfg80211_mlme_down(rdev, dev); - wdev_unlock(wdev); - break; - case NL80211_IFTYPE_MESH_POINT: - cfg80211_leave_mesh(rdev, dev); - break; - default: - break; - } - wdev->beacon_interval = 0; - break; - case NETDEV_DOWN: - dev_hold(dev); - queue_work(cfg80211_wq, &wdev->cleanup_work); - break; - case NETDEV_UP: - /* - * If we have a really quick DOWN/UP succession we may - * have this work still pending ... cancel it and see - * if it was pending, in which case we need to account - * for some of the work it would have done. - */ - if (cancel_work_sync(&wdev->cleanup_work)) { - mutex_lock(&rdev->devlist_mtx); - rdev->opencount--; - mutex_unlock(&rdev->devlist_mtx); - dev_put(dev); - } - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - switch (wdev->iftype) { -#ifdef CONFIG_CFG80211_WEXT - case NL80211_IFTYPE_ADHOC: - cfg80211_ibss_wext_join(rdev, wdev); - break; - case NL80211_IFTYPE_STATION: - cfg80211_mgd_wext_connect(rdev, wdev); - break; -#endif -#ifdef CONFIG_MAC80211_MESH - case NL80211_IFTYPE_MESH_POINT: - { - /* backward compat code... */ - struct mesh_setup setup; - memcpy(&setup, &default_mesh_setup, - sizeof(setup)); - /* back compat only needed for mesh_id */ - setup.mesh_id = wdev->ssid; - setup.mesh_id_len = wdev->mesh_id_up_len; - if (wdev->mesh_id_up_len) - __cfg80211_join_mesh(rdev, dev, - &setup, - &default_mesh_config); - break; - } -#endif - default: - break; - } - wdev_unlock(wdev); - rdev->opencount++; - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - - /* - * Configure power management to the driver here so that its - * correctly set also after interface type changes etc. - */ - if ((wdev->iftype == NL80211_IFTYPE_STATION || - wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && - rdev->ops->set_power_mgmt) - if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, - wdev->ps, - wdev->ps_timeout)) { - /* assume this means it's off */ - wdev->ps = false; - } - break; - case NETDEV_UNREGISTER: - /* - * NB: cannot take rdev->mtx here because this may be - * called within code protected by it when interfaces - * are removed with nl80211. - */ - mutex_lock(&rdev->devlist_mtx); - /* - * 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(&wdev->list)) { - sysfs_remove_link(&dev->dev.kobj, "phy80211"); - list_del_rcu(&wdev->list); - rdev->devlist_generation++; - cfg80211_mlme_purge_registrations(wdev); -#ifdef CONFIG_CFG80211_WEXT - kfree(wdev->wext.keys); -#endif - } - mutex_unlock(&rdev->devlist_mtx); - /* - * synchronise (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(&wdev->list); - break; - case NETDEV_PRE_UP: - if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) - return notifier_from_errno(-EOPNOTSUPP); - if (rfkill_blocked(rdev->rfkill)) - return notifier_from_errno(-ERFKILL); - ret = cfg80211_can_add_interface(rdev, wdev->iftype); - if (ret) - return notifier_from_errno(ret); - break; - } - - return NOTIFY_DONE; -} - -static struct notifier_block cfg80211_netdev_notifier = { - .notifier_call = cfg80211_netdev_notifier_call, -}; - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) -static void __net_exit cfg80211_pernet_exit(struct net *net) -{ - struct cfg80211_registered_device *rdev; - - rtnl_lock(); - mutex_lock(&cfg80211_mutex); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (net_eq(wiphy_net(&rdev->wiphy), net)) - WARN_ON(cfg80211_switch_netns(rdev, &init_net)); - } - mutex_unlock(&cfg80211_mutex); - rtnl_unlock(); -} - -static struct pernet_operations cfg80211_pernet_ops = { - .exit = cfg80211_pernet_exit, -}; -#endif - -static int __init cfg80211_init(void) -{ - int err; - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - err = register_pernet_device(&cfg80211_pernet_ops); - if (err) - goto out_fail_pernet; -#endif - - err = wiphy_sysfs_init(); - if (err) - goto out_fail_sysfs; - - err = register_netdevice_notifier(&cfg80211_netdev_notifier); - if (err) - goto out_fail_notifier; - - err = nl80211_init(); - if (err) - goto out_fail_nl80211; - - ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL); - - err = regulatory_init(); - if (err) - goto out_fail_reg; - - cfg80211_wq = create_singlethread_workqueue("cfg80211"); - if (!cfg80211_wq) - goto out_fail_wq; - - return 0; - -out_fail_wq: - regulatory_exit(); -out_fail_reg: - debugfs_remove(ieee80211_debugfs_dir); -out_fail_nl80211: - unregister_netdevice_notifier(&cfg80211_netdev_notifier); -out_fail_notifier: - wiphy_sysfs_exit(); -out_fail_sysfs: -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - unregister_pernet_device(&cfg80211_pernet_ops); -out_fail_pernet: -#endif - return err; -} -subsys_initcall(cfg80211_init); - -static void __exit cfg80211_exit(void) -{ - debugfs_remove(ieee80211_debugfs_dir); - nl80211_exit(); - unregister_netdevice_notifier(&cfg80211_netdev_notifier); - wiphy_sysfs_exit(); - regulatory_exit(); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - unregister_pernet_device(&cfg80211_pernet_ops); -#endif - destroy_workqueue(cfg80211_wq); -} -module_exit(cfg80211_exit); diff --git a/net/wireless_ath/core.h b/net/wireless_ath/core.h deleted file mode 100755 index 11ff6bb..0000000 --- a/net/wireless_ath/core.h +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Wireless configuration interface internals. - * - * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - */ -#ifndef __NET_WIRELESS_CORE_H -#define __NET_WIRELESS_CORE_H -#include <linux/mutex.h> -#include <linux/list.h> -#include <linux/netdevice.h> -#include <linux/kref.h> -#include <linux/rbtree.h> -#include <linux/debugfs.h> -#include <linux/rfkill.h> -#include <linux/workqueue.h> -#include <net/genetlink.h> -#include <net/cfg80211.h> -#include "reg.h" - -struct cfg80211_registered_device { - const struct cfg80211_ops *ops; - struct list_head list; - /* we hold this mutex during any call so that - * we cannot do multiple calls at once, and also - * to avoid the deregister call to proceed while - * any call is in progress */ - struct mutex mtx; - - /* rfkill support */ - struct rfkill_ops rfkill_ops; - struct rfkill *rfkill; - struct work_struct rfkill_sync; - - /* ISO / IEC 3166 alpha2 for which this device is receiving - * country IEs on, this can help disregard country IEs from APs - * on the same alpha2 quickly. The alpha2 may differ from - * cfg80211_regdomain's alpha2 when an intersection has occurred. - * If the AP is reconfigured this can also be used to tell us if - * the country on the country IE changed. */ - char country_ie_alpha2[2]; - - /* If a Country IE has been received this tells us the environment - * which its telling us its in. This defaults to ENVIRON_ANY */ - enum environment_cap env; - - /* wiphy index, internal only */ - int wiphy_idx; - - /* associate netdev list */ - struct mutex devlist_mtx; - /* protected by devlist_mtx or RCU */ - struct list_head netdev_list; - int devlist_generation; - int opencount; /* also protected by devlist_mtx */ - wait_queue_head_t dev_wait; - - u32 ap_beacons_nlpid; - - /* BSSes/scanning */ - spinlock_t bss_lock; - struct list_head bss_list; - struct rb_root bss_tree; - u32 bss_generation; - struct cfg80211_scan_request *scan_req; /* protected by RTNL */ - struct cfg80211_sched_scan_request *sched_scan_req; - unsigned long suspend_at; - struct work_struct scan_done_wk; - struct work_struct sched_scan_results_wk; - - struct mutex sched_scan_mtx; - -#ifdef CONFIG_NL80211_TESTMODE - struct genl_info *testmode_info; -#endif - - struct work_struct conn_work; - struct work_struct event_work; - - struct cfg80211_wowlan *wowlan; - - /* must be last because of the way we do wiphy_priv(), - * and it should at least be aligned to NETDEV_ALIGN */ - struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); -}; - -static inline -struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) -{ - BUG_ON(!wiphy); - return container_of(wiphy, struct cfg80211_registered_device, wiphy); -} - -/* Note 0 is valid, hence phy0 */ -static inline -bool wiphy_idx_valid(int wiphy_idx) -{ - return wiphy_idx >= 0; -} - -static inline void -cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) -{ - int i; - - if (!rdev->wowlan) - return; - for (i = 0; i < rdev->wowlan->n_patterns; i++) - kfree(rdev->wowlan->patterns[i].mask); - kfree(rdev->wowlan->patterns); - kfree(rdev->wowlan); -} - -extern struct workqueue_struct *cfg80211_wq; -extern struct mutex cfg80211_mutex; -extern struct list_head cfg80211_rdev_list; -extern int cfg80211_rdev_list_generation; - -static inline void assert_cfg80211_lock(void) -{ - lockdep_assert_held(&cfg80211_mutex); -} - -/* - * You can use this to mark a wiphy_idx as not having an associated wiphy. - * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL - */ -#define WIPHY_IDX_STALE -1 - -struct cfg80211_internal_bss { - struct list_head list; - struct rb_node rbn; - unsigned long ts; - struct kref ref; - atomic_t hold; - bool beacon_ies_allocated; - bool proberesp_ies_allocated; - - /* must be last because of priv member */ - struct cfg80211_bss pub; -}; - -static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub) -{ - return container_of(pub, struct cfg80211_internal_bss, pub); -} - -static inline void cfg80211_ref_bss(struct cfg80211_internal_bss *bss) -{ - kref_get(&bss->ref); -} - -static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss) -{ - atomic_inc(&bss->hold); -} - -static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss) -{ - int r = atomic_dec_return(&bss->hold); - WARN_ON(r < 0); -} - - -struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx); -int get_wiphy_idx(struct wiphy *wiphy); - -struct cfg80211_registered_device * -__cfg80211_rdev_from_info(struct genl_info *info); - -/* - * This function returns a pointer to the driver - * that the genl_info item that is passed refers to. - * If successful, it returns non-NULL and also locks - * the driver's mutex! - * - * This means that you need to call cfg80211_unlock_rdev() - * before being allowed to acquire &cfg80211_mutex! - * - * This is necessary because we need to lock the global - * mutex to get an item off the list safely, and then - * we lock the rdev mutex so it doesn't go away under us. - * - * We don't want to keep cfg80211_mutex locked - * for all the time in order to allow requests on - * other interfaces to go through at the same time. - * - * The result of this can be a PTR_ERR and hence must - * be checked with IS_ERR() for errors. - */ -extern struct cfg80211_registered_device * -cfg80211_get_dev_from_info(struct genl_info *info); - -/* requires cfg80211_rdev_mutex to be held! */ -struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); - -/* identical to cfg80211_get_dev_from_info but only operate on ifindex */ -extern struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); - -int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, - struct net *net); - -static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) -{ - mutex_lock(&rdev->mtx); -} - -static inline void cfg80211_unlock_rdev(struct cfg80211_registered_device *rdev) -{ - BUG_ON(IS_ERR(rdev) || !rdev); - mutex_unlock(&rdev->mtx); -} - -static inline void wdev_lock(struct wireless_dev *wdev) - __acquires(wdev) -{ - mutex_lock(&wdev->mtx); - __acquire(wdev->mtx); -} - -static inline void wdev_unlock(struct wireless_dev *wdev) - __releases(wdev) -{ - __release(wdev->mtx); - mutex_unlock(&wdev->mtx); -} - -#define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) -#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) - -enum cfg80211_event_type { - EVENT_CONNECT_RESULT, - EVENT_ROAMED, - EVENT_DISCONNECTED, - EVENT_IBSS_JOINED, -}; - -struct cfg80211_event { - struct list_head list; - enum cfg80211_event_type type; - - union { - struct { - u8 bssid[ETH_ALEN]; - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - u16 status; - } cr; - struct { - const u8 *req_ie; - const u8 *resp_ie; - size_t req_ie_len; - size_t resp_ie_len; - struct cfg80211_bss *bss; - } rm; - struct { - const u8 *ie; - size_t ie_len; - u16 reason; - } dc; - struct { - u8 bssid[ETH_ALEN]; - } ij; - }; -}; - -struct cfg80211_cached_keys { - struct key_params params[6]; - u8 data[6][WLAN_MAX_KEY_LEN]; - int def, defmgmt; -}; - - -/* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); - -extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname); - -void ieee80211_set_bitrate_flags(struct wiphy *wiphy); - -void cfg80211_bss_expire(struct cfg80211_registered_device *dev); -void cfg80211_bss_age(struct cfg80211_registered_device *dev, - unsigned long age_secs); - -/* IBSS */ -int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys); -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys); -void cfg80211_clear_ibss(struct net_device *dev, bool nowext); -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext); -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext); -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); -int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev); - -/* mesh */ -extern const struct mesh_config default_mesh_config; -extern const struct mesh_setup default_mesh_setup; -int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf); -int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf); -int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev); - -/* MLME */ -int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, - const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change); -int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change); -int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt); -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt); -int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change); -void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, - struct net_device *dev); -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev, - struct cfg80211_bss *bss); -int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, - u16 frame_type, const u8 *match_data, - int match_len); -void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); -void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); -int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie); - -/* SME */ -int __cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys, - const u8 *prev_bssid); -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys); -int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, - bool wextev); -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, - bool wextev); -void __cfg80211_roamed(struct wireless_dev *wdev, - struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len); -int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev); - -void cfg80211_conn_work(struct work_struct *work); -void cfg80211_sme_failed_assoc(struct wireless_dev *wdev); -bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); - -/* internal helpers */ -bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); -int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, - struct key_params *params, int key_idx, - bool pairwise, const u8 *mac_addr); -void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, - size_t ie_len, u16 reason, bool from_ap); -void cfg80211_sme_scan_done(struct net_device *dev); -void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); -void cfg80211_sme_disassoc(struct net_device *dev, int idx); -void __cfg80211_scan_done(struct work_struct *wk); -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); -void __cfg80211_sched_scan_results(struct work_struct *wk); -int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, - bool driver_initiated); -void cfg80211_upload_connect_keys(struct wireless_dev *wdev); -int cfg80211_change_iface(struct cfg80211_registered_device *rdev, - struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params); -void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev); - -int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - enum nl80211_iftype iftype); - -static inline int -cfg80211_can_add_interface(struct cfg80211_registered_device *rdev, - enum nl80211_iftype iftype) -{ - return cfg80211_can_change_interface(rdev, NULL, iftype); -} - -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type); -int cfg80211_set_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type); - -u16 cfg80211_calculate_bitrate(struct rate_info *rate); - -int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, - const u8 *rates, unsigned int n_rates, - u32 *mask); - -int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int); - -#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS -#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) -#else -/* - * Trick to enable using it as a condition, - * and also not give a warning when it's - * not used that way. - */ -#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; }) -#endif - -#endif /* __NET_WIRELESS_CORE_H */ diff --git a/net/wireless_ath/db.txt b/net/wireless_ath/db.txt deleted file mode 100755 index 23b85a3..0000000 --- a/net/wireless_ath/db.txt +++ /dev/null @@ -1,697 +0,0 @@ -# This is the world regulatory domain -country 00: - (2402 - 2472 @ 40), (3, 20) - # Channel 12 - 13. No HT40 channel fits here - (2457 - 2482 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS - # Channel 14. Only JP enables this and for 802.11b only - (2474 - 2494 @ 20), (3, 20), PASSIVE-SCAN, NO-IBSS, NO-OFDM - # Channel 36 - 48 - (5170 - 5250 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS - # NB: 5260 MHz - 5700 MHz requies DFS - # Channel 149 - 165 - (5735 - 5835 @ 40), (3, 20), PASSIVE-SCAN, NO-IBSS - - -country AE: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AL: - (2402 - 2482 @ 20), (N/A, 20) - -country AM: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 18) - (5250 - 5330 @ 20), (N/A, 18), DFS - -country AN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AR: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country AT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AU: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 23) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country AW: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country AZ: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country BA: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country BB: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 23) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country BD: - (2402 - 2482 @ 40), (N/A, 20) - -country BE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country BG: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 23) - (5250 - 5290 @ 40), (N/A, 23), DFS - (5490 - 5710 @ 40), (N/A, 30), DFS - -country BH: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - (5735 - 5835 @ 20), (N/A, 20) - -country BL: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country BN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 30) - -country BO: - (2402 - 2482 @ 40), (N/A, 30) - (5735 - 5835 @ 40), (N/A, 30) - -country BR: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country BY: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country BZ: - (2402 - 2482 @ 40), (N/A, 30) - (5735 - 5835 @ 40), (N/A, 30) - -country CA: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country CH: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country CL: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 20) - -country CN: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country CO: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country CR: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country CS: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country CY: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -# Data from http://www.ctu.eu/164/download/VOR/VOR-12-08-2005-34.pdf -# and http://www.ctu.eu/164/download/VOR/VOR-12-05-2007-6-AN.pdf -# Power at 5250 - 5350 MHz and 5470 - 5725 MHz can be doubled if TPC is -# implemented. -country CZ: DFS-ETSI - (2400 - 2483.5 @ 40), (N/A, 100 mW) - (5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR - (5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS - (5470 - 5725 @ 40), (N/A, 500 mW), DFS - -# Data from "Frequenznutzungsplan" (as published in April 2008), downloaded from -# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38448/publicationFile/2659/Frequenznutzungsplan2008_Id17448pdf.pdf -# For the 5GHz range also see -# http://www.bundesnetzagentur.de/cae/servlet/contentblob/38216/publicationFile/6579/WLAN5GHzVfg7_2010_28042010pdf.pdf -# The values have been reduced by a factor of 2 (3db) for non TPC devices -# (in other words: devices with TPC can use twice the tx power of this table). -# Note that the docs do not require TPC for 5150--5250; the reduction to -# 100mW thus is not strictly required -- however the conservative 100mW -# limit is used here as the non-interference with radar and satellite -# apps relies on the attenuation by the building walls only in the -# absence of DFS; the neighbour countries have 100mW limit here as well. - -country DE: DFS-ETSI - # entries 279004 and 280006 - (2400 - 2483.5 @ 40), (N/A, 100 mW) - # entry 303005 - (5150 - 5250 @ 40), (N/A, 100 mW), NO-OUTDOOR - # entries 304002 and 305002 - (5250 - 5350 @ 40), (N/A, 100 mW), NO-OUTDOOR, DFS - # entries 308002, 309001 and 310003 - (5470 - 5725 @ 40), (N/A, 500 mW), DFS - -country DK: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country DO: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country DZ: - (2402 - 2482 @ 40), (N/A, 20) - -country EC: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country EE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country EG: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - -country ES: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country FI: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country FR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country GE: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country GB: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country GD: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country GR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country GL: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - (5490 - 5710 @ 20), (N/A, 27), DFS - -country GT: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country GU: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country HN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country HK: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country HR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country HT: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country HU: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country ID: - (2402 - 2482 @ 40), (N/A, 20) - -country IE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country IL: - (2402 - 2482 @ 40), (N/A, 20) - (5150 - 5250 @ 40), (N/A, 200 mW), NO-OUTDOOR - (5250 - 5350 @ 40), (N/A, 200 mW), NO-OUTDOOR, DFS - -country IN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 20) - -country IS: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country IR: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country IT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country JM: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country JP: - (2402 - 2472 @ 40), (N/A, 20) - (2457 - 2482 @ 20), (N/A, 20) - (2474 - 2494 @ 20), (N/A, 20), NO-OFDM - (4910 - 4930 @ 10), (N/A, 23) - (4910 - 4990 @ 40), (N/A, 23) - (4930 - 4950 @ 10), (N/A, 23) - (5030 - 5045 @ 10), (N/A, 23) - (5030 - 5090 @ 40), (N/A, 23) - (5050 - 5060 @ 10), (N/A, 23) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 23), DFS - -country JO: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - -country KE: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country KH: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country KP: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5330 @ 40), (3, 20) - (5160 - 5250 @ 40), (3, 20), DFS - (5490 - 5630 @ 40), (3, 30), DFS - (5735 - 5815 @ 40), (3, 30) - -country KR: - (2402 - 2482 @ 20), (N/A, 20) - (5170 - 5250 @ 20), (3, 20) - (5250 - 5330 @ 20), (3, 20), DFS - (5490 - 5630 @ 20), (3, 30), DFS - (5735 - 5815 @ 20), (3, 30) - -country KW: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - -country KZ: - (2402 - 2482 @ 40), (N/A, 20) - -country LB: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country LI: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country LK: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 20), DFS - (5490 - 5710 @ 20), (3, 20), DFS - (5735 - 5835 @ 20), (3, 30) - -country LT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country LU: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country LV: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country MC: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 18) - (5250 - 5330 @ 40), (N/A, 18), DFS - -country MA: - (2402 - 2482 @ 40), (N/A, 20) - -country MO: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 23) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country MK: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country MT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country MY: - (2402 - 2482 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 30), DFS - (5735 - 5835 @ 40), (N/A, 30) - -country MX: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country NL: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20), NO-OUTDOOR - (5250 - 5330 @ 40), (N/A, 20), NO-OUTDOOR, DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country NO: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country NP: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country NZ: - (2402 - 2482 @ 40), (N/A, 30) - (5170 - 5250 @ 20), (3, 23) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country OM: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country PA: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country PE: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country PG: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country PH: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country PK: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country PL: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country PT: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country PR: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 23), DFS - (5735 - 5835 @ 40), (3, 30) - -country QA: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 40), (N/A, 30) - -country RO: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country RU: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5835 @ 20), (N/A, 30) - -country SA: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 23) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country SE: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country SG: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5735 - 5835 @ 40), (N/A, 20) - -country SI: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country SK: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - (5490 - 5710 @ 40), (N/A, 27), DFS - -country SV: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (3, 17) - (5250 - 5330 @ 20), (3, 23), DFS - (5735 - 5835 @ 20), (3, 30) - -country SY: - (2402 - 2482 @ 40), (N/A, 20) - -country TW: - (2402 - 2472 @ 40), (3, 27) - (5270 - 5330 @ 40), (3, 17), DFS - (5735 - 5815 @ 40), (3, 30) - -country TH: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country TT: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country TN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - -country TR: DFS-ETSI - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 20), (N/A, 20) - (5250 - 5330 @ 20), (N/A, 20), DFS - -country UA: - (2402 - 2482 @ 40), (N/A, 20) - -country US: DFS-FCC - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5600 @ 40), (3, 20), DFS - (5650 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country UY: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country UZ: - (2402 - 2472 @ 40), (3, 27) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country VE: - (2402 - 2482 @ 40), (N/A, 20) - (5735 - 5815 @ 40), (N/A, 23) - -country VN: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (N/A, 20) - (5250 - 5330 @ 40), (N/A, 20), DFS - -country YE: - (2402 - 2482 @ 40), (N/A, 20) - -country ZA: - (2402 - 2482 @ 40), (N/A, 20) - (5170 - 5250 @ 40), (3, 17) - (5250 - 5330 @ 40), (3, 20), DFS - (5490 - 5710 @ 40), (3, 20), DFS - (5735 - 5835 @ 40), (3, 30) - -country ZW: - (2402 - 2482 @ 40), (N/A, 20) - diff --git a/net/wireless_ath/debugfs.c b/net/wireless_ath/debugfs.c deleted file mode 100755 index 39765bc..0000000 --- a/net/wireless_ath/debugfs.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * cfg80211 debugfs - * - * Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com> - * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> - * - * 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/slab.h> -#include "core.h" -#include "debugfs.h" - -static int cfg80211_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ -static ssize_t name## _read(struct file *file, char __user *userbuf, \ - size_t count, loff_t *ppos) \ -{ \ - struct wiphy *wiphy= file->private_data; \ - char buf[buflen]; \ - int res; \ - \ - res = scnprintf(buf, buflen, fmt "\n", ##value); \ - return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ -} \ - \ -static const struct file_operations name## _ops = { \ - .read = name## _read, \ - .open = cfg80211_open_file_generic, \ - .llseek = generic_file_llseek, \ -}; - -DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d", - wiphy->rts_threshold) -DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d", - wiphy->frag_threshold); -DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d", - wiphy->retry_short) -DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d", - wiphy->retry_long); - -static int ht_print_chan(struct ieee80211_channel *chan, - char *buf, int buf_size, int offset) -{ - if (WARN_ON(offset > buf_size)) - return 0; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - return snprintf(buf + offset, - buf_size - offset, - "%d Disabled\n", - chan->center_freq); - - return snprintf(buf + offset, - buf_size - offset, - "%d HT40 %c%c\n", - chan->center_freq, - (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', - (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); -} - -static ssize_t ht40allow_map_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct wiphy *wiphy = file->private_data; - char *buf; - unsigned int offset = 0, buf_size = PAGE_SIZE, i, r; - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - - buf = kzalloc(buf_size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - mutex_lock(&cfg80211_mutex); - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - if (!sband) - continue; - for (i = 0; i < sband->n_channels; i++) - offset += ht_print_chan(&sband->channels[i], - buf, buf_size, offset); - } - - mutex_unlock(&cfg80211_mutex); - - r = simple_read_from_buffer(user_buf, count, ppos, buf, offset); - - kfree(buf); - - return r; -} - -static const struct file_operations ht40allow_map_ops = { - .read = ht40allow_map_read, - .open = cfg80211_open_file_generic, - .llseek = default_llseek, -}; - -#define DEBUGFS_ADD(name) \ - debugfs_create_file(#name, S_IRUGO, phyd, &rdev->wiphy, &name## _ops); - -void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) -{ - struct dentry *phyd = rdev->wiphy.debugfsdir; - - DEBUGFS_ADD(rts_threshold); - DEBUGFS_ADD(fragmentation_threshold); - DEBUGFS_ADD(short_retry_limit); - DEBUGFS_ADD(long_retry_limit); - DEBUGFS_ADD(ht40allow_map); -} diff --git a/net/wireless_ath/debugfs.h b/net/wireless_ath/debugfs.h deleted file mode 100755 index 74fdd38..0000000 --- a/net/wireless_ath/debugfs.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __CFG80211_DEBUGFS_H -#define __CFG80211_DEBUGFS_H - -#ifdef CONFIG_CFG80211_DEBUGFS -void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev); -#else -static inline -void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {} -#endif - -#endif /* __CFG80211_DEBUGFS_H */ diff --git a/net/wireless_ath/ethtool.c b/net/wireless_ath/ethtool.c deleted file mode 100755 index 9bde4d1..0000000 --- a/net/wireless_ath/ethtool.c +++ /dev/null @@ -1,78 +0,0 @@ -#include <linux/utsname.h> -#include <net/cfg80211.h> -#include "core.h" -#include "ethtool.h" - -static void cfg80211_get_drvinfo(struct net_device *dev, - struct ethtool_drvinfo *info) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name, - sizeof(info->driver)); - - strlcpy(info->version, init_utsname()->release, sizeof(info->version)); - - if (wdev->wiphy->fw_version[0]) - strncpy(info->fw_version, wdev->wiphy->fw_version, - sizeof(info->fw_version)); - else - strncpy(info->fw_version, "N/A", sizeof(info->fw_version)); - - strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)), - sizeof(info->bus_info)); -} - -static int cfg80211_get_regs_len(struct net_device *dev) -{ - /* For now, return 0... */ - return 0; -} - -static void cfg80211_get_regs(struct net_device *dev, struct ethtool_regs *regs, - void *data) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - regs->version = wdev->wiphy->hw_version; - regs->len = 0; -} - -static void cfg80211_get_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - - memset(rp, 0, sizeof(*rp)); - - if (rdev->ops->get_ringparam) - rdev->ops->get_ringparam(wdev->wiphy, - &rp->tx_pending, &rp->tx_max_pending, - &rp->rx_pending, &rp->rx_max_pending); -} - -static int cfg80211_set_ringparam(struct net_device *dev, - struct ethtool_ringparam *rp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - - if (rp->rx_mini_pending != 0 || rp->rx_jumbo_pending != 0) - return -EINVAL; - - if (rdev->ops->set_ringparam) - return rdev->ops->set_ringparam(wdev->wiphy, - rp->tx_pending, rp->rx_pending); - - return -ENOTSUPP; -} - -const struct ethtool_ops cfg80211_ethtool_ops = { - .get_drvinfo = cfg80211_get_drvinfo, - .get_regs_len = cfg80211_get_regs_len, - .get_regs = cfg80211_get_regs, - .get_link = ethtool_op_get_link, - .get_ringparam = cfg80211_get_ringparam, - .set_ringparam = cfg80211_set_ringparam, -}; diff --git a/net/wireless_ath/ethtool.h b/net/wireless_ath/ethtool.h deleted file mode 100755 index 695ecad..0000000 --- a/net/wireless_ath/ethtool.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef __CFG80211_ETHTOOL__ -#define __CFG80211_ETHTOOL__ - -extern const struct ethtool_ops cfg80211_ethtool_ops; - -#endif /* __CFG80211_ETHTOOL__ */ diff --git a/net/wireless_ath/genregdb.awk b/net/wireless_ath/genregdb.awk deleted file mode 100755 index 53c143f..0000000 --- a/net/wireless_ath/genregdb.awk +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/awk -f -# -# genregdb.awk -- generate regdb.c from db.txt -# -# Actually, it reads from stdin (presumed to be db.txt) and writes -# to stdout (presumed to be regdb.c), but close enough... -# -# Copyright 2009 John W. Linville <linville@tuxdriver.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. -# - -BEGIN { - active = 0 - rules = 0; - print "/*" - print " * DO NOT EDIT -- file generated from data in db.txt" - print " */" - print "" - print "#include <linux/nl80211.h>" - print "#include <net/cfg80211.h>" - print "#include \"regdb.h\"" - print "" - regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n" -} - -/^[ \t]*#/ { - # Ignore -} - -!active && /^[ \t]*$/ { - # Ignore -} - -!active && /country/ { - country=$2 - sub(/:/, "", country) - printf "static const struct ieee80211_regdomain regdom_%s = {\n", country - printf "\t.alpha2 = \"%s\",\n", country - printf "\t.reg_rules = {\n" - active = 1 - regdb = regdb "\t®dom_" country ",\n" -} - -active && /^[ \t]*\(/ { - start = $1 - sub(/\(/, "", start) - end = $3 - bw = $5 - sub(/\),/, "", bw) - gain = $6 - sub(/\(/, "", gain) - sub(/,/, "", gain) - power = $7 - sub(/\)/, "", power) - sub(/,/, "", power) - # power might be in mW... - units = $8 - sub(/\)/, "", units) - sub(/,/, "", units) - if (units == "mW") { - if (power == 100) { - power = 20 - } else if (power == 200) { - power = 23 - } else if (power == 500) { - power = 27 - } else if (power == 1000) { - power = 30 - } else { - print "Unknown power value in database!" - } - } - flagstr = "" - for (i=8; i<=NF; i++) - flagstr = flagstr $i - split(flagstr, flagarray, ",") - flags = "" - for (arg in flagarray) { - if (flagarray[arg] == "NO-OFDM") { - flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | " - } else if (flagarray[arg] == "NO-CCK") { - flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | " - } else if (flagarray[arg] == "NO-INDOOR") { - flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | " - } else if (flagarray[arg] == "NO-OUTDOOR") { - flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | " - } else if (flagarray[arg] == "DFS") { - flags = flags "\n\t\t\tNL80211_RRF_DFS | " - } else if (flagarray[arg] == "PTP-ONLY") { - flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | " - } else if (flagarray[arg] == "PTMP-ONLY") { - flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | " - } else if (flagarray[arg] == "PASSIVE-SCAN") { - flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | " - } else if (flagarray[arg] == "NO-IBSS") { - flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | " - } - } - flags = flags "0" - printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags - rules++ -} - -active && /^[ \t]*$/ { - active = 0 - printf "\t},\n" - printf "\t.n_reg_rules = %d\n", rules - printf "};\n\n" - rules = 0; -} - -END { - print regdb "};" - print "" - print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);" -} diff --git a/net/wireless_ath/ibss.c b/net/wireless_ath/ibss.c deleted file mode 100755 index 30f20fe..0000000 --- a/net/wireless_ath/ibss.c +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Some IBSS support code for cfg80211. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include "wext-compat.h" -#include "nl80211.h" - - -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return; - - if (!wdev->ssid_len) - return; - - bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); - - if (WARN_ON(!bss)) - return; - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - } - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - cfg80211_upload_connect_keys(wdev); - - nl80211_send_ibss_bssid(wiphy_to_dev(wdev->wiphy), dev, bssid, - GFP_KERNEL); -#ifdef CONFIG_CFG80211_WEXT - memset(&wrqu, 0, sizeof(wrqu)); - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); -#endif -} - -void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(!wdev->ssid_len); - - ev = kzalloc(sizeof(*ev), gfp); - if (!ev) - return; - - ev->type = EVENT_IBSS_JOINED; - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_ibss_joined); - -int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->ssid_len) - return -EALREADY; - - if (!params->basic_rates) { - /* - * If no rates were explicitly configured, - * use the mandatory rate set for 11b or - * 11a for maximum compatibility. - */ - struct ieee80211_supported_band *sband = - rdev->wiphy.bands[params->channel->band]; - int j; - u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? - IEEE80211_RATE_MANDATORY_A : - IEEE80211_RATE_MANDATORY_B; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].flags & flag) - params->basic_rates |= BIT(j); - } - } - - if (WARN_ON(wdev->connect_keys)) - kfree(wdev->connect_keys); - wdev->connect_keys = connkeys; - -#ifdef CONFIG_CFG80211_WEXT - wdev->wext.ibss.channel = params->channel; -#endif - err = rdev->ops->join_ibss(&rdev->wiphy, dev, params); - if (err) { - wdev->connect_keys = NULL; - return err; - } - - memcpy(wdev->ssid, params->ssid, params->ssid_len); - wdev->ssid_len = params->ssid_len; - - return 0; -} - -int cfg80211_join_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_ibss_params *params, - struct cfg80211_cached_keys *connkeys) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = __cfg80211_join_ibss(rdev, dev, params, connkeys); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int i; - - ASSERT_WDEV_LOCK(wdev); - - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - - /* - * Delete all the keys ... pairwise keys can't really - * exist any more anyway, but default keys might. - */ - if (rdev->ops->del_key) - for (i = 0; i < 6; i++) - rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - } - - wdev->current_bss = NULL; - wdev->ssid_len = 0; -#ifdef CONFIG_CFG80211_WEXT - if (!nowext) - wdev->wext.ibss.ssid_len = 0; -#endif -} - -void cfg80211_clear_ibss(struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_clear_ibss(dev, nowext); - wdev_unlock(wdev); -} - -int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->ssid_len) - return -ENOLINK; - - err = rdev->ops->leave_ibss(&rdev->wiphy, dev); - - if (err) - return err; - - __cfg80211_clear_ibss(dev, nowext); - - return 0; -} - -int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool nowext) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_leave_ibss(rdev, dev, nowext); - wdev_unlock(wdev); - - return err; -} - -#ifdef CONFIG_CFG80211_WEXT -int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) -{ - struct cfg80211_cached_keys *ck = NULL; - enum ieee80211_band band; - int i, err; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->wext.ibss.beacon_interval) - wdev->wext.ibss.beacon_interval = 100; - - /* try to find an IBSS channel if none requested ... */ - if (!wdev->wext.ibss.channel) { - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - - sband = rdev->wiphy.bands[band]; - if (!sband) - continue; - - for (i = 0; i < sband->n_channels; i++) { - chan = &sband->channels[i]; - if (chan->flags & IEEE80211_CHAN_NO_IBSS) - continue; - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - wdev->wext.ibss.channel = chan; - break; - } - - if (wdev->wext.ibss.channel) - break; - } - - if (!wdev->wext.ibss.channel) - return -EINVAL; - } - - /* don't join -- SSID is not there */ - if (!wdev->wext.ibss.ssid_len) - return 0; - - if (!netif_running(wdev->netdev)) - return 0; - - if (wdev->wext.keys) { - wdev->wext.keys->def = wdev->wext.default_key; - wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; - } - - wdev->wext.ibss.privacy = wdev->wext.default_key != -1; - - if (wdev->wext.keys) { - ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); - if (!ck) - return -ENOMEM; - for (i = 0; i < 6; i++) - ck->params[i].key = ck->data[i]; - } - err = __cfg80211_join_ibss(rdev, wdev->netdev, - &wdev->wext.ibss, ck); - if (err) - kfree(ck); - - return err; -} - -int cfg80211_ibss_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan = NULL; - int err, freq; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); - if (freq < 0) - return freq; - - if (freq) { - chan = ieee80211_get_channel(wdev->wiphy, freq); - if (!chan) - return -EINVAL; - if (chan->flags & IEEE80211_CHAN_NO_IBSS || - chan->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - } - - if (wdev->wext.ibss.channel == chan) - return 0; - - wdev_lock(wdev); - err = 0; - if (wdev->ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); - - if (err) - return err; - - if (chan) { - wdev->wext.ibss.channel = chan; - wdev->wext.ibss.channel_fixed = true; - } else { - /* cfg80211_ibss_wext_join will pick one if needed */ - wdev->wext.ibss.channel_fixed = false; - } - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int cfg80211_ibss_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan = NULL; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - wdev_lock(wdev); - if (wdev->current_bss) - chan = wdev->current_bss->pub.channel; - else if (wdev->wext.ibss.channel) - chan = wdev->wext.ibss.channel; - wdev_unlock(wdev); - - if (chan) { - freq->m = chan->center_freq; - freq->e = 6; - return 0; - } - - /* no channel if not joining */ - return -EINVAL; -} - -int cfg80211_ibss_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - size_t len = data->length; - int err; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - wdev_lock(wdev); - err = 0; - if (wdev->ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); - - if (err) - return err; - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - wdev->wext.ibss.ssid = wdev->ssid; - memcpy(wdev->wext.ibss.ssid, ssid, len); - wdev->wext.ibss.ssid_len = len; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int cfg80211_ibss_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - data->flags = 0; - - wdev_lock(wdev); - if (wdev->ssid_len) { - data->flags = 1; - data->length = wdev->ssid_len; - memcpy(ssid, wdev->ssid, data->length); - } else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) { - data->flags = 1; - data->length = wdev->wext.ibss.ssid_len; - memcpy(ssid, wdev->wext.ibss.ssid, data->length); - } - wdev_unlock(wdev); - - return 0; -} - -int cfg80211_ibss_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 *bssid = ap_addr->sa_data; - int err; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - if (ap_addr->sa_family != ARPHRD_ETHER) - return -EINVAL; - - /* automatic mode */ - if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) - bssid = NULL; - - /* both automatic */ - if (!bssid && !wdev->wext.ibss.bssid) - return 0; - - /* fixed already - and no change */ - if (wdev->wext.ibss.bssid && bssid && - compare_ether_addr(bssid, wdev->wext.ibss.bssid) == 0) - return 0; - - wdev_lock(wdev); - err = 0; - if (wdev->ssid_len) - err = __cfg80211_leave_ibss(rdev, dev, true); - wdev_unlock(wdev); - - if (err) - return err; - - if (bssid) { - memcpy(wdev->wext.bssid, bssid, ETH_ALEN); - wdev->wext.ibss.bssid = wdev->wext.bssid; - } else - wdev->wext.ibss.bssid = NULL; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_ibss_wext_join(rdev, wdev); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int cfg80211_ibss_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for ibss! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return -EINVAL; - - ap_addr->sa_family = ARPHRD_ETHER; - - wdev_lock(wdev); - if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); - else if (wdev->wext.ibss.bssid) - memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN); - else - memset(ap_addr->sa_data, 0, ETH_ALEN); - - wdev_unlock(wdev); - - return 0; -} -#endif diff --git a/net/wireless_ath/lib80211.c b/net/wireless_ath/lib80211.c deleted file mode 100755 index a55c27b..0000000 --- a/net/wireless_ath/lib80211.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * lib80211 -- common bits for IEEE802.11 drivers - * - * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com> - * - * Portions copied from old ieee80211 component, w/ original copyright - * notices below: - * - * Host AP crypto routines - * - * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> - * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com> - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/module.h> -#include <linux/ctype.h> -#include <linux/ieee80211.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/string.h> - -#include <net/lib80211.h> - -#define DRV_NAME "lib80211" - -#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers" - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>"); -MODULE_LICENSE("GPL"); - -struct lib80211_crypto_alg { - struct list_head list; - struct lib80211_crypto_ops *ops; -}; - -static LIST_HEAD(lib80211_crypto_algs); -static DEFINE_SPINLOCK(lib80211_crypto_lock); - -static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, - int force); -static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info); -static void lib80211_crypt_deinit_handler(unsigned long data); - -const char *print_ssid(char *buf, const char *ssid, u8 ssid_len) -{ - const char *s = ssid; - char *d = buf; - - ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN); - while (ssid_len--) { - if (isprint(*s)) { - *d++ = *s++; - continue; - } - - *d++ = '\\'; - if (*s == '\0') - *d++ = '0'; - else if (*s == '\n') - *d++ = 'n'; - else if (*s == '\r') - *d++ = 'r'; - else if (*s == '\t') - *d++ = 't'; - else if (*s == '\\') - *d++ = '\\'; - else - d += snprintf(d, 3, "%03o", *s); - s++; - } - *d = '\0'; - return buf; -} -EXPORT_SYMBOL(print_ssid); - -int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name, - spinlock_t *lock) -{ - memset(info, 0, sizeof(*info)); - - info->name = name; - info->lock = lock; - - INIT_LIST_HEAD(&info->crypt_deinit_list); - setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler, - (unsigned long)info); - - return 0; -} -EXPORT_SYMBOL(lib80211_crypt_info_init); - -void lib80211_crypt_info_free(struct lib80211_crypt_info *info) -{ - int i; - - lib80211_crypt_quiescing(info); - del_timer_sync(&info->crypt_deinit_timer); - lib80211_crypt_deinit_entries(info, 1); - - for (i = 0; i < NUM_WEP_KEYS; i++) { - struct lib80211_crypt_data *crypt = info->crypt[i]; - if (crypt) { - if (crypt->ops) { - crypt->ops->deinit(crypt->priv); - module_put(crypt->ops->owner); - } - kfree(crypt); - info->crypt[i] = NULL; - } - } -} -EXPORT_SYMBOL(lib80211_crypt_info_free); - -static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info, - int force) -{ - struct lib80211_crypt_data *entry, *next; - unsigned long flags; - - spin_lock_irqsave(info->lock, flags); - list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) { - if (atomic_read(&entry->refcnt) != 0 && !force) - continue; - - list_del(&entry->list); - - if (entry->ops) { - entry->ops->deinit(entry->priv); - module_put(entry->ops->owner); - } - kfree(entry); - } - spin_unlock_irqrestore(info->lock, flags); -} - -/* After this, crypt_deinit_list won't accept new members */ -static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info) -{ - unsigned long flags; - - spin_lock_irqsave(info->lock, flags); - info->crypt_quiesced = 1; - spin_unlock_irqrestore(info->lock, flags); -} - -static void lib80211_crypt_deinit_handler(unsigned long data) -{ - struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data; - unsigned long flags; - - lib80211_crypt_deinit_entries(info, 0); - - spin_lock_irqsave(info->lock, flags); - if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) { - printk(KERN_DEBUG "%s: entries remaining in delayed crypt " - "deletion list\n", info->name); - info->crypt_deinit_timer.expires = jiffies + HZ; - add_timer(&info->crypt_deinit_timer); - } - spin_unlock_irqrestore(info->lock, flags); -} - -void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info, - struct lib80211_crypt_data **crypt) -{ - struct lib80211_crypt_data *tmp; - unsigned long flags; - - if (*crypt == NULL) - return; - - tmp = *crypt; - *crypt = NULL; - - /* must not run ops->deinit() while there may be pending encrypt or - * decrypt operations. Use a list of delayed deinits to avoid needing - * locking. */ - - spin_lock_irqsave(info->lock, flags); - if (!info->crypt_quiesced) { - list_add(&tmp->list, &info->crypt_deinit_list); - if (!timer_pending(&info->crypt_deinit_timer)) { - info->crypt_deinit_timer.expires = jiffies + HZ; - add_timer(&info->crypt_deinit_timer); - } - } - spin_unlock_irqrestore(info->lock, flags); -} -EXPORT_SYMBOL(lib80211_crypt_delayed_deinit); - -int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops) -{ - unsigned long flags; - struct lib80211_crypto_alg *alg; - - alg = kzalloc(sizeof(*alg), GFP_KERNEL); - if (alg == NULL) - return -ENOMEM; - - alg->ops = ops; - - spin_lock_irqsave(&lib80211_crypto_lock, flags); - list_add(&alg->list, &lib80211_crypto_algs); - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - - printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n", - ops->name); - - return 0; -} -EXPORT_SYMBOL(lib80211_register_crypto_ops); - -int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) -{ - struct lib80211_crypto_alg *alg; - unsigned long flags; - - spin_lock_irqsave(&lib80211_crypto_lock, flags); - list_for_each_entry(alg, &lib80211_crypto_algs, list) { - if (alg->ops == ops) - goto found; - } - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - return -EINVAL; - - found: - printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", - ops->name); - list_del(&alg->list); - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - kfree(alg); - return 0; -} -EXPORT_SYMBOL(lib80211_unregister_crypto_ops); - -struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name) -{ - struct lib80211_crypto_alg *alg; - unsigned long flags; - - spin_lock_irqsave(&lib80211_crypto_lock, flags); - list_for_each_entry(alg, &lib80211_crypto_algs, list) { - if (strcmp(alg->ops->name, name) == 0) - goto found; - } - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - return NULL; - - found: - spin_unlock_irqrestore(&lib80211_crypto_lock, flags); - return alg->ops; -} -EXPORT_SYMBOL(lib80211_get_crypto_ops); - -static void *lib80211_crypt_null_init(int keyidx) -{ - return (void *)1; -} - -static void lib80211_crypt_null_deinit(void *priv) -{ -} - -static struct lib80211_crypto_ops lib80211_crypt_null = { - .name = "NULL", - .init = lib80211_crypt_null_init, - .deinit = lib80211_crypt_null_deinit, - .owner = THIS_MODULE, -}; - -static int __init lib80211_init(void) -{ - pr_info(DRV_DESCRIPTION "\n"); - return lib80211_register_crypto_ops(&lib80211_crypt_null); -} - -static void __exit lib80211_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_null); - BUG_ON(!list_empty(&lib80211_crypto_algs)); -} - -module_init(lib80211_init); -module_exit(lib80211_exit); diff --git a/net/wireless_ath/lib80211_crypt_ccmp.c b/net/wireless_ath/lib80211_crypt_ccmp.c deleted file mode 100755 index 755738d..0000000 --- a/net/wireless_ath/lib80211_crypt_ccmp.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * lib80211 crypt: host-based CCMP encryption implementation for lib80211 - * - * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> - * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for - * more details. - */ - -#include <linux/kernel.h> -#include <linux/err.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/if_ether.h> -#include <linux/if_arp.h> -#include <asm/string.h> -#include <linux/wireless.h> - -#include <linux/ieee80211.h> - -#include <linux/crypto.h> - -#include <net/lib80211.h> - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("Host AP crypt: CCMP"); -MODULE_LICENSE("GPL"); - -#define AES_BLOCK_LEN 16 -#define CCMP_HDR_LEN 8 -#define CCMP_MIC_LEN 8 -#define CCMP_TK_LEN 16 -#define CCMP_PN_LEN 6 - -struct lib80211_ccmp_data { - u8 key[CCMP_TK_LEN]; - int key_set; - - u8 tx_pn[CCMP_PN_LEN]; - u8 rx_pn[CCMP_PN_LEN]; - - u32 dot11RSNAStatsCCMPFormatErrors; - u32 dot11RSNAStatsCCMPReplays; - u32 dot11RSNAStatsCCMPDecryptErrors; - - int key_idx; - - struct crypto_cipher *tfm; - - /* scratch buffers for virt_to_page() (crypto API) */ - u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], - tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; - u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; -}; - -static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm, - const u8 pt[16], u8 ct[16]) -{ - crypto_cipher_encrypt_one(tfm, ct, pt); -} - -static void *lib80211_ccmp_init(int key_idx) -{ - struct lib80211_ccmp_data *priv; - - priv = kzalloc(sizeof(*priv), GFP_ATOMIC); - if (priv == NULL) - goto fail; - priv->key_idx = key_idx; - - priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tfm)) { - priv->tfm = NULL; - goto fail; - } - - return priv; - - fail: - if (priv) { - if (priv->tfm) - crypto_free_cipher(priv->tfm); - kfree(priv); - } - - return NULL; -} - -static void lib80211_ccmp_deinit(void *priv) -{ - struct lib80211_ccmp_data *_priv = priv; - if (_priv && _priv->tfm) - crypto_free_cipher(_priv->tfm); - kfree(priv); -} - -static inline void xor_block(u8 * b, u8 * a, size_t len) -{ - int i; - for (i = 0; i < len; i++) - b[i] ^= a[i]; -} - -static void ccmp_init_blocks(struct crypto_cipher *tfm, - struct ieee80211_hdr *hdr, - u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0) -{ - u8 *pos, qc = 0; - size_t aad_len; - int a4_included, qc_included; - u8 aad[2 * AES_BLOCK_LEN]; - - a4_included = ieee80211_has_a4(hdr->frame_control); - qc_included = ieee80211_is_data_qos(hdr->frame_control); - - aad_len = 22; - if (a4_included) - aad_len += 6; - if (qc_included) { - pos = (u8 *) & hdr->addr4; - if (a4_included) - pos += 6; - qc = *pos & 0x0f; - aad_len += 2; - } - - /* CCM Initial Block: - * Flag (Include authentication header, M=3 (8-octet MIC), - * L=1 (2-octet Dlen)) - * Nonce: 0x00 | A2 | PN - * Dlen */ - b0[0] = 0x59; - b0[1] = qc; - memcpy(b0 + 2, hdr->addr2, ETH_ALEN); - memcpy(b0 + 8, pn, CCMP_PN_LEN); - b0[14] = (dlen >> 8) & 0xff; - b0[15] = dlen & 0xff; - - /* AAD: - * FC with bits 4..6 and 11..13 masked to zero; 14 is always one - * A1 | A2 | A3 - * SC with bits 4..15 (seq#) masked to zero - * A4 (if present) - * QC (if present) - */ - pos = (u8 *) hdr; - aad[0] = 0; /* aad_len >> 8 */ - aad[1] = aad_len & 0xff; - aad[2] = pos[0] & 0x8f; - aad[3] = pos[1] & 0xc7; - memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); - pos = (u8 *) & hdr->seq_ctrl; - aad[22] = pos[0] & 0x0f; - aad[23] = 0; /* all bits masked */ - memset(aad + 24, 0, 8); - if (a4_included) - memcpy(aad + 24, hdr->addr4, ETH_ALEN); - if (qc_included) { - aad[a4_included ? 30 : 24] = qc; - /* rest of QC masked */ - } - - /* Start with the first block and AAD */ - lib80211_ccmp_aes_encrypt(tfm, b0, auth); - xor_block(auth, aad, AES_BLOCK_LEN); - lib80211_ccmp_aes_encrypt(tfm, auth, auth); - xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); - lib80211_ccmp_aes_encrypt(tfm, auth, auth); - b0[0] &= 0x07; - b0[14] = b0[15] = 0; - lib80211_ccmp_aes_encrypt(tfm, b0, s0); -} - -static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len, - u8 *aeskey, int keylen, void *priv) -{ - struct lib80211_ccmp_data *key = priv; - int i; - u8 *pos; - - if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len) - return -1; - - if (aeskey != NULL && keylen >= CCMP_TK_LEN) - memcpy(aeskey, key->key, CCMP_TK_LEN); - - pos = skb_push(skb, CCMP_HDR_LEN); - memmove(pos, pos + CCMP_HDR_LEN, hdr_len); - pos += hdr_len; - - i = CCMP_PN_LEN - 1; - while (i >= 0) { - key->tx_pn[i]++; - if (key->tx_pn[i] != 0) - break; - i--; - } - - *pos++ = key->tx_pn[5]; - *pos++ = key->tx_pn[4]; - *pos++ = 0; - *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ; - *pos++ = key->tx_pn[3]; - *pos++ = key->tx_pn[2]; - *pos++ = key->tx_pn[1]; - *pos++ = key->tx_pn[0]; - - return CCMP_HDR_LEN; -} - -static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_ccmp_data *key = priv; - int data_len, i, blocks, last, len; - u8 *pos, *mic; - struct ieee80211_hdr *hdr; - u8 *b0 = key->tx_b0; - u8 *b = key->tx_b; - u8 *e = key->tx_e; - u8 *s0 = key->tx_s0; - - if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len) - return -1; - - data_len = skb->len - hdr_len; - len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv); - if (len < 0) - return -1; - - pos = skb->data + hdr_len + CCMP_HDR_LEN; - hdr = (struct ieee80211_hdr *)skb->data; - ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); - - blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); - last = data_len % AES_BLOCK_LEN; - - for (i = 1; i <= blocks; i++) { - len = (i == blocks && last) ? last : AES_BLOCK_LEN; - /* Authentication */ - xor_block(b, pos, len); - lib80211_ccmp_aes_encrypt(key->tfm, b, b); - /* Encryption, with counter */ - b0[14] = (i >> 8) & 0xff; - b0[15] = i & 0xff; - lib80211_ccmp_aes_encrypt(key->tfm, b0, e); - xor_block(pos, e, len); - pos += len; - } - - mic = skb_put(skb, CCMP_MIC_LEN); - for (i = 0; i < CCMP_MIC_LEN; i++) - mic[i] = b[i] ^ s0[i]; - - return 0; -} - -/* - * deal with seq counter wrapping correctly. - * refer to timer_after() for jiffies wrapping handling - */ -static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) -{ - u32 iv32_n, iv16_n; - u32 iv32_o, iv16_o; - - iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; - iv16_n = (pn_n[4] << 8) | pn_n[5]; - - iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; - iv16_o = (pn_o[4] << 8) | pn_o[5]; - - if ((s32)iv32_n - (s32)iv32_o < 0 || - (iv32_n == iv32_o && iv16_n <= iv16_o)) - return 1; - return 0; -} - -static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_ccmp_data *key = priv; - u8 keyidx, *pos; - struct ieee80211_hdr *hdr; - u8 *b0 = key->rx_b0; - u8 *b = key->rx_b; - u8 *a = key->rx_a; - u8 pn[6]; - int i, blocks, last, len; - size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; - u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; - - if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { - key->dot11RSNAStatsCCMPFormatErrors++; - return -1; - } - - hdr = (struct ieee80211_hdr *)skb->data; - pos = skb->data + hdr_len; - keyidx = pos[3]; - if (!(keyidx & (1 << 5))) { - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: received packet without ExtIV" - " flag from %pM\n", hdr->addr2); - } - key->dot11RSNAStatsCCMPFormatErrors++; - return -2; - } - keyidx >>= 6; - if (key->key_idx != keyidx) { - printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " - "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); - return -6; - } - if (!key->key_set) { - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: received packet from %pM" - " with keyid=%d that does not have a configured" - " key\n", hdr->addr2, keyidx); - } - return -3; - } - - pn[0] = pos[7]; - pn[1] = pos[6]; - pn[2] = pos[5]; - pn[3] = pos[4]; - pn[4] = pos[1]; - pn[5] = pos[0]; - pos += 8; - - if (ccmp_replay_check(pn, key->rx_pn)) { -#ifdef CONFIG_LIB80211_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: replay detected: STA=%pM " - "previous PN %02x%02x%02x%02x%02x%02x " - "received PN %02x%02x%02x%02x%02x%02x\n", - hdr->addr2, - key->rx_pn[0], key->rx_pn[1], key->rx_pn[2], - key->rx_pn[3], key->rx_pn[4], key->rx_pn[5], - pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]); - } -#endif - key->dot11RSNAStatsCCMPReplays++; - return -4; - } - - ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); - xor_block(mic, b, CCMP_MIC_LEN); - - blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN); - last = data_len % AES_BLOCK_LEN; - - for (i = 1; i <= blocks; i++) { - len = (i == blocks && last) ? last : AES_BLOCK_LEN; - /* Decrypt, with counter */ - b0[14] = (i >> 8) & 0xff; - b0[15] = i & 0xff; - lib80211_ccmp_aes_encrypt(key->tfm, b0, b); - xor_block(pos, b, len); - /* Authentication */ - xor_block(a, pos, len); - lib80211_ccmp_aes_encrypt(key->tfm, a, a); - pos += len; - } - - if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { - if (net_ratelimit()) { - printk(KERN_DEBUG "CCMP: decrypt failed: STA=" - "%pM\n", hdr->addr2); - } - key->dot11RSNAStatsCCMPDecryptErrors++; - return -5; - } - - memcpy(key->rx_pn, pn, CCMP_PN_LEN); - - /* Remove hdr and MIC */ - memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); - skb_pull(skb, CCMP_HDR_LEN); - skb_trim(skb, skb->len - CCMP_MIC_LEN); - - return keyidx; -} - -static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_ccmp_data *data = priv; - int keyidx; - struct crypto_cipher *tfm = data->tfm; - - keyidx = data->key_idx; - memset(data, 0, sizeof(*data)); - data->key_idx = keyidx; - data->tfm = tfm; - if (len == CCMP_TK_LEN) { - memcpy(data->key, key, CCMP_TK_LEN); - data->key_set = 1; - if (seq) { - data->rx_pn[0] = seq[5]; - data->rx_pn[1] = seq[4]; - data->rx_pn[2] = seq[3]; - data->rx_pn[3] = seq[2]; - data->rx_pn[4] = seq[1]; - data->rx_pn[5] = seq[0]; - } - crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); - } else if (len == 0) - data->key_set = 0; - else - return -1; - - return 0; -} - -static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_ccmp_data *data = priv; - - if (len < CCMP_TK_LEN) - return -1; - - if (!data->key_set) - return 0; - memcpy(key, data->key, CCMP_TK_LEN); - - if (seq) { - seq[0] = data->tx_pn[5]; - seq[1] = data->tx_pn[4]; - seq[2] = data->tx_pn[3]; - seq[3] = data->tx_pn[2]; - seq[4] = data->tx_pn[1]; - seq[5] = data->tx_pn[0]; - } - - return CCMP_TK_LEN; -} - -static char *lib80211_ccmp_print_stats(char *p, void *priv) -{ - struct lib80211_ccmp_data *ccmp = priv; - - p += sprintf(p, "key[%d] alg=CCMP key_set=%d " - "tx_pn=%02x%02x%02x%02x%02x%02x " - "rx_pn=%02x%02x%02x%02x%02x%02x " - "format_errors=%d replays=%d decrypt_errors=%d\n", - ccmp->key_idx, ccmp->key_set, - ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2], - ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5], - ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2], - ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5], - ccmp->dot11RSNAStatsCCMPFormatErrors, - ccmp->dot11RSNAStatsCCMPReplays, - ccmp->dot11RSNAStatsCCMPDecryptErrors); - - return p; -} - -static struct lib80211_crypto_ops lib80211_crypt_ccmp = { - .name = "CCMP", - .init = lib80211_ccmp_init, - .deinit = lib80211_ccmp_deinit, - .encrypt_mpdu = lib80211_ccmp_encrypt, - .decrypt_mpdu = lib80211_ccmp_decrypt, - .encrypt_msdu = NULL, - .decrypt_msdu = NULL, - .set_key = lib80211_ccmp_set_key, - .get_key = lib80211_ccmp_get_key, - .print_stats = lib80211_ccmp_print_stats, - .extra_mpdu_prefix_len = CCMP_HDR_LEN, - .extra_mpdu_postfix_len = CCMP_MIC_LEN, - .owner = THIS_MODULE, -}; - -static int __init lib80211_crypto_ccmp_init(void) -{ - return lib80211_register_crypto_ops(&lib80211_crypt_ccmp); -} - -static void __exit lib80211_crypto_ccmp_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp); -} - -module_init(lib80211_crypto_ccmp_init); -module_exit(lib80211_crypto_ccmp_exit); diff --git a/net/wireless_ath/lib80211_crypt_tkip.c b/net/wireless_ath/lib80211_crypt_tkip.c deleted file mode 100755 index 3873484..0000000 --- a/net/wireless_ath/lib80211_crypt_tkip.c +++ /dev/null @@ -1,780 +0,0 @@ -/* - * lib80211 crypt: host-based TKIP encryption implementation for lib80211 - * - * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> - * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for - * more details. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/err.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/scatterlist.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/mm.h> -#include <linux/if_ether.h> -#include <linux/if_arp.h> -#include <asm/string.h> - -#include <linux/wireless.h> -#include <linux/ieee80211.h> -#include <net/iw_handler.h> - -#include <linux/crypto.h> -#include <linux/crc32.h> - -#include <net/lib80211.h> - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("lib80211 crypt: TKIP"); -MODULE_LICENSE("GPL"); - -#define TKIP_HDR_LEN 8 - -struct lib80211_tkip_data { -#define TKIP_KEY_LEN 32 - u8 key[TKIP_KEY_LEN]; - int key_set; - - u32 tx_iv32; - u16 tx_iv16; - u16 tx_ttak[5]; - int tx_phase1_done; - - u32 rx_iv32; - u16 rx_iv16; - u16 rx_ttak[5]; - int rx_phase1_done; - u32 rx_iv32_new; - u16 rx_iv16_new; - - u32 dot11RSNAStatsTKIPReplays; - u32 dot11RSNAStatsTKIPICVErrors; - u32 dot11RSNAStatsTKIPLocalMICFailures; - - int key_idx; - - struct crypto_blkcipher *rx_tfm_arc4; - struct crypto_hash *rx_tfm_michael; - struct crypto_blkcipher *tx_tfm_arc4; - struct crypto_hash *tx_tfm_michael; - - /* scratch buffers for virt_to_page() (crypto API) */ - u8 rx_hdr[16], tx_hdr[16]; - - unsigned long flags; -}; - -static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv) -{ - struct lib80211_tkip_data *_priv = priv; - unsigned long old_flags = _priv->flags; - _priv->flags = flags; - return old_flags; -} - -static unsigned long lib80211_tkip_get_flags(void *priv) -{ - struct lib80211_tkip_data *_priv = priv; - return _priv->flags; -} - -static void *lib80211_tkip_init(int key_idx) -{ - struct lib80211_tkip_data *priv; - - priv = kzalloc(sizeof(*priv), GFP_ATOMIC); - if (priv == NULL) - goto fail; - - priv->key_idx = key_idx; - - priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tx_tfm_arc4)) { - priv->tx_tfm_arc4 = NULL; - goto fail; - } - - priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tx_tfm_michael)) { - priv->tx_tfm_michael = NULL; - goto fail; - } - - priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->rx_tfm_arc4)) { - priv->rx_tfm_arc4 = NULL; - goto fail; - } - - priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, - CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->rx_tfm_michael)) { - priv->rx_tfm_michael = NULL; - goto fail; - } - - return priv; - - fail: - if (priv) { - if (priv->tx_tfm_michael) - crypto_free_hash(priv->tx_tfm_michael); - if (priv->tx_tfm_arc4) - crypto_free_blkcipher(priv->tx_tfm_arc4); - if (priv->rx_tfm_michael) - crypto_free_hash(priv->rx_tfm_michael); - if (priv->rx_tfm_arc4) - crypto_free_blkcipher(priv->rx_tfm_arc4); - kfree(priv); - } - - return NULL; -} - -static void lib80211_tkip_deinit(void *priv) -{ - struct lib80211_tkip_data *_priv = priv; - if (_priv) { - if (_priv->tx_tfm_michael) - crypto_free_hash(_priv->tx_tfm_michael); - if (_priv->tx_tfm_arc4) - crypto_free_blkcipher(_priv->tx_tfm_arc4); - if (_priv->rx_tfm_michael) - crypto_free_hash(_priv->rx_tfm_michael); - if (_priv->rx_tfm_arc4) - crypto_free_blkcipher(_priv->rx_tfm_arc4); - } - kfree(priv); -} - -static inline u16 RotR1(u16 val) -{ - return (val >> 1) | (val << 15); -} - -static inline u8 Lo8(u16 val) -{ - return val & 0xff; -} - -static inline u8 Hi8(u16 val) -{ - return val >> 8; -} - -static inline u16 Lo16(u32 val) -{ - return val & 0xffff; -} - -static inline u16 Hi16(u32 val) -{ - return val >> 16; -} - -static inline u16 Mk16(u8 hi, u8 lo) -{ - return lo | (((u16) hi) << 8); -} - -static inline u16 Mk16_le(__le16 * v) -{ - return le16_to_cpu(*v); -} - -static const u16 Sbox[256] = { - 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, - 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, - 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, - 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, - 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, - 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, - 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, - 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, - 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, - 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, - 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, - 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, - 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, - 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, - 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, - 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, - 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, - 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, - 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, - 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, - 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, - 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, - 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, - 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, - 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, - 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, - 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, - 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, - 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, - 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, - 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, - 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, -}; - -static inline u16 _S_(u16 v) -{ - u16 t = Sbox[Hi8(v)]; - return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); -} - -#define PHASE1_LOOP_COUNT 8 - -static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA, - u32 IV32) -{ - int i, j; - - /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ - TTAK[0] = Lo16(IV32); - TTAK[1] = Hi16(IV32); - TTAK[2] = Mk16(TA[1], TA[0]); - TTAK[3] = Mk16(TA[3], TA[2]); - TTAK[4] = Mk16(TA[5], TA[4]); - - for (i = 0; i < PHASE1_LOOP_COUNT; i++) { - j = 2 * (i & 1); - TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); - TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); - TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); - TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); - TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; - } -} - -static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK, - u16 IV16) -{ - /* Make temporary area overlap WEP seed so that the final copy can be - * avoided on little endian hosts. */ - u16 *PPK = (u16 *) & WEPSeed[4]; - - /* Step 1 - make copy of TTAK and bring in TSC */ - PPK[0] = TTAK[0]; - PPK[1] = TTAK[1]; - PPK[2] = TTAK[2]; - PPK[3] = TTAK[3]; - PPK[4] = TTAK[4]; - PPK[5] = TTAK[4] + IV16; - - /* Step 2 - 96-bit bijective mixing using S-box */ - PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0])); - PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2])); - PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4])); - PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6])); - PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8])); - PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10])); - - PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12])); - PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14])); - PPK[2] += RotR1(PPK[1]); - PPK[3] += RotR1(PPK[2]); - PPK[4] += RotR1(PPK[3]); - PPK[5] += RotR1(PPK[4]); - - /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value - * WEPSeed[0..2] is transmitted as WEP IV */ - WEPSeed[0] = Hi8(IV16); - WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; - WEPSeed[2] = Lo8(IV16); - WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1); - -#ifdef __BIG_ENDIAN - { - int i; - for (i = 0; i < 6; i++) - PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); - } -#endif -} - -static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len, - u8 * rc4key, int keylen, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - u8 *pos; - struct ieee80211_hdr *hdr; - - hdr = (struct ieee80211_hdr *)skb->data; - - if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len) - return -1; - - if (rc4key == NULL || keylen < 16) - return -1; - - if (!tkey->tx_phase1_done) { - tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, - tkey->tx_iv32); - tkey->tx_phase1_done = 1; - } - tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); - - pos = skb_push(skb, TKIP_HDR_LEN); - memmove(pos, pos + TKIP_HDR_LEN, hdr_len); - pos += hdr_len; - - *pos++ = *rc4key; - *pos++ = *(rc4key + 1); - *pos++ = *(rc4key + 2); - *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ; - *pos++ = tkey->tx_iv32 & 0xff; - *pos++ = (tkey->tx_iv32 >> 8) & 0xff; - *pos++ = (tkey->tx_iv32 >> 16) & 0xff; - *pos++ = (tkey->tx_iv32 >> 24) & 0xff; - - tkey->tx_iv16++; - if (tkey->tx_iv16 == 0) { - tkey->tx_phase1_done = 0; - tkey->tx_iv32++; - } - - return TKIP_HDR_LEN; -} - -static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 }; - int len; - u8 rc4key[16], *pos, *icv; - u32 crc; - struct scatterlist sg; - - if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { - if (net_ratelimit()) { - struct ieee80211_hdr *hdr = - (struct ieee80211_hdr *)skb->data; - printk(KERN_DEBUG ": TKIP countermeasures: dropped " - "TX packet to %pM\n", hdr->addr1); - } - return -1; - } - - if (skb_tailroom(skb) < 4 || skb->len < hdr_len) - return -1; - - len = skb->len - hdr_len; - pos = skb->data + hdr_len; - - if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0) - return -1; - - crc = ~crc32_le(~0, pos, len); - icv = skb_put(skb, 4); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - - crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); - sg_init_one(&sg, pos, len + 4); - return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); -} - -/* - * deal with seq counter wrapping correctly. - * refer to timer_after() for jiffies wrapping handling - */ -static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, - u32 iv32_o, u16 iv16_o) -{ - if ((s32)iv32_n - (s32)iv32_o < 0 || - (iv32_n == iv32_o && iv16_n <= iv16_o)) - return 1; - return 0; -} - -static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 }; - u8 rc4key[16]; - u8 keyidx, *pos; - u32 iv32; - u16 iv16; - struct ieee80211_hdr *hdr; - u8 icv[4]; - u32 crc; - struct scatterlist sg; - int plen; - - hdr = (struct ieee80211_hdr *)skb->data; - - if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) { - if (net_ratelimit()) { - printk(KERN_DEBUG ": TKIP countermeasures: dropped " - "received packet from %pM\n", hdr->addr2); - } - return -1; - } - - if (skb->len < hdr_len + TKIP_HDR_LEN + 4) - return -1; - - pos = skb->data + hdr_len; - keyidx = pos[3]; - if (!(keyidx & (1 << 5))) { - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: received packet without ExtIV" - " flag from %pM\n", hdr->addr2); - } - return -2; - } - keyidx >>= 6; - if (tkey->key_idx != keyidx) { - printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " - "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); - return -6; - } - if (!tkey->key_set) { - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: received packet from %pM" - " with keyid=%d that does not have a configured" - " key\n", hdr->addr2, keyidx); - } - return -3; - } - iv16 = (pos[0] << 8) | pos[2]; - iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); - pos += TKIP_HDR_LEN; - - if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { -#ifdef CONFIG_LIB80211_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: replay detected: STA=%pM" - " previous TSC %08x%04x received TSC " - "%08x%04x\n", hdr->addr2, - tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); - } -#endif - tkey->dot11RSNAStatsTKIPReplays++; - return -4; - } - - if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { - tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); - tkey->rx_phase1_done = 1; - } - tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); - - plen = skb->len - hdr_len - 12; - - crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); - sg_init_one(&sg, pos, plen + 4); - if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) { - if (net_ratelimit()) { - printk(KERN_DEBUG ": TKIP: failed to decrypt " - "received packet from %pM\n", - hdr->addr2); - } - return -7; - } - - crc = ~crc32_le(~0, pos, plen); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - if (memcmp(icv, pos + plen, 4) != 0) { - if (iv32 != tkey->rx_iv32) { - /* Previously cached Phase1 result was already lost, so - * it needs to be recalculated for the next packet. */ - tkey->rx_phase1_done = 0; - } -#ifdef CONFIG_LIB80211_DEBUG - if (net_ratelimit()) { - printk(KERN_DEBUG "TKIP: ICV error detected: STA=" - "%pM\n", hdr->addr2); - } -#endif - tkey->dot11RSNAStatsTKIPICVErrors++; - return -5; - } - - /* Update real counters only after Michael MIC verification has - * completed */ - tkey->rx_iv32_new = iv32; - tkey->rx_iv16_new = iv16; - - /* Remove IV and ICV */ - memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len); - skb_pull(skb, TKIP_HDR_LEN); - skb_trim(skb, skb->len - 4); - - return keyidx; -} - -static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, - u8 * data, size_t data_len, u8 * mic) -{ - struct hash_desc desc; - struct scatterlist sg[2]; - - if (tfm_michael == NULL) { - pr_warn("%s(): tfm_michael == NULL\n", __func__); - return -1; - } - sg_init_table(sg, 2); - sg_set_buf(&sg[0], hdr, 16); - sg_set_buf(&sg[1], data, data_len); - - if (crypto_hash_setkey(tfm_michael, key, 8)) - return -1; - - desc.tfm = tfm_michael; - desc.flags = 0; - return crypto_hash_digest(&desc, sg, data_len + 16, mic); -} - -static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) -{ - struct ieee80211_hdr *hdr11; - - hdr11 = (struct ieee80211_hdr *)skb->data; - - switch (le16_to_cpu(hdr11->frame_control) & - (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { - case IEEE80211_FCTL_TODS: - memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ - break; - case IEEE80211_FCTL_FROMDS: - memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ - break; - case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: - memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ - break; - case 0: - memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ - memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ - break; - } - - if (ieee80211_is_data_qos(hdr11->frame_control)) { - hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11))) - & IEEE80211_QOS_CTL_TID_MASK; - } else - hdr[12] = 0; /* priority */ - - hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ -} - -static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len, - void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - u8 *pos; - - if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { - printk(KERN_DEBUG "Invalid packet for Michael MIC add " - "(tailroom=%d hdr_len=%d skb->len=%d)\n", - skb_tailroom(skb), hdr_len, skb->len); - return -1; - } - - michael_mic_hdr(skb, tkey->tx_hdr); - pos = skb_put(skb, 8); - if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, - skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) - return -1; - - return 0; -} - -static void lib80211_michael_mic_failure(struct net_device *dev, - struct ieee80211_hdr *hdr, - int keyidx) -{ - union iwreq_data wrqu; - struct iw_michaelmicfailure ev; - - /* TODO: needed parameters: count, keyid, key type, TSC */ - memset(&ev, 0, sizeof(ev)); - ev.flags = keyidx & IW_MICFAILURE_KEY_ID; - if (hdr->addr1[0] & 0x01) - ev.flags |= IW_MICFAILURE_GROUP; - else - ev.flags |= IW_MICFAILURE_PAIRWISE; - ev.src_addr.sa_family = ARPHRD_ETHER; - memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = sizeof(ev); - wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev); -} - -static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx, - int hdr_len, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - u8 mic[8]; - - if (!tkey->key_set) - return -1; - - michael_mic_hdr(skb, tkey->rx_hdr); - if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, - skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) - return -1; - if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { - struct ieee80211_hdr *hdr; - hdr = (struct ieee80211_hdr *)skb->data; - printk(KERN_DEBUG "%s: Michael MIC verification failed for " - "MSDU from %pM keyidx=%d\n", - skb->dev ? skb->dev->name : "N/A", hdr->addr2, - keyidx); - if (skb->dev) - lib80211_michael_mic_failure(skb->dev, hdr, keyidx); - tkey->dot11RSNAStatsTKIPLocalMICFailures++; - return -1; - } - - /* Update TSC counters for RX now that the packet verification has - * completed. */ - tkey->rx_iv32 = tkey->rx_iv32_new; - tkey->rx_iv16 = tkey->rx_iv16_new; - - skb_trim(skb, skb->len - 8); - - return 0; -} - -static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - int keyidx; - struct crypto_hash *tfm = tkey->tx_tfm_michael; - struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4; - struct crypto_hash *tfm3 = tkey->rx_tfm_michael; - struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4; - - keyidx = tkey->key_idx; - memset(tkey, 0, sizeof(*tkey)); - tkey->key_idx = keyidx; - tkey->tx_tfm_michael = tfm; - tkey->tx_tfm_arc4 = tfm2; - tkey->rx_tfm_michael = tfm3; - tkey->rx_tfm_arc4 = tfm4; - if (len == TKIP_KEY_LEN) { - memcpy(tkey->key, key, TKIP_KEY_LEN); - tkey->key_set = 1; - tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ - if (seq) { - tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | - (seq[3] << 8) | seq[2]; - tkey->rx_iv16 = (seq[1] << 8) | seq[0]; - } - } else if (len == 0) - tkey->key_set = 0; - else - return -1; - - return 0; -} - -static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_tkip_data *tkey = priv; - - if (len < TKIP_KEY_LEN) - return -1; - - if (!tkey->key_set) - return 0; - memcpy(key, tkey->key, TKIP_KEY_LEN); - - if (seq) { - /* Return the sequence number of the last transmitted frame. */ - u16 iv16 = tkey->tx_iv16; - u32 iv32 = tkey->tx_iv32; - if (iv16 == 0) - iv32--; - iv16--; - seq[0] = tkey->tx_iv16; - seq[1] = tkey->tx_iv16 >> 8; - seq[2] = tkey->tx_iv32; - seq[3] = tkey->tx_iv32 >> 8; - seq[4] = tkey->tx_iv32 >> 16; - seq[5] = tkey->tx_iv32 >> 24; - } - - return TKIP_KEY_LEN; -} - -static char *lib80211_tkip_print_stats(char *p, void *priv) -{ - struct lib80211_tkip_data *tkip = priv; - p += sprintf(p, "key[%d] alg=TKIP key_set=%d " - "tx_pn=%02x%02x%02x%02x%02x%02x " - "rx_pn=%02x%02x%02x%02x%02x%02x " - "replays=%d icv_errors=%d local_mic_failures=%d\n", - tkip->key_idx, tkip->key_set, - (tkip->tx_iv32 >> 24) & 0xff, - (tkip->tx_iv32 >> 16) & 0xff, - (tkip->tx_iv32 >> 8) & 0xff, - tkip->tx_iv32 & 0xff, - (tkip->tx_iv16 >> 8) & 0xff, - tkip->tx_iv16 & 0xff, - (tkip->rx_iv32 >> 24) & 0xff, - (tkip->rx_iv32 >> 16) & 0xff, - (tkip->rx_iv32 >> 8) & 0xff, - tkip->rx_iv32 & 0xff, - (tkip->rx_iv16 >> 8) & 0xff, - tkip->rx_iv16 & 0xff, - tkip->dot11RSNAStatsTKIPReplays, - tkip->dot11RSNAStatsTKIPICVErrors, - tkip->dot11RSNAStatsTKIPLocalMICFailures); - return p; -} - -static struct lib80211_crypto_ops lib80211_crypt_tkip = { - .name = "TKIP", - .init = lib80211_tkip_init, - .deinit = lib80211_tkip_deinit, - .encrypt_mpdu = lib80211_tkip_encrypt, - .decrypt_mpdu = lib80211_tkip_decrypt, - .encrypt_msdu = lib80211_michael_mic_add, - .decrypt_msdu = lib80211_michael_mic_verify, - .set_key = lib80211_tkip_set_key, - .get_key = lib80211_tkip_get_key, - .print_stats = lib80211_tkip_print_stats, - .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */ - .extra_mpdu_postfix_len = 4, /* ICV */ - .extra_msdu_postfix_len = 8, /* MIC */ - .get_flags = lib80211_tkip_get_flags, - .set_flags = lib80211_tkip_set_flags, - .owner = THIS_MODULE, -}; - -static int __init lib80211_crypto_tkip_init(void) -{ - return lib80211_register_crypto_ops(&lib80211_crypt_tkip); -} - -static void __exit lib80211_crypto_tkip_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_tkip); -} - -module_init(lib80211_crypto_tkip_init); -module_exit(lib80211_crypto_tkip_exit); diff --git a/net/wireless_ath/lib80211_crypt_wep.c b/net/wireless_ath/lib80211_crypt_wep.c deleted file mode 100755 index c130401..0000000 --- a/net/wireless_ath/lib80211_crypt_wep.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * lib80211 crypt: host-based WEP encryption implementation for lib80211 - * - * Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi> - * Copyright (c) 2008, John W. Linville <linville@tuxdriver.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. See README and COPYING for - * more details. - */ - -#include <linux/err.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/random.h> -#include <linux/scatterlist.h> -#include <linux/skbuff.h> -#include <linux/mm.h> -#include <asm/string.h> - -#include <net/lib80211.h> - -#include <linux/crypto.h> -#include <linux/crc32.h> - -MODULE_AUTHOR("Jouni Malinen"); -MODULE_DESCRIPTION("lib80211 crypt: WEP"); -MODULE_LICENSE("GPL"); - -struct lib80211_wep_data { - u32 iv; -#define WEP_KEY_LEN 13 - u8 key[WEP_KEY_LEN + 1]; - u8 key_len; - u8 key_idx; - struct crypto_blkcipher *tx_tfm; - struct crypto_blkcipher *rx_tfm; -}; - -static void *lib80211_wep_init(int keyidx) -{ - struct lib80211_wep_data *priv; - - priv = kzalloc(sizeof(*priv), GFP_ATOMIC); - if (priv == NULL) - goto fail; - priv->key_idx = keyidx; - - priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->tx_tfm)) { - priv->tx_tfm = NULL; - goto fail; - } - - priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(priv->rx_tfm)) { - priv->rx_tfm = NULL; - goto fail; - } - /* start WEP IV from a random value */ - get_random_bytes(&priv->iv, 4); - - return priv; - - fail: - if (priv) { - if (priv->tx_tfm) - crypto_free_blkcipher(priv->tx_tfm); - if (priv->rx_tfm) - crypto_free_blkcipher(priv->rx_tfm); - kfree(priv); - } - return NULL; -} - -static void lib80211_wep_deinit(void *priv) -{ - struct lib80211_wep_data *_priv = priv; - if (_priv) { - if (_priv->tx_tfm) - crypto_free_blkcipher(_priv->tx_tfm); - if (_priv->rx_tfm) - crypto_free_blkcipher(_priv->rx_tfm); - } - kfree(priv); -} - -/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */ -static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len, - u8 *key, int keylen, void *priv) -{ - struct lib80211_wep_data *wep = priv; - u32 klen; - u8 *pos; - - if (skb_headroom(skb) < 4 || skb->len < hdr_len) - return -1; - - pos = skb_push(skb, 4); - memmove(pos, pos + 4, hdr_len); - pos += hdr_len; - - klen = 3 + wep->key_len; - - wep->iv++; - - /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key - * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) - * can be used to speedup attacks, so avoid using them. */ - if ((wep->iv & 0xff00) == 0xff00) { - u8 B = (wep->iv >> 16) & 0xff; - if (B >= 3 && B < klen) - wep->iv += 0x0100; - } - - /* Prepend 24-bit IV to RC4 key and TX frame */ - *pos++ = (wep->iv >> 16) & 0xff; - *pos++ = (wep->iv >> 8) & 0xff; - *pos++ = wep->iv & 0xff; - *pos++ = wep->key_idx << 6; - - return 0; -} - -/* Perform WEP encryption on given skb that has at least 4 bytes of headroom - * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, - * so the payload length increases with 8 bytes. - * - * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) - */ -static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_wep_data *wep = priv; - struct blkcipher_desc desc = { .tfm = wep->tx_tfm }; - u32 crc, klen, len; - u8 *pos, *icv; - struct scatterlist sg; - u8 key[WEP_KEY_LEN + 3]; - - /* other checks are in lib80211_wep_build_iv */ - if (skb_tailroom(skb) < 4) - return -1; - - /* add the IV to the frame */ - if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv)) - return -1; - - /* Copy the IV into the first 3 bytes of the key */ - skb_copy_from_linear_data_offset(skb, hdr_len, key, 3); - - /* Copy rest of the WEP key (the secret part) */ - memcpy(key + 3, wep->key, wep->key_len); - - len = skb->len - hdr_len - 4; - pos = skb->data + hdr_len + 4; - klen = 3 + wep->key_len; - - /* Append little-endian CRC32 over only the data and encrypt it to produce ICV */ - crc = ~crc32_le(~0, pos, len); - icv = skb_put(skb, 4); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - - crypto_blkcipher_setkey(wep->tx_tfm, key, klen); - sg_init_one(&sg, pos, len + 4); - return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4); -} - -/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of - * the frame: IV (4 bytes), encrypted payload (including SNAP header), - * ICV (4 bytes). len includes both IV and ICV. - * - * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on - * failure. If frame is OK, IV and ICV will be removed. - */ -static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) -{ - struct lib80211_wep_data *wep = priv; - struct blkcipher_desc desc = { .tfm = wep->rx_tfm }; - u32 crc, klen, plen; - u8 key[WEP_KEY_LEN + 3]; - u8 keyidx, *pos, icv[4]; - struct scatterlist sg; - - if (skb->len < hdr_len + 8) - return -1; - - pos = skb->data + hdr_len; - key[0] = *pos++; - key[1] = *pos++; - key[2] = *pos++; - keyidx = *pos++ >> 6; - if (keyidx != wep->key_idx) - return -1; - - klen = 3 + wep->key_len; - - /* Copy rest of the WEP key (the secret part) */ - memcpy(key + 3, wep->key, wep->key_len); - - /* Apply RC4 to data and compute CRC32 over decrypted data */ - plen = skb->len - hdr_len - 8; - - crypto_blkcipher_setkey(wep->rx_tfm, key, klen); - sg_init_one(&sg, pos, plen + 4); - if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) - return -7; - - crc = ~crc32_le(~0, pos, plen); - icv[0] = crc; - icv[1] = crc >> 8; - icv[2] = crc >> 16; - icv[3] = crc >> 24; - if (memcmp(icv, pos + plen, 4) != 0) { - /* ICV mismatch - drop frame */ - return -2; - } - - /* Remove IV and ICV */ - memmove(skb->data + 4, skb->data, hdr_len); - skb_pull(skb, 4); - skb_trim(skb, skb->len - 4); - - return 0; -} - -static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_wep_data *wep = priv; - - if (len < 0 || len > WEP_KEY_LEN) - return -1; - - memcpy(wep->key, key, len); - wep->key_len = len; - - return 0; -} - -static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv) -{ - struct lib80211_wep_data *wep = priv; - - if (len < wep->key_len) - return -1; - - memcpy(key, wep->key, wep->key_len); - - return wep->key_len; -} - -static char *lib80211_wep_print_stats(char *p, void *priv) -{ - struct lib80211_wep_data *wep = priv; - p += sprintf(p, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); - return p; -} - -static struct lib80211_crypto_ops lib80211_crypt_wep = { - .name = "WEP", - .init = lib80211_wep_init, - .deinit = lib80211_wep_deinit, - .encrypt_mpdu = lib80211_wep_encrypt, - .decrypt_mpdu = lib80211_wep_decrypt, - .encrypt_msdu = NULL, - .decrypt_msdu = NULL, - .set_key = lib80211_wep_set_key, - .get_key = lib80211_wep_get_key, - .print_stats = lib80211_wep_print_stats, - .extra_mpdu_prefix_len = 4, /* IV */ - .extra_mpdu_postfix_len = 4, /* ICV */ - .owner = THIS_MODULE, -}; - -static int __init lib80211_crypto_wep_init(void) -{ - return lib80211_register_crypto_ops(&lib80211_crypt_wep); -} - -static void __exit lib80211_crypto_wep_exit(void) -{ - lib80211_unregister_crypto_ops(&lib80211_crypt_wep); -} - -module_init(lib80211_crypto_wep_init); -module_exit(lib80211_crypto_wep_exit); diff --git a/net/wireless_ath/mesh.c b/net/wireless_ath/mesh.c deleted file mode 100755 index ba5337d..0000000 --- a/net/wireless_ath/mesh.c +++ /dev/null @@ -1,165 +0,0 @@ -#include <linux/ieee80211.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include "nl80211.h" -#include "core.h" - -/* Default values, timeouts in ms */ -#define MESH_TTL 31 -#define MESH_DEFAULT_ELEMENT_TTL 31 -#define MESH_MAX_RETR 3 -#define MESH_RET_T 100 -#define MESH_CONF_T 100 -#define MESH_HOLD_T 100 - -#define MESH_PATH_TIMEOUT 5000 -#define MESH_RANN_INTERVAL 5000 - -/* - * Minimum interval between two consecutive PREQs originated by the same - * interface - */ -#define MESH_PREQ_MIN_INT 10 -#define MESH_DIAM_TRAVERSAL_TIME 50 - -/* - * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds - * before timing out. This way it will remain ACTIVE and no data frames - * will be unnecessarily held in the pending queue. - */ -#define MESH_PATH_REFRESH_TIME 1000 -#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) - -/* Default maximum number of established plinks per interface */ -#define MESH_MAX_ESTAB_PLINKS 32 - -#define MESH_MAX_PREQ_RETRIES 4 - - -const struct mesh_config default_mesh_config = { - .dot11MeshRetryTimeout = MESH_RET_T, - .dot11MeshConfirmTimeout = MESH_CONF_T, - .dot11MeshHoldingTimeout = MESH_HOLD_T, - .dot11MeshMaxRetries = MESH_MAX_RETR, - .dot11MeshTTL = MESH_TTL, - .element_ttl = MESH_DEFAULT_ELEMENT_TTL, - .auto_open_plinks = true, - .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, - .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, - .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, - .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME, - .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES, - .path_refresh_time = MESH_PATH_REFRESH_TIME, - .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT, - .dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL, - .dot11MeshGateAnnouncementProtocol = false, -}; - -const struct mesh_setup default_mesh_setup = { - .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, - .path_metric = IEEE80211_PATH_METRIC_AIRTIME, - .ie = NULL, - .ie_len = 0, - .is_secure = false, -}; - -int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); - - ASSERT_WDEV_LOCK(wdev); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && - setup->is_secure) - return -EOPNOTSUPP; - - if (wdev->mesh_id_len) - return -EALREADY; - - if (!setup->mesh_id_len) - return -EINVAL; - - if (!rdev->ops->join_mesh) - return -EOPNOTSUPP; - - err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); - if (!err) { - memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); - wdev->mesh_id_len = setup->mesh_id_len; - } - - return err; -} - -int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev, - const struct mesh_setup *setup, - const struct mesh_config *conf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_join_mesh(rdev, dev, setup, conf); - wdev_unlock(wdev); - - return err; -} - -void cfg80211_notify_new_peer_candidate(struct net_device *dev, - const u8 *macaddr, const u8* ie, u8 ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) - return; - - nl80211_send_new_peer_candidate(wiphy_to_dev(wdev->wiphy), dev, - macaddr, ie, ie_len, gfp); -} -EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); - -static int __cfg80211_leave_mesh(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_MESH_POINT) - return -EOPNOTSUPP; - - if (!rdev->ops->leave_mesh) - return -EOPNOTSUPP; - - if (!wdev->mesh_id_len) - return -ENOTCONN; - - err = rdev->ops->leave_mesh(&rdev->wiphy, dev); - if (!err) - wdev->mesh_id_len = 0; - return err; -} - -int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_leave_mesh(rdev, dev); - wdev_unlock(wdev); - - return err; -} diff --git a/net/wireless_ath/mlme.c b/net/wireless_ath/mlme.c deleted file mode 100755 index 6c1bafd..0000000 --- a/net/wireless_ath/mlme.c +++ /dev/null @@ -1,1140 +0,0 @@ -/* - * cfg80211 MLME SAP interface - * - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/nl80211.h> -#include <linux/slab.h> -#include <linux/wireless.h> -#include <net/cfg80211.h> -#include <net/iw_handler.h> -#include "core.h" -#include "nl80211.h" - -void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *bssid = mgmt->bssid; - int i; - u16 status = le16_to_cpu(mgmt->u.auth.status_code); - bool done = false; - - wdev_lock(wdev); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, - ETH_ALEN) == 0) { - if (status == WLAN_STATUS_SUCCESS) { - wdev->auth_bsses[i] = wdev->authtry_bsses[i]; - } else { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - } - wdev->authtry_bsses[i] = NULL; - done = true; - break; - } - } - - if (done) { - nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL); - cfg80211_sme_rx_auth(dev, buf, len); - } - - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_rx_auth); - -void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len) -{ - u16 status_code; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u8 *ie = mgmt->u.assoc_resp.variable; - int i, ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); - struct cfg80211_internal_bss *bss = NULL; - - wdev_lock(wdev); - - status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); - - /* - * This is a bit of a hack, we don't notify userspace of - * a (re-)association reply if we tried to send a reassoc - * and got a reject -- we only try again with an assoc - * frame instead of reassoc. - */ - if (status_code != WLAN_STATUS_SUCCESS && wdev->conn && - cfg80211_sme_failed_reassoc(wdev)) - goto out; - - nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL); - - if (status_code == WLAN_STATUS_SUCCESS) { - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i]) - continue; - if (memcmp(wdev->auth_bsses[i]->pub.bssid, mgmt->bssid, - ETH_ALEN) == 0) { - bss = wdev->auth_bsses[i]; - wdev->auth_bsses[i] = NULL; - /* additional reference to drop hold */ - cfg80211_ref_bss(bss); - break; - } - } - - /* - * We might be coming here because the driver reported - * a successful association at the same time as the - * user requested a deauth. In that case, we will have - * removed the BSS from the auth_bsses list due to the - * deauth request when the assoc response makes it. If - * the two code paths acquire the lock the other way - * around, that's just the standard situation of a - * deauth being requested while connected. - */ - if (!bss) - goto out; - } else if (wdev->conn) { - cfg80211_sme_failed_assoc(wdev); - /* - * do not call connect_result() now because the - * sme will schedule work that does it later. - */ - goto out; - } - - if (!wdev->conn && wdev->sme_state == CFG80211_SME_IDLE) { - /* - * This is for the userspace SME, the CONNECTING - * state will be changed to CONNECTED by - * __cfg80211_connect_result() below. - */ - wdev->sme_state = CFG80211_SME_CONNECTING; - } - - /* this consumes one bss reference (unless bss is NULL) */ - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, - status_code, - status_code == WLAN_STATUS_SUCCESS, - bss ? &bss->pub : NULL); - /* drop hold now, and also reference acquired above */ - if (bss) { - cfg80211_unhold_bss(bss); - cfg80211_put_bss(&bss->pub); - } - - out: - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_rx_assoc); - -void __cfg80211_send_deauth(struct net_device *dev, - const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - const u8 *bssid = mgmt->bssid; - int i; - bool found = false, was_current = false; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - found = true; - was_current = true; - } else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(wdev->auth_bsses[i]->pub.bssid, bssid, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - found = true; - break; - } - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, bssid, - ETH_ALEN) == 0 && - memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - found = true; - break; - } - } - - if (!found) - return; - - nl80211_send_deauth(rdev, dev, buf, len, GFP_KERNEL); - - if (wdev->sme_state == CFG80211_SME_CONNECTED && was_current) { - u16 reason_code; - bool from_ap; - - reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); - - from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; - __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); - } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - } -} -EXPORT_SYMBOL(__cfg80211_send_deauth); - -void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_send_deauth(dev, buf, len); - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_deauth); - -void __cfg80211_send_disassoc(struct net_device *dev, - const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - const u8 *bssid = mgmt->bssid; - int i; - u16 reason_code; - bool from_ap; - bool done = false; - - ASSERT_WDEV_LOCK(wdev); - - nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL); - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return; - - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] || wdev->auth_bsses[i]) - continue; - wdev->auth_bsses[i] = wdev->current_bss; - wdev->current_bss = NULL; - done = true; - cfg80211_sme_disassoc(dev, i); - break; - } - WARN_ON(!done); - } else - WARN_ON(1); - - - reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); - - from_ap = memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0; - __cfg80211_disconnected(dev, NULL, 0, reason_code, from_ap); -} -EXPORT_SYMBOL(__cfg80211_send_disassoc); - -void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - __cfg80211_send_disassoc(dev, buf, len); - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_disassoc); - -void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, - size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); -} -EXPORT_SYMBOL(cfg80211_send_unprot_deauth); - -void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, - size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); -} -EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); - -static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr) -{ - int i; - bool done = false; - - ASSERT_WDEV_LOCK(wdev); - - for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(wdev->authtry_bsses[i]->pub.bssid, - addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - done = true; - break; - } - } - - WARN_ON(!done); -} - -void __cfg80211_auth_canceled(struct net_device *dev, const u8 *addr) -{ - __cfg80211_auth_remove(dev->ieee80211_ptr, addr); -} -EXPORT_SYMBOL(__cfg80211_auth_canceled); - -void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - wdev_lock(wdev); - - nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL); - if (wdev->sme_state == CFG80211_SME_CONNECTING) - __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - - __cfg80211_auth_remove(wdev, addr); - - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_auth_timeout); - -void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - int i; - bool done = false; - - wdev_lock(wdev); - - nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL); - if (wdev->sme_state == CFG80211_SME_CONNECTING) - __cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - - for (i = 0; addr && i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(wdev->auth_bsses[i]->pub.bssid, - addr, ETH_ALEN) == 0) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - done = true; - break; - } - } - - WARN_ON(!done); - - wdev_unlock(wdev); -} -EXPORT_SYMBOL(cfg80211_send_assoc_timeout); - -void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, - enum nl80211_key_type key_type, int key_id, - const u8 *tsc, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; - char *buf = kmalloc(128, gfp); - - if (buf) { - sprintf(buf, "MLME-MICHAELMICFAILURE.indication(" - "keyid=%d %scast addr=%pM)", key_id, - key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni", - addr); - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = strlen(buf); - wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); - kfree(buf); - } -#endif - - nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp); -} -EXPORT_SYMBOL(cfg80211_michael_mic_failure); - -/* some MLME handling for userspace SME */ -int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, - const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_auth_request req; - struct cfg80211_internal_bss *bss; - int i, err, slot = -1, nfree = 0; - - ASSERT_WDEV_LOCK(wdev); - - if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) - if (!key || !key_len || key_idx < 0 || key_idx > 4) - return -EINVAL; - - if (wdev->current_bss && - memcmp(bssid, wdev->current_bss->pub.bssid, ETH_ALEN) == 0) - return -EALREADY; - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->authtry_bsses[i] && - memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, - ETH_ALEN) == 0) - return -EALREADY; - if (wdev->auth_bsses[i] && - memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, - ETH_ALEN) == 0) - return -EALREADY; - } - - memset(&req, 0, sizeof(req)); - - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - req.auth_type = auth_type; - req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); - req.key = key; - req.key_len = key_len; - req.key_idx = key_idx; - if (!req.bss) - return -ENOENT; - - bss = bss_from_pub(req.bss); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i] && !wdev->authtry_bsses[i]) { - slot = i; - nfree++; - } - } - - /* we need one free slot for disassoc and one for this auth */ - if (nfree < 2) { - err = -ENOSPC; - goto out; - } - - if (local_state_change) - wdev->auth_bsses[slot] = bss; - else - wdev->authtry_bsses[slot] = bss; - cfg80211_hold_bss(bss); - - err = rdev->ops->auth(&rdev->wiphy, dev, &req); - if (err) { - if (local_state_change) - wdev->auth_bsses[slot] = NULL; - else - wdev->authtry_bsses[slot] = NULL; - cfg80211_unhold_bss(bss); - } - - out: - if (err) - cfg80211_put_bss(req.bss); - return err; -} - -int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, - struct net_device *dev, struct ieee80211_channel *chan, - enum nl80211_auth_type auth_type, const u8 *bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, - const u8 *key, int key_len, int key_idx, - bool local_state_change) -{ - int err; - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len, - key, key_len, key_idx, local_state_change); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_assoc_request req; - struct cfg80211_internal_bss *bss; - int i, err, slot = -1; - bool was_connected = false; - - ASSERT_WDEV_LOCK(wdev); - - memset(&req, 0, sizeof(req)); - - if (wdev->current_bss && prev_bssid && - memcmp(wdev->current_bss->pub.bssid, prev_bssid, ETH_ALEN) == 0) { - /* - * Trying to reassociate: Allow this to proceed and let the old - * association to be dropped when the new one is completed. - */ - if (wdev->sme_state == CFG80211_SME_CONNECTED) { - was_connected = true; - wdev->sme_state = CFG80211_SME_CONNECTING; - } - } else if (wdev->current_bss) - return -EALREADY; - - req.ie = ie; - req.ie_len = ie_len; - memcpy(&req.crypto, crypt, sizeof(req.crypto)); - req.use_mfp = use_mfp; - req.prev_bssid = prev_bssid; - req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len, - WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); - if (!req.bss) { - if (was_connected) - wdev->sme_state = CFG80211_SME_CONNECTED; - return -ENOENT; - } - - bss = bss_from_pub(req.bss); - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (bss == wdev->auth_bsses[i]) { - slot = i; - break; - } - } - - if (slot < 0) { - err = -ENOTCONN; - goto out; - } - - err = rdev->ops->assoc(&rdev->wiphy, dev, &req); - out: - if (err && was_connected) - wdev->sme_state = CFG80211_SME_CONNECTED; - /* still a reference in wdev->auth_bsses[slot] */ - cfg80211_put_bss(req.bss); - return err; -} - -int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, - const u8 *bssid, const u8 *prev_bssid, - const u8 *ssid, int ssid_len, - const u8 *ie, int ie_len, bool use_mfp, - struct cfg80211_crypto_settings *crypt) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, - ssid, ssid_len, ie, ie_len, use_mfp, crypt); - wdev_unlock(wdev); - - return err; -} - -int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_deauth_request req; - int i; - - ASSERT_WDEV_LOCK(wdev); - - memset(&req, 0, sizeof(req)); - req.reason_code = reason; - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - if (wdev->current_bss && - memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) { - req.bss = &wdev->current_bss->pub; - } else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i] && - memcmp(bssid, wdev->auth_bsses[i]->pub.bssid, ETH_ALEN) == 0) { - req.bss = &wdev->auth_bsses[i]->pub; - break; - } - if (wdev->authtry_bsses[i] && - memcmp(bssid, wdev->authtry_bsses[i]->pub.bssid, ETH_ALEN) == 0) { - req.bss = &wdev->authtry_bsses[i]->pub; - break; - } - } - - if (!req.bss) - return -ENOTCONN; - - return rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); -} - -int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason, - local_state_change); - wdev_unlock(wdev); - - return err; -} - -static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_disassoc_request req; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return -ENOTCONN; - - if (WARN_ON(!wdev->current_bss)) - return -ENOTCONN; - - memset(&req, 0, sizeof(req)); - req.reason_code = reason; - req.local_state_change = local_state_change; - req.ie = ie; - req.ie_len = ie_len; - if (memcmp(wdev->current_bss->pub.bssid, bssid, ETH_ALEN) == 0) - req.bss = &wdev->current_bss->pub; - else - return -ENOTCONN; - - return rdev->ops->disassoc(&rdev->wiphy, dev, &req, wdev); -} - -int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *bssid, - const u8 *ie, int ie_len, u16 reason, - bool local_state_change) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - wdev_lock(wdev); - err = __cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason, - local_state_change); - wdev_unlock(wdev); - - return err; -} - -void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_deauth_request req; - int i; - - ASSERT_WDEV_LOCK(wdev); - - if (!rdev->ops->deauth) - return; - - memset(&req, 0, sizeof(req)); - req.reason_code = WLAN_REASON_DEAUTH_LEAVING; - req.ie = NULL; - req.ie_len = 0; - - if (wdev->current_bss) { - req.bss = &wdev->current_bss->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - } - - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (wdev->auth_bsses[i]) { - req.bss = &wdev->auth_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->auth_bsses[i]) { - cfg80211_unhold_bss(wdev->auth_bsses[i]); - cfg80211_put_bss(&wdev->auth_bsses[i]->pub); - wdev->auth_bsses[i] = NULL; - } - } - if (wdev->authtry_bsses[i]) { - req.bss = &wdev->authtry_bsses[i]->pub; - rdev->ops->deauth(&rdev->wiphy, dev, &req, wdev); - if (wdev->authtry_bsses[i]) { - cfg80211_unhold_bss(wdev->authtry_bsses[i]); - cfg80211_put_bss(&wdev->authtry_bsses[i]->pub); - wdev->authtry_bsses[i] = NULL; - } - } - } -} - -void cfg80211_ready_on_channel(struct net_device *dev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_remain_on_channel(rdev, dev, cookie, chan, channel_type, - duration, gfp); -} -EXPORT_SYMBOL(cfg80211_ready_on_channel); - -void cfg80211_remain_on_channel_expired(struct net_device *dev, - u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_remain_on_channel_cancel(rdev, dev, cookie, chan, - channel_type, gfp); -} -EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); - -void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, - struct station_info *sinfo, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp); -} -EXPORT_SYMBOL(cfg80211_new_sta); - -void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp) -{ - struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp); -} -EXPORT_SYMBOL(cfg80211_del_sta); - -struct cfg80211_mgmt_registration { - struct list_head list; - - u32 nlpid; - - int match_len; - - __le16 frame_type; - - u8 match[]; -}; - -int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid, - u16 frame_type, const u8 *match_data, - int match_len) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_mgmt_registration *reg, *nreg; - int err = 0; - u16 mgmt_type; - - if (!wdev->wiphy->mgmt_stypes) - return -EOPNOTSUPP; - - if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) - return -EINVAL; - - if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) - return -EINVAL; - - mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; - if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) - return -EINVAL; - - nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); - if (!nreg) - return -ENOMEM; - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry(reg, &wdev->mgmt_registrations, list) { - int mlen = min(match_len, reg->match_len); - - if (frame_type != le16_to_cpu(reg->frame_type)) - continue; - - if (memcmp(reg->match, match_data, mlen) == 0) { - err = -EALREADY; - break; - } - } - - if (err) { - kfree(nreg); - goto out; - } - - memcpy(nreg->match, match_data, match_len); - nreg->match_len = match_len; - nreg->nlpid = snd_pid; - nreg->frame_type = cpu_to_le16(frame_type); - list_add(&nreg->list, &wdev->mgmt_registrations); - - if (rdev->ops->mgmt_frame_register) - rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, - frame_type, true); - - out: - spin_unlock_bh(&wdev->mgmt_registrations_lock); - - return err; -} - -void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_mgmt_registration *reg, *tmp; - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { - if (reg->nlpid != nlpid) - continue; - - if (rdev->ops->mgmt_frame_register) { - u16 frame_type = le16_to_cpu(reg->frame_type); - - rdev->ops->mgmt_frame_register(wiphy, wdev->netdev, - frame_type, false); - } - - list_del(®->list); - kfree(reg); - } - - spin_unlock_bh(&wdev->mgmt_registrations_lock); - - if (nlpid == wdev->ap_unexpected_nlpid) - wdev->ap_unexpected_nlpid = 0; -} - -void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) -{ - struct cfg80211_mgmt_registration *reg, *tmp; - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { - list_del(®->list); - kfree(reg); - } - - spin_unlock_bh(&wdev->mgmt_registrations_lock); -} - -int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - const struct ieee80211_mgmt *mgmt; - u16 stype; - - if (!wdev->wiphy->mgmt_stypes) - return -EOPNOTSUPP; - - if (!rdev->ops->mgmt_tx) - return -EOPNOTSUPP; - - if (len < 24 + 1) - return -EINVAL; - - mgmt = (const struct ieee80211_mgmt *) buf; - - if (!ieee80211_is_mgmt(mgmt->frame_control)) - return -EINVAL; - - stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; - if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) - return -EINVAL; - - if (ieee80211_is_action(mgmt->frame_control) && - mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { - int err = 0; - - wdev_lock(wdev); - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - if (!wdev->current_bss) { - err = -ENOTCONN; - break; - } - - if (memcmp(wdev->current_bss->pub.bssid, - mgmt->bssid, ETH_ALEN)) { - err = -ENOTCONN; - break; - } - - /* - * check for IBSS DA must be done by driver as - * cfg80211 doesn't track the stations - */ - if (wdev->iftype == NL80211_IFTYPE_ADHOC) - break; - - /* for station, check that DA is the AP */ - if (memcmp(wdev->current_bss->pub.bssid, - mgmt->da, ETH_ALEN)) { - err = -ENOTCONN; - break; - } - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_AP_VLAN: - if (memcmp(mgmt->bssid, dev->dev_addr, ETH_ALEN)) - err = -EINVAL; - break; - case NL80211_IFTYPE_MESH_POINT: - if (memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN)) { - err = -EINVAL; - break; - } - /* - * check for mesh DA must be done by driver as - * cfg80211 doesn't track the stations - */ - break; - default: - err = -EOPNOTSUPP; - break; - } - wdev_unlock(wdev); - - if (err) - return err; - } - - if (memcmp(mgmt->sa, dev->dev_addr, ETH_ALEN) != 0) - return -EINVAL; - - /* Transmit the Action frame as requested by user space */ - return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, - channel_type, channel_type_valid, - wait, buf, len, no_cck, dont_wait_for_ack, - cookie); -} - -bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, - size_t len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct cfg80211_mgmt_registration *reg; - const struct ieee80211_txrx_stypes *stypes = - &wiphy->mgmt_stypes[wdev->iftype]; - struct ieee80211_mgmt *mgmt = (void *)buf; - const u8 *data; - int data_len; - bool result = false; - __le16 ftype = mgmt->frame_control & - cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); - u16 stype; - - stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; - - if (!(stypes->rx & BIT(stype))) - return false; - - data = buf + ieee80211_hdrlen(mgmt->frame_control); - data_len = len - ieee80211_hdrlen(mgmt->frame_control); - - spin_lock_bh(&wdev->mgmt_registrations_lock); - - list_for_each_entry(reg, &wdev->mgmt_registrations, list) { - if (reg->frame_type != ftype) - continue; - - if (reg->match_len > data_len) - continue; - - if (memcmp(reg->match, data, reg->match_len)) - continue; - - /* found match! */ - - /* Indicate the received Action frame to user space */ - if (nl80211_send_mgmt(rdev, dev, reg->nlpid, freq, - buf, len, gfp)) - continue; - - result = true; - break; - } - - spin_unlock_bh(&wdev->mgmt_registrations_lock); - - return result; -} -EXPORT_SYMBOL(cfg80211_rx_mgmt); - -void cfg80211_mgmt_tx_status(struct net_device *dev, u64 cookie, - const u8 *buf, size_t len, bool ack, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - /* Indicate TX status of the Action frame to user space */ - nl80211_send_mgmt_tx_status(rdev, dev, cookie, buf, len, ack, gfp); -} -EXPORT_SYMBOL(cfg80211_mgmt_tx_status); - -void cfg80211_cqm_rssi_notify(struct net_device *dev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - /* Indicate roaming trigger event to user space */ - nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); -} -EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); - -void cfg80211_cqm_pktloss_notify(struct net_device *dev, - const u8 *peer, u32 num_packets, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - /* Indicate roaming trigger event to user space */ - nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); -} -EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); - -void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, - const u8 *replay_ctr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); -} -EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); - -void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, - const u8 *bssid, bool preauth, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); -} -EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); - -bool cfg80211_rx_spurious_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) - return false; - - return nl80211_unexpected_frame(dev, addr, gfp); -} -EXPORT_SYMBOL(cfg80211_rx_spurious_frame); - -bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO && - wdev->iftype != NL80211_IFTYPE_AP_VLAN)) - return false; - - return nl80211_unexpected_4addr_frame(dev, addr, gfp); -} -EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless_ath/nl80211.c b/net/wireless_ath/nl80211.c deleted file mode 100755 index b11cabe..0000000 --- a/net/wireless_ath/nl80211.c +++ /dev/null @@ -1,8092 +0,0 @@ -/* - * This is the new netlink-based wireless configuration interface. - * - * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <linux/if.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/if_ether.h> -#include <linux/ieee80211.h> -#include <linux/nl80211.h> -#include <linux/rtnetlink.h> -#include <linux/netlink.h> -#include <linux/etherdevice.h> -#include <net/net_namespace.h> -#include <net/genetlink.h> -#include <net/cfg80211.h> -#include <net/sock.h> -#include "core.h" -#include "nl80211.h" -#include "reg.h" - -static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type); -static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, - struct genl_info *info, - struct cfg80211_crypto_settings *settings, - int cipher_limit); - -static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); -static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info); - -/* the netlink family */ -static struct genl_family nl80211_fam = { - .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */ - .name = "nl80211", /* have users key off the name instead */ - .hdrsize = 0, /* no private header */ - .version = 1, /* no particular meaning now */ - .maxattr = NL80211_ATTR_MAX, - .netnsok = true, - .pre_doit = nl80211_pre_doit, - .post_doit = nl80211_post_doit, -}; - -/* internal helper: get rdev and dev */ -static int get_rdev_dev_by_info_ifindex(struct genl_info *info, - struct cfg80211_registered_device **rdev, - struct net_device **dev) -{ - struct nlattr **attrs = info->attrs; - int ifindex; - - if (!attrs[NL80211_ATTR_IFINDEX]) - return -EINVAL; - - ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(genl_info_net(info), ifindex); - if (!*dev) - return -ENODEV; - - *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex); - if (IS_ERR(*rdev)) { - dev_put(*dev); - return PTR_ERR(*rdev); - } - - return 0; -} - -/* policy for the attributes */ -static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { - [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, - .len = 20-1 }, - [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, - [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, - - [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, - [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, - - [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, - [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN }, - - [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, - [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, - .len = WLAN_MAX_KEY_LEN }, - [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, - [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, - [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, - [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, - [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, - - [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, - [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, - [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_STA_AID] = { .type = NLA_U16 }, - [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, - [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, - [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, - [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, - [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, - [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, - [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_MESH_ID_LEN }, - [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, - - [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, - [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, - - [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, - [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, - [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, - [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, - [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, - - [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, - [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, - - [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN }, - - [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, - [NL80211_ATTR_IE] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, - [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, - - [NL80211_ATTR_SSID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_SSID_LEN }, - [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, - [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, - [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, - [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG }, - [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 }, - [NL80211_ATTR_STA_FLAGS2] = { - .len = sizeof(struct nl80211_sta_flag_update), - }, - [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, - [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, - [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, - [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, - [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, - [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, - [NL80211_ATTR_PID] = { .type = NLA_U32 }, - [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, - [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, - .len = WLAN_PMKID_LEN }, - [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, - [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, - [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, - [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, - [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, - [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, - [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, - [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, - [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, - [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, - [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, - [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, - [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, - [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, - [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 }, - [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, - [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, - [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, - [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 }, - [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, - [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, - [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, - [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, - [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, - [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, - [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, - [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, - [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, - [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - - [NL80211_ATTR_BTCOEX_INQ_STATUS] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_SCO_STATUS] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_TYPE_ESCO] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_ESCO_TX_INTERVAL] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_ESCO_TX_PKT_LEN] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_A2DP_STATUS] = { .type = NLA_FLAG }, - [NL80211_ATTR_BTCOEX_ACL_ROLE] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_REMOTE_LMP_VER] = { .type = NLA_U32 }, - - [NL80211_ATTR_BTCOEX_ANTENNA_CONFIG] = { .type = NLA_U32 }, - [NL80211_ATTR_BT_VENDOR_ID] = { .type = NLA_U32 }, - [NL80211_ATTR_BTCOEX_DATA] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_ATTR_PRIV_CMD] = { .type = NLA_NUL_STRING, .len = 128 }, - [NL80211_ATTR_PRIV_EVENT] = { .type = NLA_NUL_STRING, .len = 128 }, - -}; - -/* policy for the key attributes */ -static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { - [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, - [NL80211_KEY_IDX] = { .type = NLA_U8 }, - [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, - [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, - [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, - [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, - [NL80211_KEY_TYPE] = { .type = NLA_U32 }, - [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, -}; - -/* policy for the key default flags */ -static const struct nla_policy -nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { - [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, - [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, -}; - -/* policy for WoWLAN attributes */ -static const struct nla_policy -nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { - [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, - [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, - [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, -}; - -/* policy for GTK rekey offload attributes */ -static const struct nla_policy -nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { - [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN }, - [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN }, - [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN }, -}; - -static const struct nla_policy -nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { - [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_SSID_LEN }, -}; - -/* ifidx get helper */ -static int nl80211_get_ifidx(struct netlink_callback *cb) -{ - int res; - - res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); - if (res) - return res; - - if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]) - return -EINVAL; - - res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]); - if (!res) - return -EINVAL; - return res; -} - -static int nl80211_prepare_netdev_dump(struct sk_buff *skb, - struct netlink_callback *cb, - struct cfg80211_registered_device **rdev, - struct net_device **dev) -{ - int ifidx = cb->args[0]; - int err; - - if (!ifidx) - ifidx = nl80211_get_ifidx(cb); - if (ifidx < 0) - return ifidx; - - cb->args[0] = ifidx; - - rtnl_lock(); - - *dev = __dev_get_by_index(sock_net(skb->sk), ifidx); - if (!*dev) { - err = -ENODEV; - goto out_rtnl; - } - - *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); - if (IS_ERR(*rdev)) { - err = PTR_ERR(*rdev); - goto out_rtnl; - } - - return 0; - out_rtnl: - rtnl_unlock(); - return err; -} - -static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev) -{ - cfg80211_unlock_rdev(rdev); - rtnl_unlock(); -} - -/* IE validation */ -static bool is_valid_ie_attr(const struct nlattr *attr) -{ - const u8 *pos; - int len; - - if (!attr) - return true; - - pos = nla_data(attr); - len = nla_len(attr); - - while (len) { - u8 elemlen; - - if (len < 2) - return false; - len -= 2; - - elemlen = pos[1]; - if (elemlen > len) - return false; - - len -= elemlen; - pos += 2 + elemlen; - } - - return true; -} - -/* message building helper */ -static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, - int flags, u8 cmd) -{ - /* since there is no private header just add the generic one */ - return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd); -} - -static int nl80211_msg_put_channel(struct sk_buff *msg, - struct ieee80211_channel *chan) -{ - NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ, - chan->center_freq); - - if (chan->flags & IEEE80211_CHAN_DISABLED) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED); - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN); - if (chan->flags & IEEE80211_CHAN_NO_IBSS) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS); - if (chan->flags & IEEE80211_CHAN_RADAR) - NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); - - NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, - DBM_TO_MBM(chan->max_power)); - - return 0; - - nla_put_failure: - return -ENOBUFS; -} - -/* netlink command implementations */ - -struct key_parse { - struct key_params p; - int idx; - int type; - bool def, defmgmt; - bool def_uni, def_multi; -}; - -static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) -{ - struct nlattr *tb[NL80211_KEY_MAX + 1]; - int err = nla_parse_nested(tb, NL80211_KEY_MAX, key, - nl80211_key_policy); - if (err) - return err; - - k->def = !!tb[NL80211_KEY_DEFAULT]; - k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; - - if (k->def) { - k->def_uni = true; - k->def_multi = true; - } - if (k->defmgmt) - k->def_multi = true; - - if (tb[NL80211_KEY_IDX]) - k->idx = nla_get_u8(tb[NL80211_KEY_IDX]); - - if (tb[NL80211_KEY_DATA]) { - k->p.key = nla_data(tb[NL80211_KEY_DATA]); - k->p.key_len = nla_len(tb[NL80211_KEY_DATA]); - } - - if (tb[NL80211_KEY_SEQ]) { - k->p.seq = nla_data(tb[NL80211_KEY_SEQ]); - k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]); - } - - if (tb[NL80211_KEY_CIPHER]) - k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]); - - if (tb[NL80211_KEY_TYPE]) { - k->type = nla_get_u32(tb[NL80211_KEY_TYPE]); - if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) - return -EINVAL; - } - - if (tb[NL80211_KEY_DEFAULT_TYPES]) { - struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; - int err = nla_parse_nested(kdt, - NUM_NL80211_KEY_DEFAULT_TYPES - 1, - tb[NL80211_KEY_DEFAULT_TYPES], - nl80211_key_default_policy); - if (err) - return err; - - k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; - k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; - } - - return 0; -} - -static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) -{ - if (info->attrs[NL80211_ATTR_KEY_DATA]) { - k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); - k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); - } - - if (info->attrs[NL80211_ATTR_KEY_SEQ]) { - k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]); - k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]); - } - - if (info->attrs[NL80211_ATTR_KEY_IDX]) - k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (info->attrs[NL80211_ATTR_KEY_CIPHER]) - k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); - - k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; - k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; - - if (k->def) { - k->def_uni = true; - k->def_multi = true; - } - if (k->defmgmt) - k->def_multi = true; - - if (info->attrs[NL80211_ATTR_KEY_TYPE]) { - k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); - if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { - struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; - int err = nla_parse_nested( - kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, - info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], - nl80211_key_default_policy); - if (err) - return err; - - k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; - k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; - } - - return 0; -} - -static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) -{ - int err; - - memset(k, 0, sizeof(*k)); - k->idx = -1; - k->type = -1; - - if (info->attrs[NL80211_ATTR_KEY]) - err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k); - else - err = nl80211_parse_key_old(info, k); - - if (err) - return err; - - if (k->def && k->defmgmt) - return -EINVAL; - - if (k->defmgmt) { - if (k->def_uni || !k->def_multi) - return -EINVAL; - } - - if (k->idx != -1) { - if (k->defmgmt) { - if (k->idx < 4 || k->idx > 5) - return -EINVAL; - } else if (k->def) { - if (k->idx < 0 || k->idx > 3) - return -EINVAL; - } else { - if (k->idx < 0 || k->idx > 5) - return -EINVAL; - } - } - - return 0; -} - -static struct cfg80211_cached_keys * -nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, - struct nlattr *keys) -{ - struct key_parse parse; - struct nlattr *key; - struct cfg80211_cached_keys *result; - int rem, err, def = 0; - - result = kzalloc(sizeof(*result), GFP_KERNEL); - if (!result) - return ERR_PTR(-ENOMEM); - - result->def = -1; - result->defmgmt = -1; - - nla_for_each_nested(key, keys, rem) { - memset(&parse, 0, sizeof(parse)); - parse.idx = -1; - - err = nl80211_parse_key_new(key, &parse); - if (err) - goto error; - err = -EINVAL; - if (!parse.p.key) - goto error; - if (parse.idx < 0 || parse.idx > 4) - goto error; - if (parse.def) { - if (def) - goto error; - def = 1; - result->def = parse.idx; - if (!parse.def_uni || !parse.def_multi) - goto error; - } else if (parse.defmgmt) - goto error; - err = cfg80211_validate_key_settings(rdev, &parse.p, - parse.idx, false, NULL); - if (err) - goto error; - result->params[parse.idx].cipher = parse.p.cipher; - result->params[parse.idx].key_len = parse.p.key_len; - result->params[parse.idx].key = result->data[parse.idx]; - memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); - } - - return result; - error: - kfree(result); - return ERR_PTR(err); -} - -static int nl80211_key_allowed(struct wireless_dev *wdev) -{ - ASSERT_WDEV_LOCK(wdev); - - switch (wdev->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_MESH_POINT: - break; - case NL80211_IFTYPE_ADHOC: - if (!wdev->current_bss) - return -ENOLINK; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return -ENOLINK; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) -{ - struct nlattr *nl_modes = nla_nest_start(msg, attr); - int i; - - if (!nl_modes) - goto nla_put_failure; - - i = 0; - while (ifmodes) { - if (ifmodes & 1) - NLA_PUT_FLAG(msg, i); - ifmodes >>= 1; - i++; - } - - nla_nest_end(msg, nl_modes); - return 0; - -nla_put_failure: - return -ENOBUFS; -} - -static int nl80211_put_iface_combinations(struct wiphy *wiphy, - struct sk_buff *msg) -{ - struct nlattr *nl_combis; - int i, j; - - nl_combis = nla_nest_start(msg, - NL80211_ATTR_INTERFACE_COMBINATIONS); - if (!nl_combis) - goto nla_put_failure; - - for (i = 0; i < wiphy->n_iface_combinations; i++) { - const struct ieee80211_iface_combination *c; - struct nlattr *nl_combi, *nl_limits; - - c = &wiphy->iface_combinations[i]; - - nl_combi = nla_nest_start(msg, i + 1); - if (!nl_combi) - goto nla_put_failure; - - nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS); - if (!nl_limits) - goto nla_put_failure; - - for (j = 0; j < c->n_limits; j++) { - struct nlattr *nl_limit; - - nl_limit = nla_nest_start(msg, j + 1); - if (!nl_limit) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX, - c->limits[j].max); - if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES, - c->limits[j].types)) - goto nla_put_failure; - nla_nest_end(msg, nl_limit); - } - - nla_nest_end(msg, nl_limits); - - if (c->beacon_int_infra_match) - NLA_PUT_FLAG(msg, - NL80211_IFACE_COMB_STA_AP_BI_MATCH); - NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS, - c->num_different_channels); - NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM, - c->max_interfaces); - - nla_nest_end(msg, nl_combi); - } - - nla_nest_end(msg, nl_combis); - - return 0; -nla_put_failure: - return -ENOBUFS; -} - -static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, - struct cfg80211_registered_device *dev) -{ - void *hdr; - struct nlattr *nl_bands, *nl_band; - struct nlattr *nl_freqs, *nl_freq; - struct nlattr *nl_rates, *nl_rate; - struct nlattr *nl_cmds; - enum ieee80211_band band; - struct ieee80211_channel *chan; - struct ieee80211_rate *rate; - int i; - const struct ieee80211_txrx_stypes *mgmt_stypes = - dev->wiphy.mgmt_stypes; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx); - NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, - cfg80211_rdev_list_generation); - - NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT, - dev->wiphy.retry_short); - NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG, - dev->wiphy.retry_long); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, - dev->wiphy.frag_threshold); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, - dev->wiphy.rts_threshold); - NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, - dev->wiphy.coverage_class); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, - dev->wiphy.max_scan_ssids); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, - dev->wiphy.max_sched_scan_ssids); - NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, - dev->wiphy.max_scan_ie_len); - NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, - dev->wiphy.max_sched_scan_ie_len); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS, - dev->wiphy.max_match_sets); - - if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) - NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN); - if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) - NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH); - if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) - NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) - NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) - NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT); - if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) - NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP); - - NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES, - sizeof(u32) * dev->wiphy.n_cipher_suites, - dev->wiphy.cipher_suites); - - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS, - dev->wiphy.max_num_pmkids); - - if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE); - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, - dev->wiphy.available_antennas_tx); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, - dev->wiphy.available_antennas_rx); - - if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) - NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD, - dev->wiphy.probe_resp_offload); - - if ((dev->wiphy.available_antennas_tx || - dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { - u32 tx_ant = 0, rx_ant = 0; - int res; - res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); - if (!res) { - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant); - } - } - - if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES, - dev->wiphy.interface_modes)) - goto nla_put_failure; - - nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); - if (!nl_bands) - goto nla_put_failure; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!dev->wiphy.bands[band]) - continue; - - nl_band = nla_nest_start(msg, band); - if (!nl_band) - goto nla_put_failure; - - /* add HT info */ - if (dev->wiphy.bands[band]->ht_cap.ht_supported) { - NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET, - sizeof(dev->wiphy.bands[band]->ht_cap.mcs), - &dev->wiphy.bands[band]->ht_cap.mcs); - NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA, - dev->wiphy.bands[band]->ht_cap.cap); - NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, - dev->wiphy.bands[band]->ht_cap.ampdu_factor); - NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, - dev->wiphy.bands[band]->ht_cap.ampdu_density); - } - - /* add frequencies */ - nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); - if (!nl_freqs) - goto nla_put_failure; - - for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) { - nl_freq = nla_nest_start(msg, i); - if (!nl_freq) - goto nla_put_failure; - - chan = &dev->wiphy.bands[band]->channels[i]; - - if (nl80211_msg_put_channel(msg, chan)) - goto nla_put_failure; - - nla_nest_end(msg, nl_freq); - } - - nla_nest_end(msg, nl_freqs); - - /* add bitrates */ - nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES); - if (!nl_rates) - goto nla_put_failure; - - for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) { - nl_rate = nla_nest_start(msg, i); - if (!nl_rate) - goto nla_put_failure; - - rate = &dev->wiphy.bands[band]->bitrates[i]; - NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE, - rate->bitrate); - if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) - NLA_PUT_FLAG(msg, - NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE); - - nla_nest_end(msg, nl_rate); - } - - nla_nest_end(msg, nl_rates); - - nla_nest_end(msg, nl_band); - } - nla_nest_end(msg, nl_bands); - - nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS); - if (!nl_cmds) - goto nla_put_failure; - - i = 0; -#define CMD(op, n) \ - do { \ - if (dev->ops->op) { \ - i++; \ - NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \ - } \ - } while (0) - - CMD(add_virtual_intf, NEW_INTERFACE); - CMD(change_virtual_intf, SET_INTERFACE); - CMD(add_key, NEW_KEY); - CMD(add_beacon, NEW_BEACON); - CMD(add_station, NEW_STATION); - CMD(add_mpath, NEW_MPATH); - CMD(update_mesh_config, SET_MESH_CONFIG); - CMD(change_bss, SET_BSS); - CMD(auth, AUTHENTICATE); - CMD(assoc, ASSOCIATE); - CMD(deauth, DEAUTHENTICATE); - CMD(disassoc, DISASSOCIATE); - CMD(join_ibss, JOIN_IBSS); - CMD(join_mesh, JOIN_MESH); - CMD(set_pmksa, SET_PMKSA); - CMD(del_pmksa, DEL_PMKSA); - CMD(flush_pmksa, FLUSH_PMKSA); - CMD(remain_on_channel, REMAIN_ON_CHANNEL); - CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); - CMD(mgmt_tx, FRAME); - CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); - if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); - } - CMD(set_channel, SET_CHANNEL); - CMD(set_wds_peer, SET_WDS_PEER); - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { - CMD(tdls_mgmt, TDLS_MGMT); - CMD(tdls_oper, TDLS_OPER); - } - if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) - CMD(sched_scan_start, START_SCHED_SCAN); - CMD(probe_client, PROBE_CLIENT); - if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS); - } - -#undef CMD - - if (dev->ops->connect || dev->ops->auth) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); - } - - if (dev->ops->disconnect || dev->ops->deauth) { - i++; - NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); - } - - nla_nest_end(msg, nl_cmds); - - if (dev->ops->remain_on_channel) - NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, - dev->wiphy.max_remain_on_channel_duration); - - if (dev->ops->mgmt_tx_cancel_wait) - NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); - - if (mgmt_stypes) { - u16 stypes; - struct nlattr *nl_ftypes, *nl_ifs; - enum nl80211_iftype ift; - - nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES); - if (!nl_ifs) - goto nla_put_failure; - - for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { - nl_ftypes = nla_nest_start(msg, ift); - if (!nl_ftypes) - goto nla_put_failure; - i = 0; - stypes = mgmt_stypes[ift].tx; - while (stypes) { - if (stypes & 1) - NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, - (i << 4) | IEEE80211_FTYPE_MGMT); - stypes >>= 1; - i++; - } - nla_nest_end(msg, nl_ftypes); - } - - nla_nest_end(msg, nl_ifs); - - nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES); - if (!nl_ifs) - goto nla_put_failure; - - for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { - nl_ftypes = nla_nest_start(msg, ift); - if (!nl_ftypes) - goto nla_put_failure; - i = 0; - stypes = mgmt_stypes[ift].rx; - while (stypes) { - if (stypes & 1) - NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, - (i << 4) | IEEE80211_FTYPE_MGMT); - stypes >>= 1; - i++; - } - nla_nest_end(msg, nl_ftypes); - } - nla_nest_end(msg, nl_ifs); - } - - if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) { - struct nlattr *nl_wowlan; - - nl_wowlan = nla_nest_start(msg, - NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); - if (!nl_wowlan) - goto nla_put_failure; - - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); - if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); - if (dev->wiphy.wowlan.n_patterns) { - struct nl80211_wowlan_pattern_support pat = { - .max_patterns = dev->wiphy.wowlan.n_patterns, - .min_pattern_len = - dev->wiphy.wowlan.pattern_min_len, - .max_pattern_len = - dev->wiphy.wowlan.pattern_max_len, - }; - NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN, - sizeof(pat), &pat); - } - - nla_nest_end(msg, nl_wowlan); - } - - if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES, - dev->wiphy.software_iftypes)) - goto nla_put_failure; - - if (nl80211_put_iface_combinations(&dev->wiphy, msg)) - goto nla_put_failure; - - if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) - NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME, - dev->wiphy.ap_sme_capa); - - NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) -{ - int idx = 0; - int start = cb->args[0]; - struct cfg80211_registered_device *dev; - - mutex_lock(&cfg80211_mutex); - list_for_each_entry(dev, &cfg80211_rdev_list, list) { - if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) - continue; - if (++idx <= start) - continue; - if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - dev) < 0) { - idx--; - break; - } - } - mutex_unlock(&cfg80211_mutex); - - cb->args[0] = idx; - - return skb->len; -} - -static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) -{ - struct sk_buff *msg; - struct cfg80211_registered_device *dev = info->user_ptr[0]; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { - [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, - [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, - [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, - [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, - [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, -}; - -static int parse_txq_params(struct nlattr *tb[], - struct ieee80211_txq_params *txq_params) -{ - if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || - !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || - !tb[NL80211_TXQ_ATTR_AIFS]) - return -EINVAL; - - txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); - txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); - txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); - txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); - txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); - - return 0; -} - -static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) -{ - /* - * You can only set the channel explicitly for AP, mesh - * and WDS type interfaces; all others have their channel - * managed via their respective "establish a connection" - * command (connect, join, ...) - * - * Monitors are special as they are normally slaved to - * whatever else is going on, so they behave as though - * you tried setting the wiphy channel itself. - */ - return !wdev || - wdev->iftype == NL80211_IFTYPE_AP || - wdev->iftype == NL80211_IFTYPE_WDS || - wdev->iftype == NL80211_IFTYPE_MESH_POINT || - wdev->iftype == NL80211_IFTYPE_MONITOR || - wdev->iftype == NL80211_IFTYPE_P2P_GO; -} - -static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - struct genl_info *info) -{ - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq; - int result; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - if (!nl80211_can_set_dev_channel(wdev)) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32(info->attrs[ - NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - return -EINVAL; - } - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - - mutex_lock(&rdev->devlist_mtx); - if (wdev) { - wdev_lock(wdev); - result = cfg80211_set_freq(rdev, wdev, freq, channel_type); - wdev_unlock(wdev); - } else { - result = cfg80211_set_freq(rdev, NULL, freq, channel_type); - } - mutex_unlock(&rdev->devlist_mtx); - - return result; -} - -static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *netdev = info->user_ptr[1]; - - return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); -} - -static int nl80211_set_wds_peer(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 wireless_dev *wdev = dev->ieee80211_ptr; - const u8 *bssid; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (netif_running(dev)) - return -EBUSY; - - if (!rdev->ops->set_wds_peer) - return -EOPNOTSUPP; - - if (wdev->iftype != NL80211_IFTYPE_WDS) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid); -} - - -static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev; - struct net_device *netdev = NULL; - struct wireless_dev *wdev; - int result = 0, rem_txq_params = 0; - struct nlattr *nl_txq_params; - u32 changed; - u8 retry_short = 0, retry_long = 0; - u32 frag_threshold = 0, rts_threshold = 0; - u8 coverage_class = 0; - - /* - * Try to find the wiphy and netdev. Normally this - * function shouldn't need the netdev, but this is - * done for backward compatibility -- previously - * setting the channel was done per wiphy, but now - * it is per netdev. Previous userland like hostapd - * also passed a netdev to set_wiphy, so that it is - * possible to let that go to the right netdev! - */ - mutex_lock(&cfg80211_mutex); - - if (info->attrs[NL80211_ATTR_IFINDEX]) { - int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - - netdev = dev_get_by_index(genl_info_net(info), ifindex); - if (netdev && netdev->ieee80211_ptr) { - rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); - mutex_lock(&rdev->mtx); - } else - netdev = NULL; - } - - if (!netdev) { - rdev = __cfg80211_rdev_from_info(info); - if (IS_ERR(rdev)) { - mutex_unlock(&cfg80211_mutex); - return PTR_ERR(rdev); - } - wdev = NULL; - netdev = NULL; - result = 0; - - mutex_lock(&rdev->mtx); - } else if (netif_running(netdev) && - nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) - wdev = netdev->ieee80211_ptr; - else - wdev = NULL; - - /* - * end workaround code, by now the rdev is available - * and locked, and wdev may or may not be NULL. - */ - - if (info->attrs[NL80211_ATTR_WIPHY_NAME]) - result = cfg80211_dev_rename( - rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); - - mutex_unlock(&cfg80211_mutex); - - if (result) - goto bad_res; - - if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { - struct ieee80211_txq_params txq_params; - struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; - - if (!rdev->ops->set_txq_params) { - result = -EOPNOTSUPP; - goto bad_res; - } - - if (!netdev) { - result = -EINVAL; - goto bad_res; - } - - if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { - result = -EINVAL; - goto bad_res; - } - - nla_for_each_nested(nl_txq_params, - info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], - rem_txq_params) { - nla_parse(tb, NL80211_TXQ_ATTR_MAX, - nla_data(nl_txq_params), - nla_len(nl_txq_params), - txq_params_policy); - result = parse_txq_params(tb, &txq_params); - if (result) - goto bad_res; - - result = rdev->ops->set_txq_params(&rdev->wiphy, - netdev, - &txq_params); - if (result) - goto bad_res; - } - } - - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - result = __nl80211_set_channel(rdev, wdev, info); - if (result) - goto bad_res; - } - - if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { - enum nl80211_tx_power_setting type; - int idx, mbm = 0; - - if (!rdev->ops->set_tx_power) { - result = -EOPNOTSUPP; - goto bad_res; - } - - idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING; - type = nla_get_u32(info->attrs[idx]); - - if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && - (type != NL80211_TX_POWER_AUTOMATIC)) { - result = -EINVAL; - goto bad_res; - } - - if (type != NL80211_TX_POWER_AUTOMATIC) { - idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; - mbm = nla_get_u32(info->attrs[idx]); - } - - result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); - if (result) - goto bad_res; - } - - if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && - info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { - u32 tx_ant, rx_ant; - if ((!rdev->wiphy.available_antennas_tx && - !rdev->wiphy.available_antennas_rx) || - !rdev->ops->set_antenna) { - result = -EOPNOTSUPP; - goto bad_res; - } - - tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); - rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); - - /* reject antenna configurations which don't match the - * available antenna masks, except for the "all" mask */ - if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || - (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { - result = -EINVAL; - goto bad_res; - } - - tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; - rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; - - result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); - if (result) - goto bad_res; - } - - changed = 0; - - if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { - retry_short = nla_get_u8( - info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); - if (retry_short == 0) { - result = -EINVAL; - goto bad_res; - } - changed |= WIPHY_PARAM_RETRY_SHORT; - } - - if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) { - retry_long = nla_get_u8( - info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); - if (retry_long == 0) { - result = -EINVAL; - goto bad_res; - } - changed |= WIPHY_PARAM_RETRY_LONG; - } - - if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { - frag_threshold = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); - if (frag_threshold < 256) { - result = -EINVAL; - goto bad_res; - } - if (frag_threshold != (u32) -1) { - /* - * Fragments (apart from the last one) are required to - * have even length. Make the fragmentation code - * simpler by stripping LSB should someone try to use - * odd threshold value. - */ - frag_threshold &= ~0x1; - } - changed |= WIPHY_PARAM_FRAG_THRESHOLD; - } - - if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { - rts_threshold = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); - changed |= WIPHY_PARAM_RTS_THRESHOLD; - } - - if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { - coverage_class = nla_get_u8( - info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); - changed |= WIPHY_PARAM_COVERAGE_CLASS; - } - - if (changed) { - u8 old_retry_short, old_retry_long; - u32 old_frag_threshold, old_rts_threshold; - u8 old_coverage_class; - - if (!rdev->ops->set_wiphy_params) { - result = -EOPNOTSUPP; - goto bad_res; - } - - old_retry_short = rdev->wiphy.retry_short; - old_retry_long = rdev->wiphy.retry_long; - old_frag_threshold = rdev->wiphy.frag_threshold; - old_rts_threshold = rdev->wiphy.rts_threshold; - old_coverage_class = rdev->wiphy.coverage_class; - - if (changed & WIPHY_PARAM_RETRY_SHORT) - rdev->wiphy.retry_short = retry_short; - if (changed & WIPHY_PARAM_RETRY_LONG) - rdev->wiphy.retry_long = retry_long; - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) - rdev->wiphy.frag_threshold = frag_threshold; - if (changed & WIPHY_PARAM_RTS_THRESHOLD) - rdev->wiphy.rts_threshold = rts_threshold; - if (changed & WIPHY_PARAM_COVERAGE_CLASS) - rdev->wiphy.coverage_class = coverage_class; - - result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); - if (result) { - rdev->wiphy.retry_short = old_retry_short; - rdev->wiphy.retry_long = old_retry_long; - rdev->wiphy.frag_threshold = old_frag_threshold; - rdev->wiphy.rts_threshold = old_rts_threshold; - rdev->wiphy.coverage_class = old_coverage_class; - } - } - - bad_res: - mutex_unlock(&rdev->mtx); - if (netdev) - dev_put(netdev); - return result; -} - - -static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, - struct cfg80211_registered_device *rdev, - struct net_device *dev) -{ - void *hdr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, - rdev->devlist_generation ^ - (cfg80211_rdev_list_generation << 2)); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_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 cfg80211_registered_device *rdev; - struct wireless_dev *wdev; - - mutex_lock(&cfg80211_mutex); - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) - continue; - if (wp_idx < wp_start) { - wp_idx++; - continue; - } - if_idx = 0; - - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev, &rdev->netdev_list, list) { - if (if_idx < if_start) { - if_idx++; - continue; - } - if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev->netdev) < 0) { - mutex_unlock(&rdev->devlist_mtx); - goto out; - } - if_idx++; - } - mutex_unlock(&rdev->devlist_mtx); - - wp_idx++; - } - out: - mutex_unlock(&cfg80211_mutex); - - cb->args[0] = wp_idx; - cb->args[1] = if_idx; - - return skb->len; -} - -static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) -{ - struct sk_buff *msg; - struct cfg80211_registered_device *dev = info->user_ptr[0]; - struct net_device *netdev = info->user_ptr[1]; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, - dev, netdev) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { - [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, - [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, -}; - -static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) -{ - struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; - int flag; - - *mntrflags = 0; - - if (!nla) - return -EINVAL; - - if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX, - nla, mntr_flags_policy)) - return -EINVAL; - - for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) - if (flags[flag]) - *mntrflags |= (1<<flag); - - return 0; -} - -static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u8 use_4addr, - enum nl80211_iftype iftype) -{ - if (!use_4addr) { - if (netdev && br_port_exists(netdev)) - return -EBUSY; - return 0; - } - - switch (iftype) { - case NL80211_IFTYPE_AP_VLAN: - if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) - return 0; - break; - case NL80211_IFTYPE_STATION: - if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) - return 0; - break; - default: - break; - } - - return -EOPNOTSUPP; -} - -static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct vif_params params; - int err; - enum nl80211_iftype otype, ntype; - struct net_device *dev = info->user_ptr[1]; - u32 _flags, *flags = NULL; - bool change = false; - - memset(¶ms, 0, sizeof(params)); - - otype = ntype = dev->ieee80211_ptr->iftype; - - if (info->attrs[NL80211_ATTR_IFTYPE]) { - ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); - if (otype != ntype) - change = true; - if (ntype > NL80211_IFTYPE_MAX) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_MESH_ID]) { - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (ntype != NL80211_IFTYPE_MESH_POINT) - return -EINVAL; - if (netif_running(dev)) - return -EBUSY; - - wdev_lock(wdev); - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != - IEEE80211_MAX_MESH_ID_LEN); - wdev->mesh_id_up_len = - nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), - wdev->mesh_id_up_len); - wdev_unlock(wdev); - } - - if (info->attrs[NL80211_ATTR_4ADDR]) { - params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); - change = true; - err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype); - if (err) - return err; - } else { - params.use_4addr = -1; - } - - if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { - if (ntype != NL80211_IFTYPE_MONITOR) - return -EINVAL; - err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], - &_flags); - if (err) - return err; - - flags = &_flags; - change = true; - } - - if (change) - err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms); - else - err = 0; - - if (!err && params.use_4addr != -1) - dev->ieee80211_ptr->use_4addr = params.use_4addr; - - return err; -} - -static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct vif_params params; - struct net_device *dev; - int err; - enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; - u32 flags; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_IFNAME]) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IFTYPE]) { - type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); - if (type > NL80211_IFTYPE_MAX) - return -EINVAL; - } - - if (!rdev->ops->add_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << type))) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_4ADDR]) { - params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); - err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); - if (err) - return err; - } - - err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? - info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, - &flags); - dev = rdev->ops->add_virtual_intf(&rdev->wiphy, - nla_data(info->attrs[NL80211_ATTR_IFNAME]), - type, err ? NULL : &flags, ¶ms); - if (IS_ERR(dev)) - return PTR_ERR(dev); - - if (type == NL80211_IFTYPE_MESH_POINT && - info->attrs[NL80211_ATTR_MESH_ID]) { - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != - IEEE80211_MAX_MESH_ID_LEN); - wdev->mesh_id_up_len = - nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), - wdev->mesh_id_up_len); - wdev_unlock(wdev); - } - - return 0; -} - -static int nl80211_del_interface(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]; - - if (!rdev->ops->del_virtual_intf) - return -EOPNOTSUPP; - - return rdev->ops->del_virtual_intf(&rdev->wiphy, dev); -} - -struct get_key_cookie { - struct sk_buff *msg; - int error; - int idx; -}; - -static void get_key_callback(void *c, struct key_params *params) -{ - struct nlattr *key; - struct get_key_cookie *cookie = c; - - if (params->key) - NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA, - params->key_len, params->key); - - if (params->seq) - NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ, - params->seq_len, params->seq); - - if (params->cipher) - NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER, - params->cipher); - - key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY); - if (!key) - goto nla_put_failure; - - if (params->key) - NLA_PUT(cookie->msg, NL80211_KEY_DATA, - params->key_len, params->key); - - if (params->seq) - NLA_PUT(cookie->msg, NL80211_KEY_SEQ, - params->seq_len, params->seq); - - if (params->cipher) - NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER, - params->cipher); - - NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx); - - nla_nest_end(cookie->msg, key); - - return; - nla_put_failure: - cookie->error = 1; -} - -static int nl80211_get_key(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]; - u8 key_idx = 0; - const u8 *mac_addr = NULL; - bool pairwise; - struct get_key_cookie cookie = { - .error = 0, - }; - void *hdr; - struct sk_buff *msg; - - if (info->attrs[NL80211_ATTR_KEY_IDX]) - key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); - - if (key_idx > 5) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - pairwise = !!mac_addr; - if (info->attrs[NL80211_ATTR_KEY_TYPE]) { - u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); - if (kt >= NUM_NL80211_KEYTYPES) - return -EINVAL; - if (kt != NL80211_KEYTYPE_GROUP && - kt != NL80211_KEYTYPE_PAIRWISE) - return -EINVAL; - pairwise = kt == NL80211_KEYTYPE_PAIRWISE; - } - - if (!rdev->ops->get_key) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_NEW_KEY); - if (IS_ERR(hdr)) { - nlmsg_free(msg); - return PTR_ERR(hdr); - } - - cookie.msg = msg; - cookie.idx = key_idx; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - if (mac_addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - - if (pairwise && mac_addr && - !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) { - nlmsg_free(msg); - return -ENOENT; - } - - err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise, - mac_addr, &cookie, get_key_callback); - - if (err) - goto free_msg; - - if (cookie.error) - goto nla_put_failure; - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct key_parse key; - int err; - struct net_device *dev = info->user_ptr[1]; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (key.idx < 0) - return -EINVAL; - - /* only support setting default key */ - if (!key.def && !key.defmgmt) - return -EINVAL; - - wdev_lock(dev->ieee80211_ptr); - - if (key.def) { - if (!rdev->ops->set_default_key) { - err = -EOPNOTSUPP; - goto out; - } - - err = nl80211_key_allowed(dev->ieee80211_ptr); - if (err) - goto out; - - err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, - key.def_uni, key.def_multi); - - if (err) - goto out; - -#ifdef CONFIG_CFG80211_WEXT - dev->ieee80211_ptr->wext.default_key = key.idx; -#endif - } else { - if (key.def_uni || !key.def_multi) { - err = -EINVAL; - goto out; - } - - if (!rdev->ops->set_default_mgmt_key) { - err = -EOPNOTSUPP; - goto out; - } - - err = nl80211_key_allowed(dev->ieee80211_ptr); - if (err) - goto out; - - err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, - dev, key.idx); - if (err) - goto out; - -#ifdef CONFIG_CFG80211_WEXT - dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; -#endif - } - - out: - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -static int nl80211_new_key(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 key_parse key; - const u8 *mac_addr = NULL; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (!key.p.key) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (key.type == -1) { - if (mac_addr) - key.type = NL80211_KEYTYPE_PAIRWISE; - else - key.type = NL80211_KEYTYPE_GROUP; - } - - /* for now */ - if (key.type != NL80211_KEYTYPE_PAIRWISE && - key.type != NL80211_KEYTYPE_GROUP) - return -EINVAL; - - if (!rdev->ops->add_key) - return -EOPNOTSUPP; - - if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr)) - return -EINVAL; - - wdev_lock(dev->ieee80211_ptr); - err = nl80211_key_allowed(dev->ieee80211_ptr); - if (!err) - err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr, &key.p); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -static int nl80211_del_key(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]; - u8 *mac_addr = NULL; - struct key_parse key; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (key.type == -1) { - if (mac_addr) - key.type = NL80211_KEYTYPE_PAIRWISE; - else - key.type = NL80211_KEYTYPE_GROUP; - } - - /* for now */ - if (key.type != NL80211_KEYTYPE_PAIRWISE && - key.type != NL80211_KEYTYPE_GROUP) - return -EINVAL; - - if (!rdev->ops->del_key) - return -EOPNOTSUPP; - - wdev_lock(dev->ieee80211_ptr); - err = nl80211_key_allowed(dev->ieee80211_ptr); - - if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr && - !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) - err = -ENOENT; - - if (!err) - err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, - key.type == NL80211_KEYTYPE_PAIRWISE, - mac_addr); - -#ifdef CONFIG_CFG80211_WEXT - if (!err) { - if (key.idx == dev->ieee80211_ptr->wext.default_key) - dev->ieee80211_ptr->wext.default_key = -1; - else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key) - dev->ieee80211_ptr->wext.default_mgmt_key = -1; - } -#endif - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) -{ - int (*call)(struct wiphy *wiphy, struct net_device *dev, - struct beacon_parameters *info); - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct beacon_parameters params; - int haveinfo = 0, err; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) || - !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) || - !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) || - !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP])) - return -EINVAL; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - memset(¶ms, 0, sizeof(params)); - - switch (info->genlhdr->cmd) { - case NL80211_CMD_NEW_BEACON: - /* these are required for NEW_BEACON */ - if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || - !info->attrs[NL80211_ATTR_DTIM_PERIOD] || - !info->attrs[NL80211_ATTR_BEACON_HEAD]) - return -EINVAL; - - params.interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - params.dtim_period = - nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - - err = cfg80211_validate_beacon_int(rdev, params.interval); - if (err) - return err; - - /* - * In theory, some of these attributes could be required for - * NEW_BEACON, but since they were not used when the command was - * originally added, keep them optional for old user space - * programs to work with drivers that do not need the additional - * information. - */ - if (info->attrs[NL80211_ATTR_SSID]) { - params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - params.ssid_len = - nla_len(info->attrs[NL80211_ATTR_SSID]); - if (params.ssid_len == 0 || - params.ssid_len > IEEE80211_MAX_SSID_LEN) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) { - params.hidden_ssid = nla_get_u32( - info->attrs[NL80211_ATTR_HIDDEN_SSID]); - if (params.hidden_ssid != - NL80211_HIDDEN_SSID_NOT_IN_USE && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_LEN && - params.hidden_ssid != - NL80211_HIDDEN_SSID_ZERO_CONTENTS) - return -EINVAL; - } - - params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; - - if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { - params.auth_type = nla_get_u32( - info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(params.auth_type)) - return -EINVAL; - } else - params.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - err = nl80211_crypto_settings(rdev, info, ¶ms.crypto, - NL80211_MAX_NR_CIPHER_SUITES); - if (err) - return err; - - call = rdev->ops->add_beacon; - break; - case NL80211_CMD_SET_BEACON: - call = rdev->ops->set_beacon; - break; - default: - WARN_ON(1); - return -EOPNOTSUPP; - } - - if (!call) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { - params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); - params.head_len = - nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); - haveinfo = 1; - } - - if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { - params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); - params.tail_len = - nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); - haveinfo = 1; - } - - if (!haveinfo) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IE]) { - params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]); - params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) { - params.proberesp_ies = - nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); - params.proberesp_ies_len = - nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]); - } - - if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) { - params.assocresp_ies = - nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); - params.assocresp_ies_len = - nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]); - } - - if (info->attrs[NL80211_ATTR_PROBE_RESP]) { - params.probe_resp = - nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]); - params.probe_resp_len = - nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]); - } - - err = call(&rdev->wiphy, dev, ¶ms); - if (!err && params.interval) - wdev->beacon_interval = params.interval; - return err; -} - -static int nl80211_del_beacon(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 wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - if (!rdev->ops->del_beacon) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - err = rdev->ops->del_beacon(&rdev->wiphy, dev); - if (!err) - wdev->beacon_interval = 0; - return err; -} - -static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { - [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, - [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, -}; - -static int parse_station_flags(struct genl_info *info, - struct station_parameters *params) -{ - struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; - struct nlattr *nla; - int flag; - - /* - * Try parsing the new attribute first so userspace - * can specify both for older kernels. - */ - nla = info->attrs[NL80211_ATTR_STA_FLAGS2]; - if (nla) { - struct nl80211_sta_flag_update *sta_flags; - - sta_flags = nla_data(nla); - params->sta_flags_mask = sta_flags->mask; - params->sta_flags_set = sta_flags->set; - if ((params->sta_flags_mask | - params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) - return -EINVAL; - return 0; - } - - /* if present, parse the old attribute */ - - nla = info->attrs[NL80211_ATTR_STA_FLAGS]; - if (!nla) - return 0; - - if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, - nla, sta_flags_policy)) - return -EINVAL; - - params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1; - params->sta_flags_mask &= ~1; - - for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) - if (flags[flag]) - params->sta_flags_set |= (1<<flag); - - return 0; -} - -static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, - int attr) -{ - struct nlattr *rate; - u16 bitrate; - - rate = nla_nest_start(msg, attr); - if (!rate) - goto nla_put_failure; - - /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ - bitrate = cfg80211_calculate_bitrate(info); - if (bitrate > 0) - NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); - - if (info->flags & RATE_INFO_FLAGS_MCS) - NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs); - if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); - if (info->flags & RATE_INFO_FLAGS_SHORT_GI) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); - - nla_nest_end(msg, rate); - return true; - -nla_put_failure: - return false; -} - -static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, - const u8 *mac_addr, struct station_info *sinfo) -{ - void *hdr; - struct nlattr *sinfoattr, *bss_param; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation); - - sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); - if (!sinfoattr) - goto nla_put_failure; - if (sinfo->filled & STATION_INFO_CONNECTED_TIME) - NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME, - sinfo->connected_time); - if (sinfo->filled & STATION_INFO_INACTIVE_TIME) - NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, - sinfo->inactive_time); - if (sinfo->filled & STATION_INFO_RX_BYTES) - NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES, - sinfo->rx_bytes); - if (sinfo->filled & STATION_INFO_TX_BYTES) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES, - sinfo->tx_bytes); - if (sinfo->filled & STATION_INFO_LLID) - NLA_PUT_U16(msg, NL80211_STA_INFO_LLID, - sinfo->llid); - if (sinfo->filled & STATION_INFO_PLID) - NLA_PUT_U16(msg, NL80211_STA_INFO_PLID, - sinfo->plid); - if (sinfo->filled & STATION_INFO_PLINK_STATE) - NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, - sinfo->plink_state); - if (sinfo->filled & STATION_INFO_SIGNAL) - NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, - sinfo->signal); - if (sinfo->filled & STATION_INFO_SIGNAL_AVG) - NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, - sinfo->signal_avg); - if (sinfo->filled & STATION_INFO_TX_BITRATE) { - if (!nl80211_put_sta_rate(msg, &sinfo->txrate, - NL80211_STA_INFO_TX_BITRATE)) - goto nla_put_failure; - } - if (sinfo->filled & STATION_INFO_RX_BITRATE) { - if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, - NL80211_STA_INFO_RX_BITRATE)) - goto nla_put_failure; - } - if (sinfo->filled & STATION_INFO_RX_PACKETS) - NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, - sinfo->rx_packets); - if (sinfo->filled & STATION_INFO_TX_PACKETS) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS, - sinfo->tx_packets); - if (sinfo->filled & STATION_INFO_TX_RETRIES) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES, - sinfo->tx_retries); - if (sinfo->filled & STATION_INFO_TX_FAILED) - NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED, - sinfo->tx_failed); - if (sinfo->filled & STATION_INFO_BSS_PARAM) { - bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); - if (!bss_param) - goto nla_put_failure; - - if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) - NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT); - if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) - NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE); - if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) - NLA_PUT_FLAG(msg, - NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME); - NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD, - sinfo->bss_param.dtim_period); - NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL, - sinfo->bss_param.beacon_interval); - - nla_nest_end(msg, bss_param); - } - if (sinfo->filled & STATION_INFO_STA_FLAGS) - NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS, - sizeof(struct nl80211_sta_flag_update), - &sinfo->sta_flags); - nla_nest_end(msg, sinfoattr); - - if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES) - NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len, - sinfo->assoc_req_ies); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_station(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct station_info sinfo; - struct cfg80211_registered_device *dev; - struct net_device *netdev; - u8 mac_addr[ETH_ALEN]; - int sta_idx = cb->args[1]; - int err; - - err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); - if (err) - return err; - - if (!dev->ops->dump_station) { - err = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - memset(&sinfo, 0, sizeof(sinfo)); - err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx, - mac_addr, &sinfo); - if (err == -ENOENT) - break; - if (err) - goto out_err; - - if (nl80211_send_station(skb, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, mac_addr, - &sinfo) < 0) - goto out; - - sta_idx++; - } - - - out: - cb->args[1] = sta_idx; - err = skb->len; - out_err: - nl80211_finish_netdev_dump(dev); - - return err; -} - -static int nl80211_get_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]; - struct station_info sinfo; - struct sk_buff *msg; - u8 *mac_addr = NULL; - int err; - - memset(&sinfo, 0, sizeof(sinfo)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!rdev->ops->get_station) - return -EOPNOTSUPP; - - err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo); - if (err) - return err; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, - dev, mac_addr, &sinfo) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -/* - * Get vlan interface making sure it is running and on the right wiphy. - */ -static int get_vlan(struct genl_info *info, - struct cfg80211_registered_device *rdev, - struct net_device **vlan) -{ - struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; - *vlan = NULL; - - if (vlanattr) { - *vlan = dev_get_by_index(genl_info_net(info), - nla_get_u32(vlanattr)); - if (!*vlan) - return -ENODEV; - if (!(*vlan)->ieee80211_ptr) - return -EINVAL; - if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) - return -EINVAL; - if (!netif_running(*vlan)) - return -ENETDOWN; - } - return 0; -} - -static int nl80211_set_station(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 station_parameters params; - u8 *mac_addr = NULL; - - memset(¶ms, 0, sizeof(params)); - - params.listen_interval = -1; - params.plink_state = -1; - - if (info->attrs[NL80211_ATTR_STA_AID]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { - params.supported_rates = - nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.supported_rates_len = - nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - } - - if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - - if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) - params.ht_capa = - nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); - - if (parse_station_flags(info, ¶ms)) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) - params.plink_action = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); - - if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) - params.plink_state = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); - - err = get_vlan(info, rdev, ¶ms.vlan); - if (err) - goto out; - - /* validate settings */ - err = 0; - - switch (dev->ieee80211_ptr->iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - /* disallow mesh-specific things */ - if (params.plink_action) - err = -EINVAL; - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - /* disallow things sta doesn't support */ - if (params.plink_action) - err = -EINVAL; - if (params.vlan) - err = -EINVAL; - if (params.supported_rates && - !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) - err = -EINVAL; - if (params.ht_capa) - err = -EINVAL; - if (params.listen_interval >= 0) - err = -EINVAL; - if (params.sta_flags_mask & - ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | - BIT(NL80211_STA_FLAG_TDLS_PEER))) - err = -EINVAL; - /* can't change the TDLS bit */ - if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && - (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER))) - err = -EINVAL; - break; - case NL80211_IFTYPE_MESH_POINT: - /* disallow things mesh doesn't support */ - if (params.vlan) - err = -EINVAL; - if (params.ht_capa) - err = -EINVAL; - if (params.listen_interval >= 0) - err = -EINVAL; - if (params.sta_flags_mask & - ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | - BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_AUTHORIZED))) - err = -EINVAL; - break; - default: - err = -EINVAL; - } - - if (err) - goto out; - - if (!rdev->ops->change_station) { - err = -EOPNOTSUPP; - goto out; - } - - err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms); - - out: - if (params.vlan) - dev_put(params.vlan); - - return err; -} - -static struct nla_policy -nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = { - [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, - [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, -}; - -static int nl80211_new_station(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 station_parameters params; - u8 *mac_addr = NULL; - - memset(¶ms, 0, sizeof(params)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_STA_AID]) - return -EINVAL; - - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - params.supported_rates = - nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.supported_rates_len = - nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); - params.listen_interval = - nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); - - params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); - if (!params.aid || params.aid > IEEE80211_MAX_AID) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) - params.ht_capa = - nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); - - if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) - params.plink_action = - nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); - - if (parse_station_flags(info, ¶ms)) - return -EINVAL; - - /* parse WME attributes if sta is WME capable */ - if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && - (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) && - info->attrs[NL80211_ATTR_STA_WME]) { - struct nlattr *tb[NL80211_STA_WME_MAX + 1]; - struct nlattr *nla; - - nla = info->attrs[NL80211_ATTR_STA_WME]; - err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla, - nl80211_sta_wme_policy); - if (err) - return err; - - if (tb[NL80211_STA_WME_UAPSD_QUEUES]) - params.uapsd_queues = - nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]); - if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) - return -EINVAL; - - if (tb[NL80211_STA_WME_MAX_SP]) - params.max_sp = - nla_get_u8(tb[NL80211_STA_WME_MAX_SP]); - - if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) - return -EINVAL; - - params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; - } - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - /* - * Only managed stations can add TDLS peers, and only when the - * wiphy supports external TDLS setup. - */ - if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION && - !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && - (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && - (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))) - return -EINVAL; - - err = get_vlan(info, rdev, ¶ms.vlan); - if (err) - goto out; - - /* validate settings */ - err = 0; - - if (!rdev->ops->add_station) { - err = -EOPNOTSUPP; - goto out; - } - - err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms); - - out: - if (params.vlan) - dev_put(params.vlan); - return err; -} - -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; - - if (info->attrs[NL80211_ATTR_MAC]) - mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EINVAL; - - if (!rdev->ops->del_station) - return -EOPNOTSUPP; - - return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr); -} - -static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, - u8 *dst, u8 *next_hop, - struct mpath_info *pinfo) -{ - void *hdr; - struct nlattr *pinfoattr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); - NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation); - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); - if (!pinfoattr) - goto nla_put_failure; - if (pinfo->filled & MPATH_INFO_FRAME_QLEN) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, - pinfo->frame_qlen); - if (pinfo->filled & MPATH_INFO_SN) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN, - pinfo->sn); - if (pinfo->filled & MPATH_INFO_METRIC) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, - pinfo->metric); - if (pinfo->filled & MPATH_INFO_EXPTIME) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME, - pinfo->exptime); - if (pinfo->filled & MPATH_INFO_FLAGS) - NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS, - pinfo->flags); - if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) - NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, - pinfo->discovery_timeout); - if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) - NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, - pinfo->discovery_retries); - - nla_nest_end(msg, pinfoattr); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_mpath(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct mpath_info pinfo; - struct cfg80211_registered_device *dev; - struct net_device *netdev; - u8 dst[ETH_ALEN]; - u8 next_hop[ETH_ALEN]; - int path_idx = cb->args[1]; - int err; - - err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); - if (err) - return err; - - if (!dev->ops->dump_mpath) { - err = -EOPNOTSUPP; - goto out_err; - } - - if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { - err = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx, - dst, next_hop, &pinfo); - if (err == -ENOENT) - break; - if (err) - goto out_err; - - if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, dst, next_hop, - &pinfo) < 0) - goto out; - - path_idx++; - } - - - out: - cb->args[1] = path_idx; - err = skb->len; - out_err: - nl80211_finish_netdev_dump(dev); - return err; -} - -static int nl80211_get_mpath(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 next_hop[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_mpath) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo); - if (err) - return err; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0, - dev, dst, next_hop, &pinfo) < 0) { - nlmsg_free(msg); - return -ENOBUFS; - } - - return genlmsg_reply(msg, info); -} - -static int nl80211_set_mpath(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 *dst = NULL; - u8 *next_hop = NULL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) - return -EINVAL; - - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); - - if (!rdev->ops->change_mpath) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop); -} - -static int nl80211_new_mpath(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 *dst = NULL; - u8 *next_hop = NULL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) - return -EINVAL; - - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); - - if (!rdev->ops->add_mpath) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop); -} - -static int nl80211_del_mpath(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 *dst = NULL; - - if (info->attrs[NL80211_ATTR_MAC]) - dst = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!rdev->ops->del_mpath) - return -EOPNOTSUPP; - - return rdev->ops->del_mpath(&rdev->wiphy, dev, dst); -} - -static int nl80211_set_bss(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 bss_parameters params; - - memset(¶ms, 0, sizeof(params)); - /* default to not changing parameters */ - params.use_cts_prot = -1; - params.use_short_preamble = -1; - params.use_short_slot_time = -1; - params.ap_isolate = -1; - params.ht_opmode = -1; - - if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) - params.use_cts_prot = - nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); - if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) - params.use_short_preamble = - nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); - if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) - params.use_short_slot_time = - nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); - if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { - params.basic_rates = - nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - params.basic_rates_len = - nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - } - if (info->attrs[NL80211_ATTR_AP_ISOLATE]) - params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); - if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) - params.ht_opmode = - nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); - - if (!rdev->ops->change_bss) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - return rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms); -} - -static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { - [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, -}; - -static int parse_reg_rule(struct nlattr *tb[], - struct ieee80211_reg_rule *reg_rule) -{ - struct ieee80211_freq_range *freq_range = ®_rule->freq_range; - struct ieee80211_power_rule *power_rule = ®_rule->power_rule; - - if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) - return -EINVAL; - if (!tb[NL80211_ATTR_FREQ_RANGE_START]) - return -EINVAL; - if (!tb[NL80211_ATTR_FREQ_RANGE_END]) - return -EINVAL; - if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) - return -EINVAL; - if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) - return -EINVAL; - - reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); - - freq_range->start_freq_khz = - nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); - freq_range->end_freq_khz = - nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); - freq_range->max_bandwidth_khz = - nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); - - power_rule->max_eirp = - nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); - - if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) - power_rule->max_antenna_gain = - nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); - - return 0; -} - -static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) -{ - int r; - char *data = NULL; - - /* - * You should only get this when cfg80211 hasn't yet initialized - * completely when built-in to the kernel right between the time - * window between nl80211_init() and regulatory_init(), if that is - * even possible. - */ - mutex_lock(&cfg80211_mutex); - if (unlikely(!cfg80211_regdomain)) { - mutex_unlock(&cfg80211_mutex); - return -EINPROGRESS; - } - mutex_unlock(&cfg80211_mutex); - - if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) - return -EINVAL; - - data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - - r = regulatory_hint_user(data); - - return r; -} - -static int nl80211_get_mesh_config(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct mesh_config cur_params; - int err = 0; - void *hdr; - struct nlattr *pinfoattr; - struct sk_buff *msg; - - if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!rdev->ops->get_mesh_config) - return -EOPNOTSUPP; - - wdev_lock(wdev); - /* If not connected, get default parameters */ - if (!wdev->mesh_id_len) - memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); - else - err = rdev->ops->get_mesh_config(&rdev->wiphy, dev, - &cur_params); - wdev_unlock(wdev); - - if (err) - return err; - - /* Draw up a netlink message to send back */ - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_MESH_CONFIG); - if (!hdr) - goto out; - pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); - if (!pinfoattr) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT, - cur_params.dot11MeshRetryTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT, - cur_params.dot11MeshConfirmTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT, - cur_params.dot11MeshHoldingTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, - cur_params.dot11MeshMaxPeerLinks); - NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES, - cur_params.dot11MeshMaxRetries); - NLA_PUT_U8(msg, NL80211_MESHCONF_TTL, - cur_params.dot11MeshTTL); - NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL, - cur_params.element_ttl); - NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, - cur_params.auto_open_plinks); - NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, - cur_params.dot11MeshHWMPmaxPREQretries); - NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, - cur_params.path_refresh_time); - NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, - cur_params.min_discovery_timeout); - NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, - cur_params.dot11MeshHWMPactivePathTimeout); - NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, - cur_params.dot11MeshHWMPpreqMinInterval); - NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, - cur_params.dot11MeshHWMPnetDiameterTraversalTime); - NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE, - cur_params.dot11MeshHWMPRootMode); - NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL, - cur_params.dot11MeshHWMPRannInterval); - NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, - cur_params.dot11MeshGateAnnouncementProtocol); - nla_nest_end(msg, pinfoattr); - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - out: - nlmsg_free(msg); - return -ENOBUFS; -} - -static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { - [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, - [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, - [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, - [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, - [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, - - [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, - [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, - [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, - [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, - [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 }, - [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 }, - [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 }, -}; - -static const struct nla_policy - nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { - [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, - [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, - [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, - [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY, - .len = IEEE80211_MAX_DATA_LEN }, - [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, -}; - -static int nl80211_parse_mesh_config(struct genl_info *info, - struct mesh_config *cfg, - u32 *mask_out) -{ - struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; - u32 mask = 0; - -#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ -do {\ - if (table[attr_num]) {\ - cfg->param = nla_fn(table[attr_num]); \ - mask |= (1 << (attr_num - 1)); \ - } \ -} while (0);\ - - - if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) - return -EINVAL; - if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, - info->attrs[NL80211_ATTR_MESH_CONFIG], - nl80211_meshconf_params_policy)) - return -EINVAL; - - /* This makes sure that there aren't more than 32 mesh config - * parameters (otherwise our bitfield scheme would not work.) */ - BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); - - /* Fill in the params struct */ - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, - mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, - mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, - mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, - mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, - mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, - mask, NL80211_MESHCONF_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, - mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, - mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, - mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, - nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, - mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, - mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, - mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, - nla_get_u32); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, - mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPnetDiameterTraversalTime, - mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPRootMode, mask, - NL80211_MESHCONF_HWMP_ROOTMODE, - nla_get_u8); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshHWMPRannInterval, mask, - NL80211_MESHCONF_HWMP_RANN_INTERVAL, - nla_get_u16); - FILL_IN_MESH_PARAM_IF_SET(tb, cfg, - dot11MeshGateAnnouncementProtocol, mask, - NL80211_MESHCONF_GATE_ANNOUNCEMENTS, - nla_get_u8); - if (mask_out) - *mask_out = mask; - - return 0; - -#undef FILL_IN_MESH_PARAM_IF_SET -} - -static int nl80211_parse_mesh_setup(struct genl_info *info, - struct mesh_setup *setup) -{ - struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; - - if (!info->attrs[NL80211_ATTR_MESH_SETUP]) - return -EINVAL; - if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, - info->attrs[NL80211_ATTR_MESH_SETUP], - nl80211_mesh_setup_params_policy)) - return -EINVAL; - - if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) - setup->path_sel_proto = - (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? - IEEE80211_PATH_PROTOCOL_VENDOR : - IEEE80211_PATH_PROTOCOL_HWMP; - - if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) - setup->path_metric = - (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? - IEEE80211_PATH_METRIC_VENDOR : - IEEE80211_PATH_METRIC_AIRTIME; - - - if (tb[NL80211_MESH_SETUP_IE]) { - struct nlattr *ieattr = - tb[NL80211_MESH_SETUP_IE]; - if (!is_valid_ie_attr(ieattr)) - return -EINVAL; - setup->ie = nla_data(ieattr); - setup->ie_len = nla_len(ieattr); - } - setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); - setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); - - return 0; -} - -static int nl80211_update_mesh_config(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct mesh_config cfg; - u32 mask; - int err; - - if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) - return -EOPNOTSUPP; - - if (!rdev->ops->update_mesh_config) - return -EOPNOTSUPP; - - err = nl80211_parse_mesh_config(info, &cfg, &mask); - if (err) - return err; - - wdev_lock(wdev); - if (!wdev->mesh_id_len) - err = -ENOLINK; - - if (!err) - err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, - mask, &cfg); - - wdev_unlock(wdev); - - return err; -} - -static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) -{ - struct sk_buff *msg; - void *hdr = NULL; - struct nlattr *nl_reg_rules; - unsigned int i; - int err = -EINVAL; - - mutex_lock(&cfg80211_mutex); - - if (!cfg80211_regdomain) - goto out; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) { - err = -ENOBUFS; - goto out; - } - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_REG); - if (!hdr) - goto put_failure; - - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, - cfg80211_regdomain->alpha2); - - nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); - if (!nl_reg_rules) - goto nla_put_failure; - - for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { - struct nlattr *nl_reg_rule; - const struct ieee80211_reg_rule *reg_rule; - const struct ieee80211_freq_range *freq_range; - const struct ieee80211_power_rule *power_rule; - - reg_rule = &cfg80211_regdomain->reg_rules[i]; - freq_range = ®_rule->freq_range; - power_rule = ®_rule->power_rule; - - nl_reg_rule = nla_nest_start(msg, i); - if (!nl_reg_rule) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS, - reg_rule->flags); - NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START, - freq_range->start_freq_khz); - NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END, - freq_range->end_freq_khz); - NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW, - freq_range->max_bandwidth_khz); - NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, - power_rule->max_antenna_gain); - NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, - power_rule->max_eirp); - - nla_nest_end(msg, nl_reg_rule); - } - - nla_nest_end(msg, nl_reg_rules); - - genlmsg_end(msg, hdr); - err = genlmsg_reply(msg, info); - goto out; - -nla_put_failure: - genlmsg_cancel(msg, hdr); -put_failure: - nlmsg_free(msg); - err = -EMSGSIZE; -out: - mutex_unlock(&cfg80211_mutex); - return err; -} - -static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) -{ - struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; - struct nlattr *nl_reg_rule; - char *alpha2 = NULL; - int rem_reg_rules = 0, r = 0; - u32 num_rules = 0, rule_idx = 0, size_of_regd; - struct ieee80211_regdomain *rd = NULL; - - if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_REG_RULES]) - return -EINVAL; - - alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); - - nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { - num_rules++; - if (num_rules > NL80211_MAX_SUPP_REG_RULES) - return -EINVAL; - } - - mutex_lock(&cfg80211_mutex); - - if (!reg_is_valid_request(alpha2)) { - r = -EINVAL; - goto bad_reg; - } - - size_of_regd = sizeof(struct ieee80211_regdomain) + - (num_rules * sizeof(struct ieee80211_reg_rule)); - - rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) { - r = -ENOMEM; - goto bad_reg; - } - - rd->n_reg_rules = num_rules; - rd->alpha2[0] = alpha2[0]; - rd->alpha2[1] = alpha2[1]; - - nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], - rem_reg_rules) { - nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, - nla_data(nl_reg_rule), nla_len(nl_reg_rule), - reg_rule_policy); - r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); - if (r) - goto bad_reg; - - rule_idx++; - - if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { - r = -EINVAL; - goto bad_reg; - } - } - - BUG_ON(rule_idx != num_rules); - - r = set_regdom(rd); - - mutex_unlock(&cfg80211_mutex); - - return r; - - bad_reg: - mutex_unlock(&cfg80211_mutex); - kfree(rd); - return r; -} - -static int validate_scan_freqs(struct nlattr *freqs) -{ - struct nlattr *attr1, *attr2; - int n_channels = 0, tmp1, tmp2; - - nla_for_each_nested(attr1, freqs, tmp1) { - n_channels++; - /* - * Some hardware has a limited channel list for - * scanning, and it is pretty much nonsensical - * to scan for a channel twice, so disallow that - * and don't require drivers to check that the - * channel list they get isn't longer than what - * they can scan, as long as they can scan all - * the channels they registered at once. - */ - nla_for_each_nested(attr2, freqs, tmp2) - if (attr1 != attr2 && - nla_get_u32(attr1) == nla_get_u32(attr2)) - return 0; - } - - return n_channels; -} - -static int nl80211_trigger_scan(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 cfg80211_scan_request *request; - struct nlattr *attr; - struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_channels, i; - size_t ie_len; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - wiphy = &rdev->wiphy; - - if (!rdev->ops->scan) - return -EOPNOTSUPP; - - if (rdev->scan_req) - return -EBUSY; - - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - n_channels = validate_scan_freqs( - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); - if (!n_channels) - return -EINVAL; - } else { - enum ieee80211_band band; - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) - n_ssids++; - - if (n_ssids > wiphy->max_scan_ssids) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IE]) - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - else - ie_len = 0; - - if (ie_len > wiphy->max_scan_ie_len) - return -EINVAL; - - request = kzalloc(sizeof(*request) - + sizeof(*request->ssids) * n_ssids - + sizeof(*request->channels) * n_channels - + ie_len, GFP_KERNEL); - if (!request) - return -ENOMEM; - - if (n_ssids) - request->ssids = (void *)&request->channels[n_channels]; - request->n_ssids = n_ssids; - if (ie_len) { - if (request->ssids) - request->ie = (void *)(request->ssids + n_ssids); - else - request->ie = (void *)(request->channels + n_channels); - } - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - /* user specified, bail out if channel not found */ - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) { - struct ieee80211_channel *chan; - - chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); - - if (!chan) { - err = -EINVAL; - goto out_free; - } - - /* ignore disabled channels */ - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } else { - enum ieee80211_band band; - - /* all channels */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - if (!wiphy->bands[band]) - continue; - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - struct ieee80211_channel *chan; - - chan = &wiphy->bands[band]->channels[j]; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } - } - - if (!i) { - err = -EINVAL; - goto out_free; - } - - request->n_channels = i; - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { - if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; - } - request->ssids[i].ssid_len = nla_len(attr); - memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); - i++; - } - } - - if (info->attrs[NL80211_ATTR_IE]) { - request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - memcpy((void *)request->ie, - nla_data(info->attrs[NL80211_ATTR_IE]), - request->ie_len); - } - - for (i = 0; i < IEEE80211_NUM_BANDS; i++) - if (wiphy->bands[i]) - request->rates[i] = - (1 << wiphy->bands[i]->n_bitrates) - 1; - - if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], - tmp) { - enum ieee80211_band band = nla_type(attr); - - if (band < 0 || band >= IEEE80211_NUM_BANDS) { - err = -EINVAL; - goto out_free; - } - err = ieee80211_get_ratemask(wiphy->bands[band], - nla_data(attr), - nla_len(attr), - &request->rates[band]); - if (err) - goto out_free; - } - } - - request->no_cck = - nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - - request->dev = dev; - request->wiphy = &rdev->wiphy; - - rdev->scan_req = request; - err = rdev->ops->scan(&rdev->wiphy, dev, request); - - if (!err) { - nl80211_send_scan_start(rdev, dev); - dev_hold(dev); - } else { - out_free: - rdev->scan_req = NULL; - kfree(request); - } - - return err; -} - -static int nl80211_start_sched_scan(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_sched_scan_request *request; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct nlattr *attr; - struct wiphy *wiphy; - int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i; - u32 interval; - enum ieee80211_band band; - size_t ie_len; - struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_start) - return -EOPNOTSUPP; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) - return -EINVAL; - - interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); - if (interval == 0) - return -EINVAL; - - wiphy = &rdev->wiphy; - - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - n_channels = validate_scan_freqs( - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]); - if (!n_channels) - return -EINVAL; - } else { - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], - tmp) - n_ssids++; - - if (n_ssids > wiphy->max_sched_scan_ssids) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], - tmp) - n_match_sets++; - - if (n_match_sets > wiphy->max_match_sets) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_IE]) - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - else - ie_len = 0; - - if (ie_len > wiphy->max_sched_scan_ie_len) - return -EINVAL; - - mutex_lock(&rdev->sched_scan_mtx); - - if (rdev->sched_scan_req) { - err = -EINPROGRESS; - goto out; - } - - request = kzalloc(sizeof(*request) - + sizeof(*request->ssids) * n_ssids - + sizeof(*request->match_sets) * n_match_sets - + sizeof(*request->channels) * n_channels - + ie_len, GFP_KERNEL); - if (!request) { - err = -ENOMEM; - goto out; - } - - if (n_ssids) - request->ssids = (void *)&request->channels[n_channels]; - request->n_ssids = n_ssids; - if (ie_len) { - if (request->ssids) - request->ie = (void *)(request->ssids + n_ssids); - else - request->ie = (void *)(request->channels + n_channels); - } - - if (n_match_sets) { - if (request->ie) - request->match_sets = (void *)(request->ie + ie_len); - else if (request->ssids) - request->match_sets = - (void *)(request->ssids + n_ssids); - else - request->match_sets = - (void *)(request->channels + n_channels); - } - request->n_match_sets = n_match_sets; - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { - /* user specified, bail out if channel not found */ - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], - tmp) { - struct ieee80211_channel *chan; - - chan = ieee80211_get_channel(wiphy, nla_get_u32(attr)); - - if (!chan) { - err = -EINVAL; - goto out_free; - } - - /* ignore disabled channels */ - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } else { - /* all channels */ - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - if (!wiphy->bands[band]) - continue; - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - struct ieee80211_channel *chan; - - chan = &wiphy->bands[band]->channels[j]; - - if (chan->flags & IEEE80211_CHAN_DISABLED) - continue; - - request->channels[i] = chan; - i++; - } - } - } - - if (!i) { - err = -EINVAL; - goto out_free; - } - - request->n_channels = i; - - i = 0; - if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], - tmp) { - if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; - } - request->ssids[i].ssid_len = nla_len(attr); - memcpy(request->ssids[i].ssid, nla_data(attr), - nla_len(attr)); - i++; - } - } - - i = 0; - if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { - nla_for_each_nested(attr, - info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH], - tmp) { - struct nlattr *ssid; - - nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX, - nla_data(attr), nla_len(attr), - nl80211_match_policy); - ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID]; - if (ssid) { - if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out_free; - } - memcpy(request->match_sets[i].ssid.ssid, - nla_data(ssid), nla_len(ssid)); - request->match_sets[i].ssid.ssid_len = - nla_len(ssid); - } - i++; - } - } - - if (info->attrs[NL80211_ATTR_IE]) { - request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - memcpy((void *)request->ie, - nla_data(info->attrs[NL80211_ATTR_IE]), - request->ie_len); - } - - request->dev = dev; - request->wiphy = &rdev->wiphy; - request->interval = interval; - - err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request); - if (!err) { - rdev->sched_scan_req = request; - nl80211_send_sched_scan(rdev, dev, - NL80211_CMD_START_SCHED_SCAN); - goto out; - } - -out_free: - kfree(request); -out: - mutex_unlock(&rdev->sched_scan_mtx); - return err; -} - -static int nl80211_stop_sched_scan(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) || - !rdev->ops->sched_scan_stop) - return -EOPNOTSUPP; - - mutex_lock(&rdev->sched_scan_mtx); - err = __cfg80211_stop_sched_scan(rdev, false); - mutex_unlock(&rdev->sched_scan_mtx); - - return err; -} - -static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, - u32 seq, int flags, - struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - struct cfg80211_internal_bss *intbss) -{ - struct cfg80211_bss *res = &intbss->pub; - void *hdr; - struct nlattr *bss; - int i; - - ASSERT_WDEV_LOCK(wdev); - - hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags, - NL80211_CMD_NEW_SCAN_RESULTS); - if (!hdr) - return -1; - - genl_dump_check_consistent(cb, hdr, &nl80211_fam); - - NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex); - - bss = nla_nest_start(msg, NL80211_ATTR_BSS); - if (!bss) - goto nla_put_failure; - if (!is_zero_ether_addr(res->bssid)) - NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid); - if (res->information_elements && res->len_information_elements) - NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, - res->len_information_elements, - res->information_elements); - if (res->beacon_ies && res->len_beacon_ies && - res->beacon_ies != res->information_elements) - NLA_PUT(msg, NL80211_BSS_BEACON_IES, - res->len_beacon_ies, res->beacon_ies); - if (res->tsf) - NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); - if (res->beacon_interval) - NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval); - NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability); - NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq); - NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO, - jiffies_to_msecs(jiffies - intbss->ts)); - - switch (rdev->wiphy.signal_type) { - case CFG80211_SIGNAL_TYPE_MBM: - NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal); - break; - case CFG80211_SIGNAL_TYPE_UNSPEC: - NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal); - break; - default: - break; - } - - switch (wdev->iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_STATION: - if (intbss == wdev->current_bss) - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_ASSOCIATED); - else for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (intbss != wdev->auth_bsses[i]) - continue; - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_AUTHENTICATED); - break; - } - break; - case NL80211_IFTYPE_ADHOC: - if (intbss == wdev->current_bss) - NLA_PUT_U32(msg, NL80211_BSS_STATUS, - NL80211_BSS_STATUS_IBSS_JOINED); - break; - default: - break; - } - - nla_nest_end(msg, bss); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_scan(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct cfg80211_registered_device *rdev; - struct net_device *dev; - struct cfg80211_internal_bss *scan; - struct wireless_dev *wdev; - int start = cb->args[1], idx = 0; - int err; - - err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev); - if (err) - return err; - - wdev = dev->ieee80211_ptr; - - wdev_lock(wdev); - spin_lock_bh(&rdev->bss_lock); - cfg80211_bss_expire(rdev); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)) - cb->seq = rdev->bss_generation; -#endif - - list_for_each_entry(scan, &rdev->bss_list, list) { - if (++idx <= start) - continue; - if (nl80211_send_bss(skb, cb, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - rdev, wdev, scan) < 0) { - idx--; - break; - } - } - - spin_unlock_bh(&rdev->bss_lock); - wdev_unlock(wdev); - - cb->args[1] = idx; - nl80211_finish_netdev_dump(rdev); - - return skb->len; -} - -static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq, - int flags, struct net_device *dev, - struct survey_info *survey) -{ - void *hdr; - struct nlattr *infoattr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, - NL80211_CMD_NEW_SURVEY_RESULTS); - if (!hdr) - return -ENOMEM; - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - - infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO); - if (!infoattr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY, - survey->channel->center_freq); - if (survey->filled & SURVEY_INFO_NOISE_DBM) - NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE, - survey->noise); - if (survey->filled & SURVEY_INFO_IN_USE) - NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME, - survey->channel_time); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY, - survey->channel_time_busy); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY, - survey->channel_time_ext_busy); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX, - survey->channel_time_rx); - if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) - NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX, - survey->channel_time_tx); - - nla_nest_end(msg, infoattr); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int nl80211_dump_survey(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct survey_info survey; - struct cfg80211_registered_device *dev; - struct net_device *netdev; - int survey_idx = cb->args[1]; - int res; - - res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev); - if (res) - return res; - - if (!dev->ops->dump_survey) { - res = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - struct ieee80211_channel *chan; - - res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx, - &survey); - if (res == -ENOENT) - break; - if (res) - goto out_err; - - /* Survey without a channel doesn't make sense */ - if (!survey.channel) { - res = -EINVAL; - goto out; - } - - chan = ieee80211_get_channel(&dev->wiphy, - survey.channel->center_freq); - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { - survey_idx++; - continue; - } - - if (nl80211_send_survey(skb, - NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - netdev, - &survey) < 0) - goto out; - survey_idx++; - } - - out: - cb->args[1] = survey_idx; - res = skb->len; - out_err: - nl80211_finish_netdev_dump(dev); - return res; -} - -static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) -{ - return auth_type <= NL80211_AUTHTYPE_MAX; -} - -static bool nl80211_valid_wpa_versions(u32 wpa_versions) -{ - return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | - NL80211_WPA_VERSION_2)); -} - -static int nl80211_authenticate(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 ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL; - int err, ssid_len, ie_len = 0; - enum nl80211_auth_type auth_type; - struct key_parse key; - bool local_state_change; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_SSID]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - err = nl80211_parse_key(info, &key); - if (err) - return err; - - if (key.idx >= 0) { - if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) - return -EINVAL; - if (!key.p.key || !key.p.key_len) - return -EINVAL; - if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || - key.p.key_len != WLAN_KEY_LEN_WEP40) && - (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || - key.p.key_len != WLAN_KEY_LEN_WEP104)) - return -EINVAL; - if (key.idx > 4) - return -EINVAL; - } else { - key.p.key_len = 0; - key.p.key = NULL; - } - - if (key.idx >= 0) { - int i; - bool ok = false; - for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) { - if (key.p.cipher == rdev->wiphy.cipher_suites[i]) { - ok = true; - break; - } - } - if (!ok) - return -EINVAL; - } - - if (!rdev->ops->auth) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - chan = ieee80211_get_channel(&rdev->wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; - - ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(auth_type)) - return -EINVAL; - - local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - - return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, - ssid, ssid_len, ie, ie_len, - key.p.key, key.p.key_len, key.idx, - local_state_change); -} - -static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, - struct genl_info *info, - struct cfg80211_crypto_settings *settings, - int cipher_limit) -{ - memset(settings, 0, sizeof(*settings)); - - settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; - - if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { - u16 proto; - proto = nla_get_u16( - info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); - settings->control_port_ethertype = cpu_to_be16(proto); - if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && - proto != ETH_P_PAE) - return -EINVAL; - if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) - settings->control_port_no_encrypt = true; - } else - settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); - - if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { - void *data; - int len, i; - - data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); - len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); - settings->n_ciphers_pairwise = len / sizeof(u32); - - if (len % sizeof(u32)) - return -EINVAL; - - if (settings->n_ciphers_pairwise > cipher_limit) - return -EINVAL; - - memcpy(settings->ciphers_pairwise, data, len); - - for (i = 0; i < settings->n_ciphers_pairwise; i++) - if (!cfg80211_supported_cipher_suite( - &rdev->wiphy, - settings->ciphers_pairwise[i])) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { - settings->cipher_group = - nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); - if (!cfg80211_supported_cipher_suite(&rdev->wiphy, - settings->cipher_group)) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { - settings->wpa_versions = - nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); - if (!nl80211_valid_wpa_versions(settings->wpa_versions)) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_AKM_SUITES]) { - void *data; - int len; - - data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); - len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); - settings->n_akm_suites = len / sizeof(u32); - - if (len % sizeof(u32)) - return -EINVAL; - - if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) - return -EINVAL; - - memcpy(settings->akm_suites, data, len); - } - - return 0; -} - -static int nl80211_associate(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 cfg80211_crypto_settings crypto; - struct ieee80211_channel *chan; - const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; - int err, ssid_len, ie_len = 0; - bool use_mfp = false; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC] || - !info->attrs[NL80211_ATTR_SSID] || - !info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - if (!rdev->ops->assoc) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - chan = ieee80211_get_channel(&rdev->wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) - return -EINVAL; - - ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - if (info->attrs[NL80211_ATTR_USE_MFP]) { - enum nl80211_mfp mfp = - nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); - if (mfp == NL80211_MFP_REQUIRED) - use_mfp = true; - else if (mfp != NL80211_MFP_NO) - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_PREV_BSSID]) - prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]); - - err = nl80211_crypto_settings(rdev, info, &crypto, 1); - if (!err) - err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid, - ssid, ssid_len, ie, ie_len, use_mfp, - &crypto); - - return err; -} - -static int nl80211_deauthenticate(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]; - const u8 *ie = NULL, *bssid; - int ie_len = 0; - u16 reason_code; - bool local_state_change; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_REASON_CODE]) - return -EINVAL; - - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (reason_code == 0) { - /* Reason Code 0 is reserved */ - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - - return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); -} - -static int nl80211_disassociate(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]; - const u8 *ie = NULL, *bssid; - int ie_len = 0; - u16 reason_code; - bool local_state_change; - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_REASON_CODE]) - return -EINVAL; - - if (!rdev->ops->disassoc) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - if (reason_code == 0) { - /* Reason Code 0 is reserved */ - return -EINVAL; - } - - if (info->attrs[NL80211_ATTR_IE]) { - ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; - - return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, - local_state_change); -} - -static bool -nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, - int mcast_rate[IEEE80211_NUM_BANDS], - int rateval) -{ - struct wiphy *wiphy = &rdev->wiphy; - bool found = false; - int band, i; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband; - - sband = wiphy->bands[band]; - if (!sband) - continue; - - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == rateval) { - mcast_rate[band] = i + 1; - found = true; - break; - } - } - } - - return found; -} - -static int nl80211_join_ibss(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 cfg80211_ibss_params ibss; - struct wiphy *wiphy; - struct cfg80211_cached_keys *connkeys = NULL; - int err; - - memset(&ibss, 0, sizeof(ibss)); - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || - !info->attrs[NL80211_ATTR_SSID] || - !nla_len(info->attrs[NL80211_ATTR_SSID])) - return -EINVAL; - - ibss.beacon_interval = 100; - - if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { - ibss.beacon_interval = - nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000) - return -EINVAL; - } - - if (!rdev->ops->join_ibss) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - wiphy = &rdev->wiphy; - - if (info->attrs[NL80211_ATTR_MAC]) { - ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (!is_valid_ether_addr(ibss.bssid)) - return -EINVAL; - } - ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - ibss.channel = ieee80211_get_channel(wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!ibss.channel || - ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || - ibss.channel->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - - ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; - ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; - - if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { - u8 *rates = - nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - int n_rates = - nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); - struct ieee80211_supported_band *sband = - wiphy->bands[ibss.channel->band]; - int err; - - err = ieee80211_get_ratemask(sband, rates, n_rates, - &ibss.basic_rates); - if (err) - return err; - } - - if (info->attrs[NL80211_ATTR_MCAST_RATE] && - !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, - nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) - return -EINVAL; - - if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { - connkeys = nl80211_parse_connkeys(rdev, - info->attrs[NL80211_ATTR_KEYS]); - if (IS_ERR(connkeys)) - return PTR_ERR(connkeys); - } - - err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); - if (err) - kfree(connkeys); - return err; -} - -static int nl80211_leave_ibss(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]; - - if (!rdev->ops->leave_ibss) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - return cfg80211_leave_ibss(rdev, dev, false); -} - -#ifdef CONFIG_NL80211_TESTMODE -static struct genl_multicast_group nl80211_testmode_mcgrp = { - .name = "testmode", -}; - -static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; - - if (!info->attrs[NL80211_ATTR_TESTDATA]) - return -EINVAL; - - err = -EOPNOTSUPP; - if (rdev->ops->testmode_cmd) { - rdev->testmode_info = info; - err = rdev->ops->testmode_cmd(&rdev->wiphy, - nla_data(info->attrs[NL80211_ATTR_TESTDATA]), - nla_len(info->attrs[NL80211_ATTR_TESTDATA])); - rdev->testmode_info = NULL; - } - - return err; -} - -static int nl80211_testmode_dump(struct sk_buff *skb, - struct netlink_callback *cb) -{ - struct cfg80211_registered_device *dev; - int err; - long phy_idx; - void *data = NULL; - int data_len = 0; - - if (cb->args[0]) { - /* - * 0 is a valid index, but not valid for args[0], - * so we need to offset by 1. - */ - phy_idx = cb->args[0] - 1; - } else { - err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize, - nl80211_fam.attrbuf, nl80211_fam.maxattr, - nl80211_policy); - if (err) - return err; - if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) - return -EINVAL; - phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]); - if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]) - cb->args[1] = - (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA]; - } - - if (cb->args[1]) { - data = nla_data((void *)cb->args[1]); - data_len = nla_len((void *)cb->args[1]); - } - - mutex_lock(&cfg80211_mutex); - dev = cfg80211_rdev_by_wiphy_idx(phy_idx); - if (!dev) { - mutex_unlock(&cfg80211_mutex); - return -ENOENT; - } - cfg80211_lock_rdev(dev); - mutex_unlock(&cfg80211_mutex); - - if (!dev->ops->testmode_dump) { - err = -EOPNOTSUPP; - goto out_err; - } - - while (1) { - void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - NL80211_CMD_TESTMODE); - struct nlattr *tmdata; - - if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) { - genlmsg_cancel(skb, hdr); - break; - } - - tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - if (!tmdata) { - genlmsg_cancel(skb, hdr); - break; - } - err = dev->ops->testmode_dump(&dev->wiphy, skb, cb, - data, data_len); - nla_nest_end(skb, tmdata); - - if (err == -ENOBUFS || err == -ENOENT) { - genlmsg_cancel(skb, hdr); - break; - } else if (err) { - genlmsg_cancel(skb, hdr); - goto out_err; - } - - genlmsg_end(skb, hdr); - } - - err = skb->len; - /* see above */ - cb->args[0] = phy_idx + 1; - out_err: - cfg80211_unlock_rdev(dev); - return err; -} - -static struct sk_buff * -__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, - int approxlen, u32 pid, u32 seq, gfp_t gfp) -{ - struct sk_buff *skb; - void *hdr; - struct nlattr *data; - - skb = nlmsg_new(approxlen + 100, gfp); - if (!skb) - return NULL; - - hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE); - if (!hdr) { - kfree_skb(skb); - return NULL; - } - - NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - - ((void **)skb->cb)[0] = rdev; - ((void **)skb->cb)[1] = hdr; - ((void **)skb->cb)[2] = data; - - return skb; - - nla_put_failure: - kfree_skb(skb); - return NULL; -} - -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, - int approxlen) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (WARN_ON(!rdev->testmode_info)) - return NULL; - - return __cfg80211_testmode_alloc_skb(rdev, approxlen, - rdev->testmode_info->snd_pid, - rdev->testmode_info->snd_seq, - GFP_KERNEL); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); - -int cfg80211_testmode_reply(struct sk_buff *skb) -{ - struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - - if (WARN_ON(!rdev->testmode_info)) { - kfree_skb(skb); - return -EINVAL; - } - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - return genlmsg_reply(skb, rdev->testmode_info); -} -EXPORT_SYMBOL(cfg80211_testmode_reply); - -struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, - int approxlen, gfp_t gfp) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); - -void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) -{ - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp); -} -EXPORT_SYMBOL(cfg80211_testmode_event); -#endif - -static int nl80211_connect(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 cfg80211_connect_params connect; - struct wiphy *wiphy; - struct cfg80211_cached_keys *connkeys = NULL; - int err; - - memset(&connect, 0, sizeof(connect)); - - if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_SSID] || - !nla_len(info->attrs[NL80211_ATTR_SSID])) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { - connect.auth_type = - nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); - if (!nl80211_valid_auth_type(connect.auth_type)) - return -EINVAL; - } else - connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; - - err = nl80211_crypto_settings(rdev, info, &connect.crypto, - NL80211_MAX_NR_CIPHER_SUITES); - if (err) - return err; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - wiphy = &rdev->wiphy; - - if (info->attrs[NL80211_ATTR_MAC]) - connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); - connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); - - if (info->attrs[NL80211_ATTR_IE]) { - connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); - connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); - } - - if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - connect.channel = - ieee80211_get_channel(wiphy, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); - if (!connect.channel || - connect.channel->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - } - - if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { - connkeys = nl80211_parse_connkeys(rdev, - info->attrs[NL80211_ATTR_KEYS]); - if (IS_ERR(connkeys)) - return PTR_ERR(connkeys); - } - - err = cfg80211_connect(rdev, dev, &connect, connkeys); - if (err) - kfree(connkeys); - return err; -} - -static int nl80211_disconnect(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]; - u16 reason; - - if (!info->attrs[NL80211_ATTR_REASON_CODE]) - reason = WLAN_REASON_DEAUTH_LEAVING; - else - reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); - - if (reason == 0) - return -EINVAL; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - return cfg80211_disconnect(rdev, dev, reason, true); -} - -static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net *net; - int err; - u32 pid; - - if (!info->attrs[NL80211_ATTR_PID]) - return -EINVAL; - - pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); - - net = get_net_ns_by_pid(pid); - if (IS_ERR(net)) - return PTR_ERR(net); - - err = 0; - - /* check if anything to do */ - if (!net_eq(wiphy_net(&rdev->wiphy), net)) - err = cfg80211_switch_netns(rdev, net); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - put_net(net); -#endif - return err; -} - -static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_pmksa *pmksa) = NULL; - struct net_device *dev = info->user_ptr[1]; - struct cfg80211_pmksa pmksa; - - memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!info->attrs[NL80211_ATTR_PMKID]) - return -EINVAL; - - pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]); - pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - switch (info->genlhdr->cmd) { - case NL80211_CMD_SET_PMKSA: - rdev_ops = rdev->ops->set_pmksa; - break; - case NL80211_CMD_DEL_PMKSA: - rdev_ops = rdev->ops->del_pmksa; - break; - default: - WARN_ON(1); - break; - } - - if (!rdev_ops) - return -EOPNOTSUPP; - - return rdev_ops(&rdev->wiphy, dev, &pmksa); -} - -static int nl80211_flush_pmksa(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]; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - if (!rdev->ops->flush_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->flush_pmksa(&rdev->wiphy, dev); -} - -static int nl80211_tdls_mgmt(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 action_code, dialog_token; - u16 status_code; - u8 *peer; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || - !rdev->ops->tdls_mgmt) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || - !info->attrs[NL80211_ATTR_STATUS_CODE] || - !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || - !info->attrs[NL80211_ATTR_IE] || - !info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - peer = nla_data(info->attrs[NL80211_ATTR_MAC]); - action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]); - status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]); - dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); - - return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code, - dialog_token, status_code, - nla_data(info->attrs[NL80211_ATTR_IE]), - nla_len(info->attrs[NL80211_ATTR_IE])); -} - -static int nl80211_tdls_oper(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]; - enum nl80211_tdls_operation operation; - u8 *peer; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || - !rdev->ops->tdls_oper) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || - !info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]); - peer = nla_data(info->attrs[NL80211_ATTR_MAC]); - - return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation); -} - -static int nl80211_remain_on_channel(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 ieee80211_channel *chan; - struct sk_buff *msg; - void *hdr; - u64 cookie; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq, duration; - int err; - - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || - !info->attrs[NL80211_ATTR_DURATION]) - return -EINVAL; - - duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); - - /* - * We should be on that channel for at least one jiffie, - * and more than 5 seconds seems excessive. - */ - if (!duration || !msecs_to_jiffies(duration) || - duration > rdev->wiphy.max_remain_on_channel_duration) - return -EINVAL; - - if (!rdev->ops->remain_on_channel) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - return -EINVAL; - } - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (chan == NULL) - return -EINVAL; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_REMAIN_ON_CHANNEL); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; - } - - err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, - channel_type, duration, &cookie); - - if (err) - goto free_msg; - - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - genlmsg_end(msg, hdr); - - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_cancel_remain_on_channel(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]; - u64 cookie; - - if (!info->attrs[NL80211_ATTR_COOKIE]) - return -EINVAL; - - if (!rdev->ops->cancel_remain_on_channel) - return -EOPNOTSUPP; - - cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - - return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); -} - -static u32 rateset_to_mask(struct ieee80211_supported_band *sband, - u8 *rates, u8 rates_len) -{ - u8 i; - u32 mask = 0; - - for (i = 0; i < rates_len; i++) { - int rate = (rates[i] & 0x7f) * 5; - int ridx; - for (ridx = 0; ridx < sband->n_bitrates; ridx++) { - struct ieee80211_rate *srate = - &sband->bitrates[ridx]; - if (rate == srate->bitrate) { - mask |= 1 << ridx; - break; - } - } - if (ridx == sband->n_bitrates) - return 0; /* rate not found */ - } - - return mask; -} - -static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { - [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_RATES }, -}; - -static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, - struct genl_info *info) -{ - struct nlattr *tb[NL80211_TXRATE_MAX + 1]; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct cfg80211_bitrate_mask mask; - int rem, i; - struct net_device *dev = info->user_ptr[1]; - struct nlattr *tx_rates; - struct ieee80211_supported_band *sband; - - if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) - return -EINVAL; - - if (!rdev->ops->set_bitrate_mask) - return -EOPNOTSUPP; - - memset(&mask, 0, sizeof(mask)); - /* Default to all rates enabled */ - for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - sband = rdev->wiphy.bands[i]; - mask.control[i].legacy = - sband ? (1 << sband->n_bitrates) - 1 : 0; - } - - /* - * The nested attribute uses enum nl80211_band as the index. This maps - * directly to the enum ieee80211_band values used in cfg80211. - */ - nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) - { - enum ieee80211_band band = nla_type(tx_rates); - if (band < 0 || band >= IEEE80211_NUM_BANDS) - return -EINVAL; - sband = rdev->wiphy.bands[band]; - if (sband == NULL) - return -EINVAL; - nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), - nla_len(tx_rates), nl80211_txattr_policy); - if (tb[NL80211_TXRATE_LEGACY]) { - mask.control[band].legacy = rateset_to_mask( - sband, - nla_data(tb[NL80211_TXRATE_LEGACY]), - nla_len(tb[NL80211_TXRATE_LEGACY])); - if (mask.control[band].legacy == 0) - return -EINVAL; - } - } - - return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); -} - -static int nl80211_register_mgmt(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]; - u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; - - if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) - return -EINVAL; - - if (info->attrs[NL80211_ATTR_FRAME_TYPE]) - frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]); - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - /* not much point in registering if we can't reply */ - if (!rdev->ops->mgmt_tx) - return -EOPNOTSUPP; - - return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid, - frame_type, - nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]), - nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH])); -} - -static int nl80211_tx_mgmt(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 ieee80211_channel *chan; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - bool channel_type_valid = false; - u32 freq; - int err; - void *hdr = NULL; - u64 cookie; - struct sk_buff *msg = NULL; - unsigned int wait = 0; - bool offchan, no_cck, dont_wait_for_ack; - - dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; - - if (!info->attrs[NL80211_ATTR_FRAME] || - !info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - - if (!rdev->ops->mgmt_tx) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - if (info->attrs[NL80211_ATTR_DURATION]) { - if (!rdev->ops->mgmt_tx_cancel_wait) - return -EINVAL; - wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); - } - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - channel_type = nla_get_u32( - info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20 && - channel_type != NL80211_CHAN_HT40PLUS && - channel_type != NL80211_CHAN_HT40MINUS) - return -EINVAL; - channel_type_valid = true; - } - - offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; - - no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (chan == NULL) - return -EINVAL; - - if (!dont_wait_for_ack) { - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_FRAME); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; - } - } - - err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, - channel_type_valid, wait, - nla_data(info->attrs[NL80211_ATTR_FRAME]), - nla_len(info->attrs[NL80211_ATTR_FRAME]), - no_cck, dont_wait_for_ack, &cookie); - if (err) - goto free_msg; - - if (msg) { - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - } - - return 0; - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_tx_mgmt_cancel_wait(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]; - u64 cookie; - - if (!info->attrs[NL80211_ATTR_COOKIE]) - return -EINVAL; - - if (!rdev->ops->mgmt_tx_cancel_wait) - return -EOPNOTSUPP; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); - - return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); -} - -static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - u8 ps_state; - bool state; - int err; - - if (!info->attrs[NL80211_ATTR_PS_STATE]) - return -EINVAL; - - ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]); - - if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) - return -EINVAL; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->set_power_mgmt) - return -EOPNOTSUPP; - - state = (ps_state == NL80211_PS_ENABLED) ? true : false; - - if (state == wdev->ps) - return 0; - - err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state, - wdev->ps_timeout); - if (!err) - wdev->ps = state; - return err; -} - -static int nl80211_priv_cmd(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - int err; - - if (!info->attrs[NL80211_ATTR_PRIV_CMD]) - return -EINVAL; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->priv_cmd) - return -EOPNOTSUPP; - - err = rdev->ops->priv_cmd(wdev->wiphy, dev, - nla_data(info->attrs[NL80211_ATTR_PRIV_CMD])); - - return err; -} - -static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - enum nl80211_ps_state ps_state; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - struct sk_buff *msg; - void *hdr; - int err; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->set_power_mgmt) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_POWER_SAVE); - if (!hdr) { - err = -ENOBUFS; - goto free_msg; - } - - if (wdev->ps) - ps_state = NL80211_PS_ENABLED; - else - ps_state = NL80211_PS_DISABLED; - - NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static struct nla_policy -nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, -}; - -static int nl80211_set_cqm_rssi(struct genl_info *info, - s32 threshold, u32 hysteresis) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct wireless_dev *wdev; - struct net_device *dev = info->user_ptr[1]; - - if (threshold > 0) - return -EINVAL; - - wdev = dev->ieee80211_ptr; - - if (!rdev->ops->set_cqm_rssi_config) - return -EOPNOTSUPP; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) - return -EOPNOTSUPP; - - return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, - threshold, hysteresis); -} - -static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) -{ - struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; - struct nlattr *cqm; - int err; - - cqm = info->attrs[NL80211_ATTR_CQM]; - if (!cqm) { - err = -EINVAL; - goto out; - } - - err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, - nl80211_attr_cqm_policy); - if (err) - goto out; - - if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && - attrs[NL80211_ATTR_CQM_RSSI_HYST]) { - s32 threshold; - u32 hysteresis; - threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); - hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); - err = nl80211_set_cqm_rssi(info, threshold, hysteresis); - } else - err = -EINVAL; - -out: - return err; -} - -static int nl80211_join_mesh(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 mesh_config cfg; - struct mesh_setup setup; - int err; - - /* start with default */ - memcpy(&cfg, &default_mesh_config, sizeof(cfg)); - memcpy(&setup, &default_mesh_setup, sizeof(setup)); - - if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { - /* and parse parameters if given */ - err = nl80211_parse_mesh_config(info, &cfg, NULL); - if (err) - return err; - } - - if (!info->attrs[NL80211_ATTR_MESH_ID] || - !nla_len(info->attrs[NL80211_ATTR_MESH_ID])) - return -EINVAL; - - setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); - setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - - if (info->attrs[NL80211_ATTR_MESH_SETUP]) { - /* parse additional setup parameters if given */ - err = nl80211_parse_mesh_setup(info, &setup); - if (err) - return err; - } - - return cfg80211_join_mesh(rdev, dev, &setup, &cfg); -} - -static int nl80211_leave_mesh(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_mesh(rdev, dev); -} - -static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct sk_buff *msg; - void *hdr; - - if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_WOWLAN); - if (!hdr) - goto nla_put_failure; - - if (rdev->wowlan) { - struct nlattr *nl_wowlan; - - nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); - if (!nl_wowlan) - goto nla_put_failure; - - if (rdev->wowlan->any) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); - if (rdev->wowlan->disconnect) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); - if (rdev->wowlan->magic_pkt) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); - if (rdev->wowlan->gtk_rekey_failure) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); - if (rdev->wowlan->eap_identity_req) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); - if (rdev->wowlan->four_way_handshake) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); - if (rdev->wowlan->rfkill_release) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); - if (rdev->wowlan->n_patterns) { - struct nlattr *nl_pats, *nl_pat; - int i, pat_len; - - nl_pats = nla_nest_start(msg, - NL80211_WOWLAN_TRIG_PKT_PATTERN); - if (!nl_pats) - goto nla_put_failure; - - for (i = 0; i < rdev->wowlan->n_patterns; i++) { - nl_pat = nla_nest_start(msg, i + 1); - if (!nl_pat) - goto nla_put_failure; - pat_len = rdev->wowlan->patterns[i].pattern_len; - NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK, - DIV_ROUND_UP(pat_len, 8), - rdev->wowlan->patterns[i].mask); - NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN, - pat_len, - rdev->wowlan->patterns[i].pattern); - nla_nest_end(msg, nl_pat); - } - nla_nest_end(msg, nl_pats); - } - - nla_nest_end(msg, nl_wowlan); - } - - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} - -static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; - struct cfg80211_wowlan no_triggers = {}; - struct cfg80211_wowlan new_triggers = {}; - struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan; - int err, i; - - if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) - goto no_triggers; - - err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG, - nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]), - nl80211_wowlan_policy); - if (err) - return err; - - if (tb[NL80211_WOWLAN_TRIG_ANY]) { - if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) - return -EINVAL; - new_triggers.any = true; - } - - if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { - if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) - return -EINVAL; - new_triggers.disconnect = true; - } - - if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { - if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) - return -EINVAL; - new_triggers.magic_pkt = true; - } - - if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) - return -EINVAL; - - if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { - if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) - return -EINVAL; - new_triggers.gtk_rekey_failure = true; - } - - if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { - if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) - return -EINVAL; - new_triggers.eap_identity_req = true; - } - - if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { - if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) - return -EINVAL; - new_triggers.four_way_handshake = true; - } - - if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { - if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) - return -EINVAL; - new_triggers.rfkill_release = true; - } - - if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { - struct nlattr *pat; - int n_patterns = 0; - int rem, pat_len, mask_len; - struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT]; - - nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], - rem) - n_patterns++; - if (n_patterns > wowlan->n_patterns) - return -EINVAL; - - new_triggers.patterns = kcalloc(n_patterns, - sizeof(new_triggers.patterns[0]), - GFP_KERNEL); - if (!new_triggers.patterns) - return -ENOMEM; - - new_triggers.n_patterns = n_patterns; - i = 0; - - nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], - rem) { - nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT, - nla_data(pat), nla_len(pat), NULL); - err = -EINVAL; - if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] || - !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]) - goto error; - pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]); - mask_len = DIV_ROUND_UP(pat_len, 8); - if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) != - mask_len) - goto error; - if (pat_len > wowlan->pattern_max_len || - pat_len < wowlan->pattern_min_len) - goto error; - - new_triggers.patterns[i].mask = - kmalloc(mask_len + pat_len, GFP_KERNEL); - if (!new_triggers.patterns[i].mask) { - err = -ENOMEM; - goto error; - } - new_triggers.patterns[i].pattern = - new_triggers.patterns[i].mask + mask_len; - memcpy(new_triggers.patterns[i].mask, - nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]), - mask_len); - new_triggers.patterns[i].pattern_len = pat_len; - memcpy(new_triggers.patterns[i].pattern, - nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]), - pat_len); - i++; - } - } - - if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) { - struct cfg80211_wowlan *ntrig; - ntrig = kmemdup(&new_triggers, sizeof(new_triggers), - GFP_KERNEL); - if (!ntrig) { - err = -ENOMEM; - goto error; - } - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = ntrig; - } else { - no_triggers: - cfg80211_rdev_free_wowlan(rdev); - rdev->wowlan = NULL; - } - - return 0; - error: - for (i = 0; i < new_triggers.n_patterns; i++) - kfree(new_triggers.patterns[i].mask); - kfree(new_triggers.patterns); - return err; -} - -static int nl80211_set_rekey_data(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct nlattr *tb[NUM_NL80211_REKEY_DATA]; - struct cfg80211_gtk_rekey_data rekey_data; - int err; - - if (!info->attrs[NL80211_ATTR_REKEY_DATA]) - return -EINVAL; - - err = nla_parse(tb, MAX_NL80211_REKEY_DATA, - nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]), - nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]), - nl80211_rekey_policy); - if (err) - return err; - - if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN) - return -ERANGE; - if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN) - return -ERANGE; - if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN) - return -ERANGE; - - memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]), - NL80211_KEK_LEN); - memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]), - NL80211_KCK_LEN); - memcpy(rekey_data.replay_ctr, - nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]), - NL80211_REPLAY_CTR_LEN); - - wdev_lock(wdev); - if (!wdev->current_bss) { - err = -ENOTCONN; - goto out; - } - - if (!rdev->ops->set_rekey_data) { - err = -EOPNOTSUPP; - goto out; - } - - err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data); - out: - wdev_unlock(wdev); - return err; -} - -static int nl80211_register_unexpected_frame(struct sk_buff *skb, - struct genl_info *info) -{ - struct net_device *dev = info->user_ptr[1]; - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) - return -EINVAL; - - if (wdev->ap_unexpected_nlpid) - return -EBUSY; - - wdev->ap_unexpected_nlpid = info->snd_pid; - return 0; -} - -static int nl80211_probe_client(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 wireless_dev *wdev = dev->ieee80211_ptr; - struct sk_buff *msg; - void *hdr; - const u8 *addr; - u64 cookie; - int err; - - if (wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO) - return -EOPNOTSUPP; - - if (!info->attrs[NL80211_ATTR_MAC]) - return -EINVAL; - - if (!rdev->ops->probe_client) - return -EOPNOTSUPP; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_PROBE_CLIENT); - - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; - } - - addr = nla_data(info->attrs[NL80211_ATTR_MAC]); - - err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); - if (err) - goto free_msg; - - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - genlmsg_end(msg, hdr); - - return genlmsg_reply(msg, info); - - nla_put_failure: - err = -ENOBUFS; - free_msg: - nlmsg_free(msg); - return err; -} - -static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) -{ - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - - if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) - return -EOPNOTSUPP; - - if (rdev->ap_beacons_nlpid) - return -EBUSY; - - rdev->ap_beacons_nlpid = info->snd_pid; - - return 0; -} - -static int nl80211_btcoex_notify_inq(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - bool scan_status; - - if (!dev) - return -ENODEV; - - scan_status = !!info->attrs[NL80211_ATTR_BTCOEX_INQ_STATUS]; - - if (!dev->ops->notify_btcoex_inq_status) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_inq_status(&dev->wiphy, scan_status); -} - -static int nl80211_btcoex_notify_sco(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - int tx_interval = 0; - int tx_pkt_len = 0; - bool sco_status; - bool esco; - - if (!dev) - return -ENODEV; - - sco_status = !!info->attrs[NL80211_ATTR_BTCOEX_SCO_STATUS]; - esco = !!info->attrs[NL80211_ATTR_BTCOEX_TYPE_ESCO]; - - if (esco) { - if (info->attrs[NL80211_ATTR_BTCOEX_ESCO_TX_INTERVAL]) - tx_interval = nla_get_u32(info->attrs - [NL80211_ATTR_BTCOEX_ESCO_TX_INTERVAL]); - - if (info->attrs[NL80211_ATTR_BTCOEX_ESCO_TX_PKT_LEN]) - tx_pkt_len = nla_get_u32(info->attrs - [NL80211_ATTR_BTCOEX_ESCO_TX_PKT_LEN]); - } - if (!dev->ops->notify_btcoex_sco_status) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_sco_status(&dev->wiphy, sco_status, - esco, tx_interval, - tx_pkt_len); -} - -static int nl80211_btcoex_notify_a2dp(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - bool a2dp_status; - - if (!dev) - return -ENODEV; - - a2dp_status = !!info->attrs[NL80211_ATTR_BTCOEX_A2DP_STATUS]; - - if (!dev->ops->notify_btcoex_a2dp_status) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_a2dp_status(&dev->wiphy, a2dp_status); -} - -static int nl80211_btcoex_notify_acl_info(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - enum nl80211_btcoex_acl_role role = NL80211_BTCOEX_ACL_ROLE_UNKNOWN; - u32 remote_lmp_ver = 0; - - if (!dev) - return -ENODEV; - - if (info->attrs[NL80211_ATTR_BTCOEX_ACL_ROLE]) - role = nla_get_u32(info->attrs[NL80211_ATTR_BTCOEX_ACL_ROLE]); - - if (info->attrs[NL80211_ATTR_BTCOEX_REMOTE_LMP_VER]) - remote_lmp_ver = nla_get_u32(info->attrs - [NL80211_ATTR_BTCOEX_REMOTE_LMP_VER]); - - if (!dev->ops->notify_btcoex_acl_info) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_acl_info(&dev->wiphy, role, - remote_lmp_ver); -} -static int nl80211_btcoex_notify_antenna_config(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - enum nl80211_btcoex_antenna_config config = NL80211_BTCOEX_ANTENNA_DA; - - if (!dev) - return -ENODEV; - - if (!info->attrs[NL80211_ATTR_BTCOEX_ANTENNA_CONFIG]) - return -EINVAL; - - config = nla_get_u32(info->attrs[NL80211_ATTR_BTCOEX_ANTENNA_CONFIG]); - - if (!dev->ops->notify_btcoex_antenna_config) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_antenna_config(&dev->wiphy, config); -} - -static int nl80211_btcoex_notify(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - u8 *buf; - int len; - - if (!dev) - return -ENODEV; - - if (!info->attrs[NL80211_ATTR_BTCOEX_DATA]) - return -EINVAL; - - if (!dev->ops->notify_btcoex) - return -EOPNOTSUPP; - - buf = (char *)nla_data(info->attrs[NL80211_ATTR_BTCOEX_DATA]); - len = nla_len(info->attrs[NL80211_ATTR_BTCOEX_DATA]); - - return dev->ops->notify_btcoex(&dev->wiphy, buf, len); -} - -static int nl80211_p2p_flush_notify(struct sk_buff *skb, - struct genl_info *info) -{ - - struct cfg80211_registered_device *dev = info->user_ptr[0]; - printk("%s() enter steven\n", __func__); - - if (!dev) - return -ENODEV; - - if (!dev->ops->notify_p2p_flush) - return -EOPNOTSUPP; - - return dev->ops->notify_p2p_flush(&dev->wiphy); -} - -static int nl80211_btcoex_notify_bt_vendor(struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *dev = info->user_ptr[0]; - enum nl80211_btcoex_antenna_config config = - NL80211_BTCOEX_VENDOR_DEFAULT; - - if (!dev) - return -ENODEV; - - if (!info->attrs[NL80211_ATTR_BT_VENDOR_ID]) - return -EINVAL; - - config = nla_get_u32(info->attrs[NL80211_ATTR_BT_VENDOR_ID]); - - if (!dev->ops->notify_btcoex_bt_vendor) - return -EOPNOTSUPP; - - return dev->ops->notify_btcoex_bt_vendor(&dev->wiphy, config); -} -#define NL80211_FLAG_NEED_WIPHY 0x01 -#define NL80211_FLAG_NEED_NETDEV 0x02 -#define NL80211_FLAG_NEED_RTNL 0x04 -#define NL80211_FLAG_CHECK_NETDEV_UP 0x08 -#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ - NL80211_FLAG_CHECK_NETDEV_UP) - -static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info) -{ - struct cfg80211_registered_device *rdev; - struct net_device *dev; - int err; - bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL; - - if (rtnl) - rtnl_lock(); - - if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) { - rdev = cfg80211_get_dev_from_info(info); - if (IS_ERR(rdev)) { - if (rtnl) - rtnl_unlock(); - return PTR_ERR(rdev); - } - info->user_ptr[0] = rdev; - } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) { - err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); - if (err) { - if (rtnl) - rtnl_unlock(); - return err; - } - if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && - !netif_running(dev)) { - cfg80211_unlock_rdev(rdev); - dev_put(dev); - if (rtnl) - rtnl_unlock(); - return -ENETDOWN; - } - info->user_ptr[0] = rdev; - info->user_ptr[1] = dev; - } - - return 0; -} - -static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb, - struct genl_info *info) -{ - if (info->user_ptr[0]) - cfg80211_unlock_rdev(info->user_ptr[0]); - if (info->user_ptr[1]) - dev_put(info->user_ptr[1]); - if (ops->internal_flags & NL80211_FLAG_NEED_RTNL) - rtnl_unlock(); -} - -static struct genl_ops nl80211_ops[] = { - { - .cmd = NL80211_CMD_GET_WIPHY, - .doit = nl80211_get_wiphy, - .dumpit = nl80211_dump_wiphy, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_WIPHY, - }, - { - .cmd = NL80211_CMD_SET_WIPHY, - .doit = nl80211_set_wiphy, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_INTERFACE, - .doit = nl80211_get_interface, - .dumpit = nl80211_dump_interface, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV, - }, - { - .cmd = NL80211_CMD_SET_INTERFACE, - .doit = nl80211_set_interface, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_INTERFACE, - .doit = nl80211_new_interface, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_INTERFACE, - .doit = nl80211_del_interface, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_KEY, - .doit = nl80211_get_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_KEY, - .doit = nl80211_set_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_KEY, - .doit = nl80211_new_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_KEY, - .doit = nl80211_del_key, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_BEACON, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_BEACON, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .doit = nl80211_addset_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_BEACON, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .doit = nl80211_del_beacon, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_STATION, - .doit = nl80211_get_station, - .dumpit = nl80211_dump_station, - .policy = nl80211_policy, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_STATION, - .doit = nl80211_set_station, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_STATION, - .doit = nl80211_new_station, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_STATION, - .doit = nl80211_del_station, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_MPATH, - .doit = nl80211_get_mpath, - .dumpit = nl80211_dump_mpath, - .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, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_NEW_MPATH, - .doit = nl80211_new_mpath, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_MPATH, - .doit = nl80211_del_mpath, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_BSS, - .doit = nl80211_set_bss, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_REG, - .doit = nl80211_get_reg, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - }, - { - .cmd = NL80211_CMD_SET_REG, - .doit = nl80211_set_reg, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, - { - .cmd = NL80211_CMD_REQ_SET_REG, - .doit = nl80211_req_set_reg, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - }, - { - .cmd = NL80211_CMD_GET_MESH_CONFIG, - .doit = nl80211_get_mesh_config, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_MESH_CONFIG, - .doit = nl80211_update_mesh_config, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_TRIGGER_SCAN, - .doit = nl80211_trigger_scan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_SCAN, - .policy = nl80211_policy, - .dumpit = nl80211_dump_scan, - }, - { - .cmd = NL80211_CMD_START_SCHED_SCAN, - .doit = nl80211_start_sched_scan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_STOP_SCHED_SCAN, - .doit = nl80211_stop_sched_scan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_AUTHENTICATE, - .doit = nl80211_authenticate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_ASSOCIATE, - .doit = nl80211_associate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEAUTHENTICATE, - .doit = nl80211_deauthenticate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DISASSOCIATE, - .doit = nl80211_disassociate, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_JOIN_IBSS, - .doit = nl80211_join_ibss, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_LEAVE_IBSS, - .doit = nl80211_leave_ibss, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, -#ifdef CONFIG_NL80211_TESTMODE - { - .cmd = NL80211_CMD_TESTMODE, - .doit = nl80211_testmode_do, - .dumpit = nl80211_testmode_dump, - .policy = nl80211_policy, -#ifdef CONFIG_MACH_PX -#else - .flags = GENL_ADMIN_PERM, -#endif - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, -#endif - { - .cmd = NL80211_CMD_CONNECT, - .doit = nl80211_connect, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DISCONNECT, - .doit = nl80211_disconnect, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_WIPHY_NETNS, - .doit = nl80211_wiphy_netns, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_SURVEY, - .policy = nl80211_policy, - .dumpit = nl80211_dump_survey, - }, - { - .cmd = NL80211_CMD_SET_PMKSA, - .doit = nl80211_setdel_pmksa, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_DEL_PMKSA, - .doit = nl80211_setdel_pmksa, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_FLUSH_PMKSA, - .doit = nl80211_flush_pmksa, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, - .doit = nl80211_remain_on_channel, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - .doit = nl80211_cancel_remain_on_channel, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, - .doit = nl80211_set_tx_bitrate_mask, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_REGISTER_FRAME, - .doit = nl80211_register_mgmt, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_FRAME, - .doit = nl80211_tx_mgmt, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, - .doit = nl80211_tx_mgmt_cancel_wait, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_POWER_SAVE, - .doit = nl80211_set_power_save, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_POWER_SAVE, - .doit = nl80211_get_power_save, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_CQM, - .doit = nl80211_set_cqm, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_CHANNEL, - .doit = nl80211_set_channel, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_WDS_PEER, - .doit = nl80211_set_wds_peer, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_JOIN_MESH, - .doit = nl80211_join_mesh, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_LEAVE_MESH, - .doit = nl80211_leave_mesh, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_GET_WOWLAN, - .doit = nl80211_get_wowlan, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_WOWLAN, - .doit = nl80211_set_wowlan, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, - .doit = nl80211_set_rekey_data, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_TDLS_MGMT, - .doit = nl80211_tdls_mgmt, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_TDLS_OPER, - .doit = nl80211_tdls_oper, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_UNEXPECTED_FRAME, - .doit = nl80211_register_unexpected_frame, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_PROBE_CLIENT, - .doit = nl80211_probe_client, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_REGISTER_BEACONS, - .doit = nl80211_register_beacons, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_WIPHY | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_INQ, - .doit = nl80211_btcoex_notify_inq, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_SCO, - .doit = nl80211_btcoex_notify_sco, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_A2DP, - .doit = nl80211_btcoex_notify_a2dp, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_ACL_INFO, - .doit = nl80211_btcoex_notify_acl_info, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_ANTENNA_CONFIG, - .doit = nl80211_btcoex_notify_antenna_config, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX_BT_VENDOR, - .doit = nl80211_btcoex_notify_bt_vendor, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_BTCOEX, - .doit = nl80211_btcoex_notify, - .policy = nl80211_policy, - /* can be retrieved by unprivileged users */ - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_PRIV, - .doit = nl80211_priv_cmd, - .policy = nl80211_policy, - .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, - { - .cmd = NL80211_CMD_P2P_FLUSH, - .doit = nl80211_p2p_flush_notify, - .policy = nl80211_policy, - .internal_flags = NL80211_FLAG_NEED_NETDEV | - NL80211_FLAG_NEED_RTNL, - }, -}; - -static struct genl_multicast_group nl80211_mlme_mcgrp = { - .name = "mlme", -}; - -/* multicast groups */ -static struct genl_multicast_group nl80211_config_mcgrp = { - .name = "config", -}; -static struct genl_multicast_group nl80211_scan_mcgrp = { - .name = "scan", -}; -static struct genl_multicast_group nl80211_regulatory_mcgrp = { - .name = "regulatory", -}; - -/* notification functions */ - -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_config_mcgrp.id, GFP_KERNEL); -} - -static int nl80211_add_scan_req(struct sk_buff *msg, - struct cfg80211_registered_device *rdev) -{ - struct cfg80211_scan_request *req = rdev->scan_req; - struct nlattr *nest; - int i; - - ASSERT_RDEV_LOCK(rdev); - - if (WARN_ON(!req)) - return 0; - - nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); - if (!nest) - goto nla_put_failure; - for (i = 0; i < req->n_ssids; i++) - NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid); - nla_nest_end(msg, nest); - - nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); - if (!nest) - goto nla_put_failure; - for (i = 0; i < req->n_channels; i++) - NLA_PUT_U32(msg, i, req->channels[i]->center_freq); - nla_nest_end(msg, nest); - - if (req->ie) - NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie); - - return 0; - nla_put_failure: - return -ENOBUFS; -} - -static int nl80211_send_scan_msg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 pid, u32 seq, int flags, - u32 cmd) -{ - void *hdr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - /* ignore errors and send incomplete event anyway */ - nl80211_add_scan_req(msg, rdev); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -static int -nl80211_send_sched_scan_msg(struct sk_buff *msg, - struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u32 pid, u32 seq, int flags, u32 cmd) -{ - void *hdr; - - hdr = nl80211hdr_put(msg, pid, seq, flags, cmd); - if (!hdr) - return -1; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - return genlmsg_end(msg, hdr); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - return -EMSGSIZE; -} - -void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_TRIGGER_SCAN) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_NEW_SCAN_RESULTS) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_SCAN_ABORTED) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, - struct net_device *netdev) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, - NL80211_CMD_SCHED_SCAN_RESULTS) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) - return; - - if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_scan_mcgrp.id, GFP_KERNEL); -} - -/* - * This can happen on global regulatory changes or device specific settings - * based on custom world regulatory domains. - */ -void nl80211_send_reg_change_event(struct regulatory_request *request) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - /* Userspace can always count this one always being set */ - NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator); - - if (request->alpha2[0] == '0' && request->alpha2[1] == '0') - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_WORLD); - else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_CUSTOM_WORLD); - else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || - request->intersect) - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_INTERSECTION); - else { - NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE, - NL80211_REGDOM_TYPE_COUNTRY); - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2); - } - - if (wiphy_idx_valid(request->wiphy_idx)) - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx); - - genlmsg_end(msg, hdr); - - rcu_read_lock(); - genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, - GFP_ATOMIC); - rcu_read_unlock(); - - return; - -nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, - enum nl80211_commands cmd, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_AUTHENTICATE, gfp); -} - -void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_ASSOCIATE, gfp); -} - -void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DEAUTHENTICATE, gfp); -} - -void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_DISASSOCIATE, gfp); -} - -void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp); -} - -void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *buf, - size_t len, gfp_t gfp) -{ - nl80211_send_mlme_event(rdev, netdev, buf, len, - NL80211_CMD_UNPROT_DISASSOCIATE, gfp); -} - -static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int cmd, - const u8 *addr, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - gfp_t gfp) -{ - nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, - addr, gfp); -} - -void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - gfp_t gfp) -{ - nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, - addr, gfp); -} - -void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - if (bssid) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); - if (req_ie) - NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); - if (resp_ie) - NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} - -void nl80211_send_roamed(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - if (req_ie) - NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); - if (resp_ie) - NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} - -void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u16 reason, - const u8 *ie, size_t ie_len, bool from_ap) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - if (from_ap && reason) - NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); - if (from_ap) - NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); - if (ie) - NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, GFP_KERNEL); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} - -void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *macaddr, const u8* ie, u8 ie_len, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr); - if (ie_len && ie) - NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - enum nl80211_key_type key_type, int key_id, - const u8 *tsc, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - if (addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type); - if (key_id != -1) - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id); - if (tsc) - NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_beacon_hint_event(struct wiphy *wiphy, - struct ieee80211_channel *channel_before, - struct ieee80211_channel *channel_after) -{ - struct sk_buff *msg; - void *hdr; - struct nlattr *nl_freq; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - /* - * Since we are applying the beacon hint to a wiphy we know its - * wiphy_idx is valid - */ - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)); - - /* Before */ - nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE); - if (!nl_freq) - goto nla_put_failure; - if (nl80211_msg_put_channel(msg, channel_before)) - goto nla_put_failure; - nla_nest_end(msg, nl_freq); - - /* After */ - nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER); - if (!nl_freq) - goto nla_put_failure; - if (nl80211_msg_put_channel(msg, channel_after)) - goto nla_put_failure; - nla_nest_end(msg, nl_freq); - - genlmsg_end(msg, hdr); - - rcu_read_lock(); - genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, - GFP_ATOMIC); - rcu_read_unlock(); - - return; - -nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -static void nl80211_send_remain_on_chan_event( - int cmd, struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - - if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp) -{ - nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, - channel_type, duration, gfp); -} - -void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp) -{ - nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - rdev, netdev, cookie, chan, - channel_type, 0, gfp); -} - -void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - struct station_info *sinfo, gfp_t gfp) -{ - struct sk_buff *msg; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); -} - -void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, - const u8 *addr, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct sk_buff *msg; - void *hdr; - int err; - u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid); - - if (!nlpid) - return false; - - msg = nlmsg_new(100, gfp); - if (!msg) - return true; - - hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); - if (!hdr) { - nlmsg_free(msg); - return true; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return true; - } - - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - return true; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - return true; -} - -bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) -{ - return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, - addr, gfp); -} - -bool nl80211_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp) -{ - return __nl80211_unexpected_frame(dev, - NL80211_CMD_UNEXPECTED_4ADDR_FRAME, - addr, gfp); -} - -int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, - int freq, const u8 *buf, size_t len, gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return -ENOMEM; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); - if (!hdr) { - nlmsg_free(msg); - return -ENOMEM; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - - genlmsg_end(msg, hdr); - - return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - return -ENOBUFS; -} - -void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - const u8 *buf, size_t len, bool ack, - gfp_t gfp) -{ - struct sk_buff *msg; - void *hdr; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - if (ack) - NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); - - genlmsg_end(msg, hdr); - - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void -nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, - rssi_event); - - nla_nest_end(msg, pinfoattr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *replay_ctr, gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *rekey_attr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - - rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); - if (!rekey_attr) - goto nla_put_failure; - - NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, - NL80211_REPLAY_CTR_LEN, replay_ctr); - - nla_nest_end(msg, rekey_attr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int index, - const u8 *bssid, bool preauth, gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *attr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - - attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); - if (!attr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index); - NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid); - if (preauth) - NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH); - - nla_nest_end(msg, attr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void -nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *peer, - u32 num_packets, gfp_t gfp) -{ - struct sk_buff *msg; - struct nlattr *pinfoattr; - void *hdr; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); - - pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); - if (!pinfoattr) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets); - - nla_nest_end(msg, pinfoattr); - - genlmsg_end(msg, hdr); - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} - -void cfg80211_probe_status(struct net_device *dev, const u8 *addr, - u64 cookie, bool acked, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct sk_buff *msg; - void *hdr; - int err; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - if (acked) - NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); - - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} -EXPORT_SYMBOL(cfg80211_probe_status); - -void cfg80211_report_obss_beacon(struct wiphy *wiphy, - const u8 *frame, size_t len, - int freq, gfp_t gfp) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct sk_buff *msg; - void *hdr; - u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid); - - if (!nlpid) - return; - - msg = nlmsg_new(len + 100, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); - if (freq) - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame); - - genlmsg_end(msg, hdr); - - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); -} -EXPORT_SYMBOL(cfg80211_report_obss_beacon); - -void cfg80211_priv_event(struct net_device *dev, - const char *priv_event, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct sk_buff *msg; - void *hdr; - int err; - - msg = nlmsg_new(NLMSG_GOODSIZE, gfp); - if (!msg) - return; - - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PRIV_EVENT); - if (!hdr) { - nlmsg_free(msg); - return; - } - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); - NLA_PUT_STRING(msg, NL80211_ATTR_PRIV_EVENT, priv_event); - - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return; - } - - genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, - nl80211_mlme_mcgrp.id, gfp); - return; - - nla_put_failure: - genlmsg_cancel(msg, hdr); - nlmsg_free(msg); - -} -EXPORT_SYMBOL(cfg80211_priv_event); - - -static int nl80211_netlink_notify(struct notifier_block * nb, - unsigned long state, - void *_notify) -{ - struct netlink_notify *notify = _notify; - struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; - - if (state != NETLINK_URELEASE) - return NOTIFY_DONE; - - rcu_read_lock(); - - list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { - list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) - cfg80211_mlme_unregister_socket(wdev, notify->pid); - if (rdev->ap_beacons_nlpid == notify->pid) - rdev->ap_beacons_nlpid = 0; - } - - rcu_read_unlock(); - - return NOTIFY_DONE; -} - -static struct notifier_block nl80211_netlink_notifier = { - .notifier_call = nl80211_netlink_notify, -}; - -/* initialisation/exit functions */ - -int nl80211_init(void) -{ - int err; - - err = genl_register_family_with_ops(&nl80211_fam, - nl80211_ops, ARRAY_SIZE(nl80211_ops)); - if (err) - return err; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp); - if (err) - goto err_out; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp); - if (err) - goto err_out; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp); - if (err) - goto err_out; - - err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp); - if (err) - goto err_out; - -#ifdef CONFIG_NL80211_TESTMODE - err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp); - if (err) - goto err_out; -#endif - - err = netlink_register_notifier(&nl80211_netlink_notifier); - if (err) - goto err_out; - - return 0; - err_out: - genl_unregister_family(&nl80211_fam); - return err; -} - -void nl80211_exit(void) -{ - netlink_unregister_notifier(&nl80211_netlink_notifier); - genl_unregister_family(&nl80211_fam); -} diff --git a/net/wireless_ath/nl80211.h b/net/wireless_ath/nl80211.h deleted file mode 100755 index 12bf4d1..0000000 --- a/net/wireless_ath/nl80211.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef __NET_WIRELESS_NL80211_H -#define __NET_WIRELESS_NL80211_H - -#include "core.h" - -int nl80211_init(void); -void nl80211_exit(void); -void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev); -void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 cmd); -void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, - struct net_device *netdev); -void nl80211_send_reg_change_event(struct regulatory_request *request); -void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr, gfp_t gfp); -void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *addr, gfp_t gfp); -void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp); -void nl80211_send_roamed(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); -void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u16 reason, - const u8 *ie, size_t ie_len, bool from_ap); - -void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - const u8 *macaddr, const u8* ie, u8 ie_len, - gfp_t gfp); -void -nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *addr, - enum nl80211_key_type key_type, - int key_id, const u8 *tsc, gfp_t gfp); - -void -nl80211_send_beacon_hint_event(struct wiphy *wiphy, - struct ieee80211_channel *channel_before, - struct ieee80211_channel *channel_after); - -void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - gfp_t gfp); - -void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, - unsigned int duration, gfp_t gfp); -void nl80211_send_remain_on_channel_cancel( - struct cfg80211_registered_device *rdev, struct net_device *netdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp); - -void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - struct station_info *sinfo, gfp_t gfp); -void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, - struct net_device *dev, const u8 *mac_addr, - gfp_t gfp); - -int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u32 nlpid, int freq, - const u8 *buf, size_t len, gfp_t gfp); -void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, - struct net_device *netdev, u64 cookie, - const u8 *buf, size_t len, bool ack, - gfp_t gfp); - -void -nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, - enum nl80211_cqm_rssi_threshold_event rssi_event, - gfp_t gfp); -void -nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *peer, - u32 num_packets, gfp_t gfp); - -void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, const u8 *bssid, - const u8 *replay_ctr, gfp_t gfp); - -void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int index, - const u8 *bssid, bool preauth, gfp_t gfp); - -bool nl80211_unexpected_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp); -bool nl80211_unexpected_4addr_frame(struct net_device *dev, - const u8 *addr, gfp_t gfp); - -#endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless_ath/radiotap.c b/net/wireless_ath/radiotap.c deleted file mode 100755 index c4ad795..0000000 --- a/net/wireless_ath/radiotap.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Radiotap parser - * - * Copyright 2007 Andy Green <andy@warmcat.com> - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * - * 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. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See COPYING for more details. - */ - -#include <linux/kernel.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include <net/ieee80211_radiotap.h> -#include <asm/unaligned.h> - -/* function prototypes and related defs are in include/net/cfg80211.h */ - -static const struct radiotap_align_size rtap_namespace_sizes[] = { - [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, }, - [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, }, - [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, }, - [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, }, - [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, }, - /* - * add more here as they are defined in radiotap.h - */ -}; - -static const struct ieee80211_radiotap_namespace radiotap_ns = { - .n_bits = ARRAY_SIZE(rtap_namespace_sizes), - .align_size = rtap_namespace_sizes, -}; - -/** - * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization - * @iterator: radiotap_iterator to initialize - * @radiotap_header: radiotap header to parse - * @max_length: total length we can parse into (eg, whole packet length) - * - * Returns: 0 or a negative error code if there is a problem. - * - * This function initializes an opaque iterator struct which can then - * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap - * argument which is present in the header. It knows about extended - * present headers and handles them. - * - * How to use: - * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator - * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) - * checking for a good 0 return code. Then loop calling - * __ieee80211_radiotap_iterator_next()... it returns either 0, - * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. - * The iterator's @this_arg member points to the start of the argument - * associated with the current argument index that is present, which can be - * found in the iterator's @this_arg_index member. This arg index corresponds - * to the IEEE80211_RADIOTAP_... defines. - * - * Radiotap header length: - * You can find the CPU-endian total radiotap header length in - * iterator->max_length after executing ieee80211_radiotap_iterator_init() - * successfully. - * - * Alignment Gotcha: - * You must take care when dereferencing iterator.this_arg - * for multibyte types... the pointer is not aligned. Use - * get_unaligned((type *)iterator.this_arg) to dereference - * iterator.this_arg for type "type" safely on all arches. - * - * Example code: - * See Documentation/networking/radiotap-headers.txt - */ - -int ieee80211_radiotap_iterator_init( - struct ieee80211_radiotap_iterator *iterator, - struct ieee80211_radiotap_header *radiotap_header, - int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns) -{ - /* Linux only supports version 0 radiotap format */ - if (radiotap_header->it_version) - return -EINVAL; - - /* sanity check for allowed length and radiotap length field */ - if (max_length < get_unaligned_le16(&radiotap_header->it_len)) - return -EINVAL; - - iterator->_rtheader = radiotap_header; - iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len); - iterator->_arg_index = 0; - iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); - iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); - iterator->_reset_on_ext = 0; - iterator->_next_bitmap = &radiotap_header->it_present; - iterator->_next_bitmap++; - iterator->_vns = vns; - iterator->current_namespace = &radiotap_ns; - iterator->is_radiotap_ns = 1; - - /* find payload start allowing for extended bitmap(s) */ - - if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) { - while (get_unaligned_le32(iterator->_arg) & - (1 << IEEE80211_RADIOTAP_EXT)) { - iterator->_arg += sizeof(uint32_t); - - /* - * check for insanity where the present bitmaps - * keep claiming to extend up to or even beyond the - * stated radiotap header length - */ - - if ((unsigned long)iterator->_arg - - (unsigned long)iterator->_rtheader > - (unsigned long)iterator->_max_length) - return -EINVAL; - } - - iterator->_arg += sizeof(uint32_t); - - /* - * no need to check again for blowing past stated radiotap - * header length, because ieee80211_radiotap_iterator_next - * checks it before it is dereferenced - */ - } - - iterator->this_arg = iterator->_arg; - - /* we are all initialized happily */ - - return 0; -} -EXPORT_SYMBOL(ieee80211_radiotap_iterator_init); - -static void find_ns(struct ieee80211_radiotap_iterator *iterator, - uint32_t oui, uint8_t subns) -{ - int i; - - iterator->current_namespace = NULL; - - if (!iterator->_vns) - return; - - for (i = 0; i < iterator->_vns->n_ns; i++) { - if (iterator->_vns->ns[i].oui != oui) - continue; - if (iterator->_vns->ns[i].subns != subns) - continue; - - iterator->current_namespace = &iterator->_vns->ns[i]; - break; - } -} - - - -/** - * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg - * @iterator: radiotap_iterator to move to next arg (if any) - * - * Returns: 0 if there is an argument to handle, - * -ENOENT if there are no more args or -EINVAL - * if there is something else wrong. - * - * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) - * in @this_arg_index and sets @this_arg to point to the - * payload for the field. It takes care of alignment handling and extended - * present fields. @this_arg can be changed by the caller (eg, - * incremented to move inside a compound argument like - * IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in - * little-endian format whatever the endianess of your CPU. - * - * Alignment Gotcha: - * You must take care when dereferencing iterator.this_arg - * for multibyte types... the pointer is not aligned. Use - * get_unaligned((type *)iterator.this_arg) to dereference - * iterator.this_arg for type "type" safely on all arches. - */ - -int ieee80211_radiotap_iterator_next( - struct ieee80211_radiotap_iterator *iterator) -{ - while (1) { - int hit = 0; - int pad, align, size, subns; - uint32_t oui; - - /* if no more EXT bits, that's it */ - if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT && - !(iterator->_bitmap_shifter & 1)) - return -ENOENT; - - if (!(iterator->_bitmap_shifter & 1)) - goto next_entry; /* arg not present */ - - /* get alignment/size of data */ - switch (iterator->_arg_index % 32) { - case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: - case IEEE80211_RADIOTAP_EXT: - align = 1; - size = 0; - break; - case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: - align = 2; - size = 6; - break; - default: - if (!iterator->current_namespace || - iterator->_arg_index >= iterator->current_namespace->n_bits) { - if (iterator->current_namespace == &radiotap_ns) - return -ENOENT; - align = 0; - } else { - align = iterator->current_namespace->align_size[iterator->_arg_index].align; - size = iterator->current_namespace->align_size[iterator->_arg_index].size; - } - if (!align) { - /* skip all subsequent data */ - iterator->_arg = iterator->_next_ns_data; - /* give up on this namespace */ - iterator->current_namespace = NULL; - goto next_entry; - } - break; - } - - /* - * arg is present, account for alignment padding - * - * Note that these alignments are relative to the start - * of the radiotap header. There is no guarantee - * that the radiotap header itself is aligned on any - * kind of boundary. - * - * The above is why get_unaligned() is used to dereference - * multibyte elements from the radiotap area. - */ - - pad = ((unsigned long)iterator->_arg - - (unsigned long)iterator->_rtheader) & (align - 1); - - if (pad) - iterator->_arg += align - pad; - - if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) { - int vnslen; - - if ((unsigned long)iterator->_arg + size - - (unsigned long)iterator->_rtheader > - (unsigned long)iterator->_max_length) - return -EINVAL; - - oui = (*iterator->_arg << 16) | - (*(iterator->_arg + 1) << 8) | - *(iterator->_arg + 2); - subns = *(iterator->_arg + 3); - - find_ns(iterator, oui, subns); - - vnslen = get_unaligned_le16(iterator->_arg + 4); - iterator->_next_ns_data = iterator->_arg + size + vnslen; - if (!iterator->current_namespace) - size += vnslen; - } - - /* - * this is what we will return to user, but we need to - * move on first so next call has something fresh to test - */ - iterator->this_arg_index = iterator->_arg_index; - iterator->this_arg = iterator->_arg; - iterator->this_arg_size = size; - - /* internally move on the size of this arg */ - iterator->_arg += size; - - /* - * check for insanity where we are given a bitmap that - * claims to have more arg content than the length of the - * radiotap section. We will normally end up equalling this - * max_length on the last arg, never exceeding it. - */ - - if ((unsigned long)iterator->_arg - - (unsigned long)iterator->_rtheader > - (unsigned long)iterator->_max_length) - return -EINVAL; - - /* these special ones are valid in each bitmap word */ - switch (iterator->_arg_index % 32) { - case IEEE80211_RADIOTAP_VENDOR_NAMESPACE: - iterator->_reset_on_ext = 1; - - iterator->is_radiotap_ns = 0; - /* - * If parser didn't register this vendor - * namespace with us, allow it to show it - * as 'raw. Do do that, set argument index - * to vendor namespace. - */ - iterator->this_arg_index = - IEEE80211_RADIOTAP_VENDOR_NAMESPACE; - if (!iterator->current_namespace) - hit = 1; - goto next_entry; - case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE: - iterator->_reset_on_ext = 1; - iterator->current_namespace = &radiotap_ns; - iterator->is_radiotap_ns = 1; - goto next_entry; - case IEEE80211_RADIOTAP_EXT: - /* - * bit 31 was set, there is more - * -- move to next u32 bitmap - */ - iterator->_bitmap_shifter = - get_unaligned_le32(iterator->_next_bitmap); - iterator->_next_bitmap++; - if (iterator->_reset_on_ext) - iterator->_arg_index = 0; - else - iterator->_arg_index++; - iterator->_reset_on_ext = 0; - break; - default: - /* we've got a hit! */ - hit = 1; - next_entry: - iterator->_bitmap_shifter >>= 1; - iterator->_arg_index++; - } - - /* if we found a valid arg earlier, return it now */ - if (hit) - return 0; - } -} -EXPORT_SYMBOL(ieee80211_radiotap_iterator_next); diff --git a/net/wireless_ath/reg.c b/net/wireless_ath/reg.c deleted file mode 100755 index a797919..0000000 --- a/net/wireless_ath/reg.c +++ /dev/null @@ -1,2307 +0,0 @@ -/* - * Copyright 2002-2005, Instant802 Networks, Inc. - * Copyright 2005-2006, Devicescape Software, Inc. - * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> - * Copyright 2008 Luis R. Rodriguez <lrodriguz@atheros.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. - */ - -/** - * DOC: Wireless regulatory infrastructure - * - * The usual implementation is for a driver to read a device EEPROM to - * determine which regulatory domain it should be operating under, then - * looking up the allowable channels in a driver-local table and finally - * registering those channels in the wiphy structure. - * - * Another set of compliance enforcement is for drivers to use their - * own compliance limits which can be stored on the EEPROM. The host - * driver or firmware may ensure these are used. - * - * In addition to all this we provide an extra layer of regulatory - * conformance. For drivers which do not have any regulatory - * information CRDA provides the complete regulatory solution. - * For others it provides a community effort on further restrictions - * to enhance compliance. - * - * Note: When number of rules --> infinity we will not be able to - * index on alpha2 any more, instead we'll probably have to - * rely on some SHA1 checksum of the regdomain for example. - * - */ - -//#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/kernel.h> -#include <linux/export.h> -#include <linux/slab.h> -#include <linux/list.h> -#include <linux/random.h> -#include <linux/ctype.h> -#include <linux/nl80211.h> -#include <linux/platform_device.h> -#include <linux/moduleparam.h> -#include <net/cfg80211.h> -#include "core.h" -#include "reg.h" -#include "regdb.h" -#include "nl80211.h" - -#ifdef CONFIG_CFG80211_REG_DEBUG -#define REG_DBG_PRINT(format, args...) \ - printk(KERN_DEBUG pr_fmt(format), ##args) -#else -#define REG_DBG_PRINT(args...) -#endif - -/* Receipt of information from last regulatory request */ -static struct regulatory_request *last_request; - -/* To trigger userspace events */ -static struct platform_device *reg_pdev; - -static struct device_type reg_device_type = { - .uevent = reg_device_uevent, -}; - -/* - * Central wireless core regulatory domains, we only need two, - * the current one and a world regulatory domain in case we have no - * information to give us an alpha2 - */ -const struct ieee80211_regdomain *cfg80211_regdomain; - -/* - * Protects static reg.c components: - * - cfg80211_world_regdom - * - cfg80211_regdom - * - last_request - */ -static DEFINE_MUTEX(reg_mutex); - -static inline void assert_reg_lock(void) -{ - lockdep_assert_held(®_mutex); -} - -/* Used to queue up regulatory hints */ -static LIST_HEAD(reg_requests_list); -static spinlock_t reg_requests_lock; - -/* Used to queue up beacon hints for review */ -static LIST_HEAD(reg_pending_beacons); -static spinlock_t reg_pending_beacons_lock; - -/* Used to keep track of processed beacon hints */ -static LIST_HEAD(reg_beacon_list); - -struct reg_beacon { - struct list_head list; - struct ieee80211_channel chan; -}; - -static void reg_todo(struct work_struct *work); -static DECLARE_WORK(reg_work, reg_todo); - -static void reg_timeout_work(struct work_struct *work); -static DECLARE_DELAYED_WORK(reg_timeout, reg_timeout_work); - -/* We keep a static world regulatory domain in case of the absence of CRDA */ -static const struct ieee80211_regdomain world_regdom = { - .n_reg_rules = 5, - .alpha2 = "00", - .reg_rules = { - /* IEEE 802.11b/g, channels 1..11 */ - REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), - /* IEEE 802.11b/g, channels 12..13. No HT40 - * channel fits here. */ - REG_RULE(2467-10, 2472+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), - /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only */ - REG_RULE(2484-10, 2484+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS | - NL80211_RRF_NO_OFDM), - /* IEEE 802.11a, channel 36..48 */ - REG_RULE(5180-10, 5240+10, 40, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), - - /* NB: 5260 MHz - 5700 MHz requies DFS */ - - /* IEEE 802.11a, channel 149..165 */ - REG_RULE(5745-10, 5825+10, 40, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), - } -}; - -static const struct ieee80211_regdomain *cfg80211_world_regdom = - &world_regdom; - -static char *ieee80211_regdom = "00"; -static char user_alpha2[2]; - -module_param(ieee80211_regdom, charp, 0444); -MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); - -static void reset_regdomains(void) -{ - /* avoid freeing static information or freeing something twice */ - if (cfg80211_regdomain == cfg80211_world_regdom) - cfg80211_regdomain = NULL; - if (cfg80211_world_regdom == &world_regdom) - cfg80211_world_regdom = NULL; - if (cfg80211_regdomain == &world_regdom) - cfg80211_regdomain = NULL; - - kfree(cfg80211_regdomain); - kfree(cfg80211_world_regdom); - - cfg80211_world_regdom = &world_regdom; - cfg80211_regdomain = NULL; -} - -/* - * Dynamic world regulatory domain requested by the wireless - * core upon initialization - */ -static void update_world_regdomain(const struct ieee80211_regdomain *rd) -{ - BUG_ON(!last_request); - - reset_regdomains(); - - cfg80211_world_regdom = rd; - cfg80211_regdomain = rd; -} - -bool is_world_regdom(const char *alpha2) -{ - if (!alpha2) - return false; - if (alpha2[0] == '0' && alpha2[1] == '0') - return true; - return false; -} - -static bool is_alpha2_set(const char *alpha2) -{ - if (!alpha2) - return false; - if (alpha2[0] != 0 && alpha2[1] != 0) - return true; - return false; -} - -static bool is_unknown_alpha2(const char *alpha2) -{ - if (!alpha2) - return false; - /* - * Special case where regulatory domain was built by driver - * but a specific alpha2 cannot be determined - */ - if (alpha2[0] == '9' && alpha2[1] == '9') - return true; - return false; -} - -static bool is_intersected_alpha2(const char *alpha2) -{ - if (!alpha2) - return false; - /* - * Special case where regulatory domain is the - * result of an intersection between two regulatory domain - * structures - */ - if (alpha2[0] == '9' && alpha2[1] == '8') - return true; - return false; -} - -static bool is_an_alpha2(const char *alpha2) -{ - if (!alpha2) - return false; - if (isalpha(alpha2[0]) && isalpha(alpha2[1])) - return true; - return false; -} - -static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) -{ - if (!alpha2_x || !alpha2_y) - return false; - if (alpha2_x[0] == alpha2_y[0] && - alpha2_x[1] == alpha2_y[1]) - return true; - return false; -} - -static bool regdom_changes(const char *alpha2) -{ - assert_cfg80211_lock(); - - if (!cfg80211_regdomain) - return true; - if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) - return false; - return true; -} - -/* - * The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets - * you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER - * has ever been issued. - */ -static bool is_user_regdom_saved(void) -{ - if (user_alpha2[0] == '9' && user_alpha2[1] == '7') - return false; - - /* This would indicate a mistake on the design */ - if (WARN((!is_world_regdom(user_alpha2) && - !is_an_alpha2(user_alpha2)), - "Unexpected user alpha2: %c%c\n", - user_alpha2[0], - user_alpha2[1])) - return false; - - return true; -} - -static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, - const struct ieee80211_regdomain *src_regd) -{ - struct ieee80211_regdomain *regd; - int size_of_regd = 0; - unsigned int i; - - size_of_regd = sizeof(struct ieee80211_regdomain) + - ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); - - regd = kzalloc(size_of_regd, GFP_KERNEL); - if (!regd) - return -ENOMEM; - - memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); - - for (i = 0; i < src_regd->n_reg_rules; i++) - memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], - sizeof(struct ieee80211_reg_rule)); - - *dst_regd = regd; - return 0; -} - -#ifdef CONFIG_CFG80211_INTERNAL_REGDB -struct reg_regdb_search_request { - char alpha2[2]; - struct list_head list; -}; - -static LIST_HEAD(reg_regdb_search_list); -static DEFINE_MUTEX(reg_regdb_search_mutex); - -static void reg_regdb_search(struct work_struct *work) -{ - struct reg_regdb_search_request *request; - const struct ieee80211_regdomain *curdom, *regdom; - int i, r; - - mutex_lock(®_regdb_search_mutex); - while (!list_empty(®_regdb_search_list)) { - request = list_first_entry(®_regdb_search_list, - struct reg_regdb_search_request, - list); - list_del(&request->list); - - for (i=0; i<reg_regdb_size; i++) { - curdom = reg_regdb[i]; - - if (!memcmp(request->alpha2, curdom->alpha2, 2)) { - r = reg_copy_regd(®dom, curdom); - if (r) - break; - mutex_lock(&cfg80211_mutex); - set_regdom(regdom); - mutex_unlock(&cfg80211_mutex); - break; - } - } - - kfree(request); - } - mutex_unlock(®_regdb_search_mutex); -} - -static DECLARE_WORK(reg_regdb_work, reg_regdb_search); - -static void reg_regdb_query(const char *alpha2) -{ - struct reg_regdb_search_request *request; - - if (!alpha2) - return; - - request = kzalloc(sizeof(struct reg_regdb_search_request), GFP_KERNEL); - if (!request) - return; - - memcpy(request->alpha2, alpha2, 2); - - mutex_lock(®_regdb_search_mutex); - list_add_tail(&request->list, ®_regdb_search_list); - mutex_unlock(®_regdb_search_mutex); - - schedule_work(®_regdb_work); -} -#else -static inline void reg_regdb_query(const char *alpha2) {} -#endif /* CONFIG_CFG80211_INTERNAL_REGDB */ - -/* - * This lets us keep regulatory code which is updated on a regulatory - * basis in userspace. Country information is filled in by - * reg_device_uevent - */ -static int call_crda(const char *alpha2) -{ - if (!is_world_regdom((char *) alpha2)) - pr_info("Calling CRDA for country: %c%c\n", - alpha2[0], alpha2[1]); - else - pr_info("Calling CRDA to update world regulatory domain\n"); - - /* query internal regulatory database (if it exists) */ - reg_regdb_query(alpha2); - - return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); -} - -/* Used by nl80211 before kmalloc'ing our regulatory domain */ -bool reg_is_valid_request(const char *alpha2) -{ - assert_cfg80211_lock(); - - if (!last_request) - return false; - - return alpha2_equal(last_request->alpha2, alpha2); -} - -/* Sanity check on a regulatory rule */ -static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) -{ - const struct ieee80211_freq_range *freq_range = &rule->freq_range; - u32 freq_diff; - - if (freq_range->start_freq_khz <= 0 || freq_range->end_freq_khz <= 0) - return false; - - if (freq_range->start_freq_khz > freq_range->end_freq_khz) - return false; - - freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; - - if (freq_range->end_freq_khz <= freq_range->start_freq_khz || - freq_range->max_bandwidth_khz > freq_diff) - return false; - - return true; -} - -static bool is_valid_rd(const struct ieee80211_regdomain *rd) -{ - const struct ieee80211_reg_rule *reg_rule = NULL; - unsigned int i; - - if (!rd->n_reg_rules) - return false; - - if (WARN_ON(rd->n_reg_rules > NL80211_MAX_SUPP_REG_RULES)) - return false; - - for (i = 0; i < rd->n_reg_rules; i++) { - reg_rule = &rd->reg_rules[i]; - if (!is_valid_reg_rule(reg_rule)) - return false; - } - - return true; -} - -static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, - u32 center_freq_khz, - u32 bw_khz) -{ - u32 start_freq_khz, end_freq_khz; - - start_freq_khz = center_freq_khz - (bw_khz/2); - end_freq_khz = center_freq_khz + (bw_khz/2); - - if (start_freq_khz >= freq_range->start_freq_khz && - end_freq_khz <= freq_range->end_freq_khz) - return true; - - return false; -} - -/** - * freq_in_rule_band - tells us if a frequency is in a frequency band - * @freq_range: frequency rule we want to query - * @freq_khz: frequency we are inquiring about - * - * This lets us know if a specific frequency rule is or is not relevant to - * a specific frequency's band. Bands are device specific and artificial - * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is - * safe for now to assume that a frequency rule should not be part of a - * frequency's band if the start freq or end freq are off by more than 2 GHz. - * This resolution can be lowered and should be considered as we add - * regulatory rule support for other "bands". - **/ -static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, - u32 freq_khz) -{ -#define ONE_GHZ_IN_KHZ 1000000 - if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) - return true; - if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ)) - return true; - return false; -#undef ONE_GHZ_IN_KHZ -} - -/* - * Helper for regdom_intersect(), this does the real - * mathematical intersection fun - */ -static int reg_rules_intersect( - const struct ieee80211_reg_rule *rule1, - const struct ieee80211_reg_rule *rule2, - struct ieee80211_reg_rule *intersected_rule) -{ - const struct ieee80211_freq_range *freq_range1, *freq_range2; - struct ieee80211_freq_range *freq_range; - const struct ieee80211_power_rule *power_rule1, *power_rule2; - struct ieee80211_power_rule *power_rule; - u32 freq_diff; - - freq_range1 = &rule1->freq_range; - freq_range2 = &rule2->freq_range; - freq_range = &intersected_rule->freq_range; - - power_rule1 = &rule1->power_rule; - power_rule2 = &rule2->power_rule; - power_rule = &intersected_rule->power_rule; - - freq_range->start_freq_khz = max(freq_range1->start_freq_khz, - freq_range2->start_freq_khz); - freq_range->end_freq_khz = min(freq_range1->end_freq_khz, - freq_range2->end_freq_khz); - freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, - freq_range2->max_bandwidth_khz); - - freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; - if (freq_range->max_bandwidth_khz > freq_diff) - freq_range->max_bandwidth_khz = freq_diff; - - power_rule->max_eirp = min(power_rule1->max_eirp, - power_rule2->max_eirp); - power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, - power_rule2->max_antenna_gain); - - intersected_rule->flags = (rule1->flags | rule2->flags); - - if (!is_valid_reg_rule(intersected_rule)) - return -EINVAL; - - return 0; -} - -/** - * regdom_intersect - do the intersection between two regulatory domains - * @rd1: first regulatory domain - * @rd2: second regulatory domain - * - * Use this function to get the intersection between two regulatory domains. - * Once completed we will mark the alpha2 for the rd as intersected, "98", - * as no one single alpha2 can represent this regulatory domain. - * - * Returns a pointer to the regulatory domain structure which will hold the - * resulting intersection of rules between rd1 and rd2. We will - * kzalloc() this structure for you. - */ -static struct ieee80211_regdomain *regdom_intersect( - const struct ieee80211_regdomain *rd1, - const struct ieee80211_regdomain *rd2) -{ - int r, size_of_regd; - unsigned int x, y; - unsigned int num_rules = 0, rule_idx = 0; - const struct ieee80211_reg_rule *rule1, *rule2; - struct ieee80211_reg_rule *intersected_rule; - struct ieee80211_regdomain *rd; - /* This is just a dummy holder to help us count */ - struct ieee80211_reg_rule irule; - - /* Uses the stack temporarily for counter arithmetic */ - intersected_rule = &irule; - - memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); - - if (!rd1 || !rd2) - return NULL; - - /* - * First we get a count of the rules we'll need, then we actually - * build them. This is to so we can malloc() and free() a - * regdomain once. The reason we use reg_rules_intersect() here - * is it will return -EINVAL if the rule computed makes no sense. - * All rules that do check out OK are valid. - */ - - for (x = 0; x < rd1->n_reg_rules; x++) { - rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules; y++) { - rule2 = &rd2->reg_rules[y]; - if (!reg_rules_intersect(rule1, rule2, - intersected_rule)) - num_rules++; - memset(intersected_rule, 0, - sizeof(struct ieee80211_reg_rule)); - } - } - - if (!num_rules) - return NULL; - - size_of_regd = sizeof(struct ieee80211_regdomain) + - ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); - - rd = kzalloc(size_of_regd, GFP_KERNEL); - if (!rd) - return NULL; - - for (x = 0; x < rd1->n_reg_rules; x++) { - rule1 = &rd1->reg_rules[x]; - for (y = 0; y < rd2->n_reg_rules; y++) { - rule2 = &rd2->reg_rules[y]; - /* - * This time around instead of using the stack lets - * write to the target rule directly saving ourselves - * a memcpy() - */ - intersected_rule = &rd->reg_rules[rule_idx]; - r = reg_rules_intersect(rule1, rule2, - intersected_rule); - /* - * No need to memset here the intersected rule here as - * we're not using the stack anymore - */ - if (r) - continue; - rule_idx++; - } - } - - if (rule_idx != num_rules) { - kfree(rd); - return NULL; - } - - rd->n_reg_rules = num_rules; - rd->alpha2[0] = '9'; - rd->alpha2[1] = '8'; - - return rd; -} - -/* - * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may - * want to just have the channel structure use these - */ -static u32 map_regdom_flags(u32 rd_flags) -{ - u32 channel_flags = 0; - if (rd_flags & NL80211_RRF_PASSIVE_SCAN) - channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN; - if (rd_flags & NL80211_RRF_NO_IBSS) - channel_flags |= IEEE80211_CHAN_NO_IBSS; - if (rd_flags & NL80211_RRF_DFS) - channel_flags |= IEEE80211_CHAN_RADAR; - return channel_flags; -} - -static int freq_reg_info_regd(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, - const struct ieee80211_reg_rule **reg_rule, - const struct ieee80211_regdomain *custom_regd) -{ - int i; - bool band_rule_found = false; - const struct ieee80211_regdomain *regd; - bool bw_fits = false; - - if (!desired_bw_khz) - desired_bw_khz = MHZ_TO_KHZ(20); - - regd = custom_regd ? custom_regd : cfg80211_regdomain; - - /* - * Follow the driver's regulatory domain, if present, unless a country - * IE has been processed or a user wants to help complaince further - */ - if (!custom_regd && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - last_request->initiator != NL80211_REGDOM_SET_BY_USER && - wiphy->regd) - regd = wiphy->regd; - - if (!regd) - return -EINVAL; - - for (i = 0; i < regd->n_reg_rules; i++) { - const struct ieee80211_reg_rule *rr; - const struct ieee80211_freq_range *fr = NULL; - - rr = ®d->reg_rules[i]; - fr = &rr->freq_range; - - /* - * We only need to know if one frequency rule was - * was in center_freq's band, that's enough, so lets - * not overwrite it once found - */ - if (!band_rule_found) - band_rule_found = freq_in_rule_band(fr, center_freq); - - bw_fits = reg_does_bw_fit(fr, - center_freq, - desired_bw_khz); - - if (band_rule_found && bw_fits) { - *reg_rule = rr; - return 0; - } - } - - if (!band_rule_found) - return -ERANGE; - - return -EINVAL; -} - -int freq_reg_info(struct wiphy *wiphy, - u32 center_freq, - u32 desired_bw_khz, - const struct ieee80211_reg_rule **reg_rule) -{ - assert_cfg80211_lock(); - return freq_reg_info_regd(wiphy, - center_freq, - desired_bw_khz, - reg_rule, - NULL); -} -EXPORT_SYMBOL(freq_reg_info); - -#ifdef CONFIG_CFG80211_REG_DEBUG -static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) -{ - switch (initiator) { - case NL80211_REGDOM_SET_BY_CORE: - return "Set by core"; - case NL80211_REGDOM_SET_BY_USER: - return "Set by user"; - case NL80211_REGDOM_SET_BY_DRIVER: - return "Set by driver"; - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - return "Set by country IE"; - default: - WARN_ON(1); - return "Set by bug"; - } -} - -static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, - const struct ieee80211_reg_rule *reg_rule) -{ - const struct ieee80211_power_rule *power_rule; - const struct ieee80211_freq_range *freq_range; - char max_antenna_gain[32]; - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (!power_rule->max_antenna_gain) - snprintf(max_antenna_gain, 32, "N/A"); - else - snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); - - REG_DBG_PRINT("Updating information on frequency %d MHz " - "for a %d MHz width channel with regulatory rule:\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); - - REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - max_antenna_gain, - power_rule->max_eirp); -} -#else -static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, - u32 desired_bw_khz, - const struct ieee80211_reg_rule *reg_rule) -{ - return; -} -#endif - -/* - * Note that right now we assume the desired channel bandwidth - * is always 20 MHz for each individual channel (HT40 uses 20 MHz - * per channel, the primary and the extension channel). To support - * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a - * new ieee80211_channel.target_bw and re run the regulatory check - * on the wiphy with the target_bw specified. Then we can simply use - * that below for the desired_bw_khz below. - */ -static void handle_channel(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator, - enum ieee80211_band band, - unsigned int chan_idx) -{ - int r; - u32 flags, bw_flags = 0; - u32 desired_bw_khz = MHZ_TO_KHZ(20); - const struct ieee80211_reg_rule *reg_rule = NULL; - const struct ieee80211_power_rule *power_rule = NULL; - const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - struct wiphy *request_wiphy = NULL; - - assert_cfg80211_lock(); - - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - - flags = chan->orig_flags; - - r = freq_reg_info(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule); - - if (r) { - /* - * We will disable all channels that do not match our - * received regulatory rule unless the hint is coming - * from a Country IE and the Country IE had no information - * about a band. The IEEE 802.11 spec allows for an AP - * to send only a subset of the regulatory rules allowed, - * so an AP in the US that only supports 2.4 GHz may only send - * a country IE with information for the 2.4 GHz band - * while 5 GHz is still supported. - */ - if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && - r == -ERANGE) - return; - - REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); - chan->flags = IEEE80211_CHAN_DISABLED; - return; - } - - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags = IEEE80211_CHAN_NO_HT40; - - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && - request_wiphy && request_wiphy == wiphy && - request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - /* - * This guarantees the driver's requested regulatory domain - * will always be used as a base for further regulatory - * settings - */ - chan->flags = chan->orig_flags = - map_regdom_flags(reg_rule->flags) | bw_flags; - chan->max_antenna_gain = chan->orig_mag = - (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_power = chan->orig_mpwr = - (int) MBM_TO_DBM(power_rule->max_eirp); - return; - } - - chan->beacon_found = false; - chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); - chan->max_antenna_gain = min(chan->orig_mag, - (int) MBI_TO_DBI(power_rule->max_antenna_gain)); - if (chan->orig_mpwr) - chan->max_power = min(chan->orig_mpwr, - (int) MBM_TO_DBM(power_rule->max_eirp)); - else - chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); -} - -static void handle_band(struct wiphy *wiphy, - enum ieee80211_band band, - enum nl80211_reg_initiator initiator) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels; i++) - handle_channel(wiphy, initiator, band, i); -} - -static bool ignore_reg_update(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator) -{ - if (!last_request) { - REG_DBG_PRINT("Ignoring regulatory request %s since " - "last_request is not set\n", - reg_initiator_name(initiator)); - return true; - } - - if (initiator == NL80211_REGDOM_SET_BY_CORE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver uses its own custom " - "regulatory domain\n", - reg_initiator_name(initiator)); - return true; - } - - /* - * wiphy->regd will be set once the device has its own - * desired regulatory domain set - */ - if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && - initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - !is_world_regdom(last_request->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s " - "since the driver requires its own regulatory " - "domain to be set first\n", - reg_initiator_name(initiator)); - return true; - } - - return false; -} - -static void handle_reg_beacon(struct wiphy *wiphy, - unsigned int chan_idx, - struct reg_beacon *reg_beacon) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - bool channel_changed = false; - struct ieee80211_channel chan_before; - - assert_cfg80211_lock(); - - sband = wiphy->bands[reg_beacon->chan.band]; - chan = &sband->channels[chan_idx]; - - if (likely(chan->center_freq != reg_beacon->chan.center_freq)) - return; - - if (chan->beacon_found) - return; - - chan->beacon_found = true; - - if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) - return; - - chan_before.center_freq = chan->center_freq; - chan_before.flags = chan->flags; - - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) { - chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; - channel_changed = true; - } - - if (chan->flags & IEEE80211_CHAN_NO_IBSS) { - chan->flags &= ~IEEE80211_CHAN_NO_IBSS; - channel_changed = true; - } - - if (channel_changed) - nl80211_send_beacon_hint_event(wiphy, &chan_before, chan); -} - -/* - * Called when a scan on a wiphy finds a beacon on - * new channel - */ -static void wiphy_update_new_beacon(struct wiphy *wiphy, - struct reg_beacon *reg_beacon) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - assert_cfg80211_lock(); - - if (!wiphy->bands[reg_beacon->chan.band]) - return; - - sband = wiphy->bands[reg_beacon->chan.band]; - - for (i = 0; i < sband->n_channels; i++) - handle_reg_beacon(wiphy, i, reg_beacon); -} - -/* - * Called upon reg changes or a new wiphy is added - */ -static void wiphy_update_beacon_reg(struct wiphy *wiphy) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - struct reg_beacon *reg_beacon; - - assert_cfg80211_lock(); - - if (list_empty(®_beacon_list)) - return; - - list_for_each_entry(reg_beacon, ®_beacon_list, list) { - if (!wiphy->bands[reg_beacon->chan.band]) - continue; - sband = wiphy->bands[reg_beacon->chan.band]; - for (i = 0; i < sband->n_channels; i++) - handle_reg_beacon(wiphy, i, reg_beacon); - } -} - -static bool reg_is_world_roaming(struct wiphy *wiphy) -{ - if (is_world_regdom(cfg80211_regdomain->alpha2) || - (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) - return true; - if (last_request && - last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) - return true; - return false; -} - -/* Reap the advantages of previously found beacons */ -static void reg_process_beacons(struct wiphy *wiphy) -{ - /* - * Means we are just firing up cfg80211, so no beacons would - * have been processed yet. - */ - if (!last_request) - return; - if (!reg_is_world_roaming(wiphy)) - return; - wiphy_update_beacon_reg(wiphy); -} - -static bool is_ht40_not_allowed(struct ieee80211_channel *chan) -{ - if (!chan) - return true; - if (chan->flags & IEEE80211_CHAN_DISABLED) - return true; - /* This would happen when regulatory rules disallow HT40 completely */ - if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) - return true; - return false; -} - -static void reg_process_ht_flags_channel(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx) -{ - struct ieee80211_supported_band *sband; - struct ieee80211_channel *channel; - struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; - unsigned int i; - - assert_cfg80211_lock(); - - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - channel = &sband->channels[chan_idx]; - - if (is_ht40_not_allowed(channel)) { - channel->flags |= IEEE80211_CHAN_NO_HT40; - return; - } - - /* - * We need to ensure the extension channels exist to - * be able to use HT40- or HT40+, this finds them (or not) - */ - for (i = 0; i < sband->n_channels; i++) { - struct ieee80211_channel *c = &sband->channels[i]; - if (c->center_freq == (channel->center_freq - 20)) - channel_before = c; - if (c->center_freq == (channel->center_freq + 20)) - channel_after = c; - } - - /* - * Please note that this assumes target bandwidth is 20 MHz, - * if that ever changes we also need to change the below logic - * to include that as well. - */ - if (is_ht40_not_allowed(channel_before)) - channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; - else - channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - - if (is_ht40_not_allowed(channel_after)) - channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; - else - channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; -} - -static void reg_process_ht_flags_band(struct wiphy *wiphy, - enum ieee80211_band band) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels; i++) - reg_process_ht_flags_channel(wiphy, band, i); -} - -static void reg_process_ht_flags(struct wiphy *wiphy) -{ - enum ieee80211_band band; - - if (!wiphy) - return; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - reg_process_ht_flags_band(wiphy, band); - } - -} - -static void wiphy_update_regulatory(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator) -{ - enum ieee80211_band band; - - assert_reg_lock(); - - if (ignore_reg_update(wiphy, initiator)) - return; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - handle_band(wiphy, band, initiator); - } - -#ifdef CONFIG_MACH_PX - last_request->processed = true; -#endif - - reg_process_beacons(wiphy); - reg_process_ht_flags(wiphy); - if (wiphy->reg_notifier) - wiphy->reg_notifier(wiphy, last_request); -} - -void regulatory_update(struct wiphy *wiphy, - enum nl80211_reg_initiator setby) -{ - mutex_lock(®_mutex); - wiphy_update_regulatory(wiphy, setby); - mutex_unlock(®_mutex); -} - -static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) -{ - struct cfg80211_registered_device *rdev; - - list_for_each_entry(rdev, &cfg80211_rdev_list, list) - wiphy_update_regulatory(&rdev->wiphy, initiator); -} - -static void handle_channel_custom(struct wiphy *wiphy, - enum ieee80211_band band, - unsigned int chan_idx, - const struct ieee80211_regdomain *regd) -{ - int r; - u32 desired_bw_khz = MHZ_TO_KHZ(20); - u32 bw_flags = 0; - const struct ieee80211_reg_rule *reg_rule = NULL; - const struct ieee80211_power_rule *power_rule = NULL; - const struct ieee80211_freq_range *freq_range = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *chan; - - assert_reg_lock(); - - sband = wiphy->bands[band]; - BUG_ON(chan_idx >= sband->n_channels); - chan = &sband->channels[chan_idx]; - - r = freq_reg_info_regd(wiphy, - MHZ_TO_KHZ(chan->center_freq), - desired_bw_khz, - ®_rule, - regd); - - if (r) { - REG_DBG_PRINT("Disabling freq %d MHz as custom " - "regd has no rule that fits a %d MHz " - "wide channel\n", - chan->center_freq, - KHZ_TO_MHZ(desired_bw_khz)); - chan->flags = IEEE80211_CHAN_DISABLED; - return; - } - - chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); - - power_rule = ®_rule->power_rule; - freq_range = ®_rule->freq_range; - - if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags = IEEE80211_CHAN_NO_HT40; - - chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; - chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); - chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); -} - -static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, - const struct ieee80211_regdomain *regd) -{ - unsigned int i; - struct ieee80211_supported_band *sband; - - BUG_ON(!wiphy->bands[band]); - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels; i++) - handle_channel_custom(wiphy, band, i, regd); -} - -/* Used by drivers prior to wiphy registration */ -void wiphy_apply_custom_regulatory(struct wiphy *wiphy, - const struct ieee80211_regdomain *regd) -{ - enum ieee80211_band band; - unsigned int bands_set = 0; - - mutex_lock(®_mutex); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wiphy->bands[band]) - continue; - handle_band_custom(wiphy, band, regd); - bands_set++; - } - mutex_unlock(®_mutex); - - /* - * no point in calling this if it won't have any effect - * on your device's supportd bands. - */ - WARN_ON(!bands_set); -} -EXPORT_SYMBOL(wiphy_apply_custom_regulatory); - -/* - * Return value which can be used by ignore_request() to indicate - * it has been determined we should intersect two regulatory domains - */ -#define REG_INTERSECT 1 - -/* This has the logic which determines when a new request - * should be ignored. */ -static int ignore_request(struct wiphy *wiphy, - struct regulatory_request *pending_request) -{ - struct wiphy *last_wiphy = NULL; - - assert_cfg80211_lock(); - - /* All initial requests are respected */ - if (!last_request) - return 0; - - switch (pending_request->initiator) { - case NL80211_REGDOM_SET_BY_CORE: - return 0; - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - - last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - if (unlikely(!is_an_alpha2(pending_request->alpha2))) - return -EINVAL; - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { - if (last_wiphy != wiphy) { - /* - * Two cards with two APs claiming different - * Country IE alpha2s. We could - * intersect them, but that seems unlikely - * to be correct. Reject second one for now. - */ - if (regdom_changes(pending_request->alpha2)) - return -EOPNOTSUPP; - return -EALREADY; - } - /* - * Two consecutive Country IE hints on the same wiphy. - * This should be picked up early by the driver/stack - */ - if (WARN_ON(regdom_changes(pending_request->alpha2))) - return 0; - return -EALREADY; - } - return 0; - case NL80211_REGDOM_SET_BY_DRIVER: - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { - if (regdom_changes(pending_request->alpha2)) - return 0; - return -EALREADY; - } - - /* - * This would happen if you unplug and plug your card - * back in or if you add a new device for which the previously - * loaded card also agrees on the regulatory domain. - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && - !regdom_changes(pending_request->alpha2)) - return -EALREADY; - - return REG_INTERSECT; - case NL80211_REGDOM_SET_BY_USER: - if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - return REG_INTERSECT; - /* - * If the user knows better the user should set the regdom - * to their country before the IE is picked up - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && - last_request->intersect) - return -EOPNOTSUPP; - /* - * Process user requests only after previous user/driver/core - * requests have been processed - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || - last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || - last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - if (regdom_changes(last_request->alpha2)) - return -EAGAIN; - } - - if (!regdom_changes(pending_request->alpha2)) - return -EALREADY; - - return 0; - } - - return -EINVAL; -} - -static void reg_set_request_processed(void) -{ - bool need_more_processing = false; - - last_request->processed = true; - - spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) - need_more_processing = true; - spin_unlock(®_requests_lock); - - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) - cancel_delayed_work_sync(®_timeout); - - if (need_more_processing) - schedule_work(®_work); -} - -/** - * __regulatory_hint - hint to the wireless core a regulatory domain - * @wiphy: if the hint comes from country information from an AP, this - * is required to be set to the wiphy that received the information - * @pending_request: the regulatory request currently being processed - * - * The Wireless subsystem can use this function to hint to the wireless core - * what it believes should be the current regulatory domain. - * - * Returns zero if all went fine, %-EALREADY if a regulatory domain had - * already been set or other standard error codes. - * - * Caller must hold &cfg80211_mutex and ®_mutex - */ -static int __regulatory_hint(struct wiphy *wiphy, - struct regulatory_request *pending_request) -{ - bool intersect = false; - int r = 0; - - assert_cfg80211_lock(); - - r = ignore_request(wiphy, pending_request); - - if (r == REG_INTERSECT) { - if (pending_request->initiator == - NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { - kfree(pending_request); - return r; - } - } - intersect = true; - } else if (r) { - /* - * If the regulatory domain being requested by the - * driver has already been set just copy it to the - * wiphy - */ - if (r == -EALREADY && - pending_request->initiator == - NL80211_REGDOM_SET_BY_DRIVER) { - r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); - if (r) { - kfree(pending_request); - return r; - } - r = -EALREADY; - goto new_request; - } - kfree(pending_request); - return r; - } - -new_request: - kfree(last_request); - - last_request = pending_request; - last_request->intersect = intersect; - - pending_request = NULL; - - if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { - user_alpha2[0] = last_request->alpha2[0]; - user_alpha2[1] = last_request->alpha2[1]; - } - - /* When r == REG_INTERSECT we do need to call CRDA */ - if (r < 0) { - /* - * Since CRDA will not be called in this case as we already - * have applied the requested regulatory domain before we just - * inform userspace we have processed the request - */ - if (r == -EALREADY) { - nl80211_send_reg_change_event(last_request); - reg_set_request_processed(); - } - return r; - } - - return call_crda(last_request->alpha2); -} - -/* This processes *all* regulatory hints */ -static void reg_process_hint(struct regulatory_request *reg_request) -{ - int r = 0; - struct wiphy *wiphy = NULL; - enum nl80211_reg_initiator initiator = reg_request->initiator; - - BUG_ON(!reg_request->alpha2); - - if (wiphy_idx_valid(reg_request->wiphy_idx)) - wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); - - if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && - !wiphy) { - kfree(reg_request); - return; - } - - r = __regulatory_hint(wiphy, reg_request); - /* This is required so that the orig_* parameters are saved */ - if (r == -EALREADY && wiphy && - wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { - wiphy_update_regulatory(wiphy, initiator); - return; - } - - /* - * We only time out user hints, given that they should be the only - * source of bogus requests. - */ - if (r != -EALREADY && - reg_request->initiator == NL80211_REGDOM_SET_BY_USER) - schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); -} - -/* - * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* - * Regulatory hints come on a first come first serve basis and we - * must process each one atomically. - */ -static void reg_process_pending_hints(void) -{ - struct regulatory_request *reg_request; - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - /* When last_request->processed becomes true this will be rescheduled */ - if (last_request && !last_request->processed) { - REG_DBG_PRINT("Pending regulatory request, waiting " - "for it to be processed...\n"); - goto out; - } - - spin_lock(®_requests_lock); - - if (list_empty(®_requests_list)) { - spin_unlock(®_requests_lock); - goto out; - } - - reg_request = list_first_entry(®_requests_list, - struct regulatory_request, - list); - list_del_init(®_request->list); - - spin_unlock(®_requests_lock); - - reg_process_hint(reg_request); - -out: - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); -} - -/* Processes beacon hints -- this has nothing to do with country IEs */ -static void reg_process_pending_beacon_hints(void) -{ - struct cfg80211_registered_device *rdev; - struct reg_beacon *pending_beacon, *tmp; - - /* - * No need to hold the reg_mutex here as we just touch wiphys - * and do not read or access regulatory variables. - */ - mutex_lock(&cfg80211_mutex); - - /* This goes through the _pending_ beacon list */ - spin_lock_bh(®_pending_beacons_lock); - - if (list_empty(®_pending_beacons)) { - spin_unlock_bh(®_pending_beacons_lock); - goto out; - } - - list_for_each_entry_safe(pending_beacon, tmp, - ®_pending_beacons, list) { - - list_del_init(&pending_beacon->list); - - /* Applies the beacon hint to current wiphys */ - list_for_each_entry(rdev, &cfg80211_rdev_list, list) - wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); - - /* Remembers the beacon hint for new wiphys or reg changes */ - list_add_tail(&pending_beacon->list, ®_beacon_list); - } - - spin_unlock_bh(®_pending_beacons_lock); -out: - mutex_unlock(&cfg80211_mutex); -} - -static void reg_todo(struct work_struct *work) -{ - reg_process_pending_hints(); - reg_process_pending_beacon_hints(); -} - -static void queue_regulatory_request(struct regulatory_request *request) -{ - if (isalpha(request->alpha2[0])) - request->alpha2[0] = toupper(request->alpha2[0]); - if (isalpha(request->alpha2[1])) - request->alpha2[1] = toupper(request->alpha2[1]); - - spin_lock(®_requests_lock); - list_add_tail(&request->list, ®_requests_list); - spin_unlock(®_requests_lock); - - schedule_work(®_work); -} - -/* - * Core regulatory hint -- happens during cfg80211_init() - * and when we restore regulatory settings. - */ -static int regulatory_hint_core(const char *alpha2) -{ - struct regulatory_request *request; - - kfree(last_request); - last_request = NULL; - - request = kzalloc(sizeof(struct regulatory_request), - GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_CORE; - - queue_regulatory_request(request); - - return 0; -} - -/* User hints */ -int regulatory_hint_user(const char *alpha2) -{ - struct regulatory_request *request; - - BUG_ON(!alpha2); - - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->wiphy_idx = WIPHY_IDX_STALE; - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_USER; - - queue_regulatory_request(request); - - return 0; -} - -/* Driver hints */ -int regulatory_hint(struct wiphy *wiphy, const char *alpha2) -{ - struct regulatory_request *request; - - BUG_ON(!alpha2); - BUG_ON(!wiphy); - - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - return -ENOMEM; - - request->wiphy_idx = get_wiphy_idx(wiphy); - - /* Must have registered wiphy first */ - BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); - - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_DRIVER; - - queue_regulatory_request(request); - - return 0; -} -EXPORT_SYMBOL(regulatory_hint); - -/* - * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and - * therefore cannot iterate over the rdev list here. - */ -void regulatory_hint_11d(struct wiphy *wiphy, - enum ieee80211_band band, - u8 *country_ie, - u8 country_ie_len) -{ - char alpha2[2]; - enum environment_cap env = ENVIRON_ANY; - struct regulatory_request *request; - - mutex_lock(®_mutex); - - if (unlikely(!last_request)) - goto out; - - /* IE len must be evenly divisible by 2 */ - if (country_ie_len & 0x01) - goto out; - - if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) - goto out; - - alpha2[0] = country_ie[0]; - alpha2[1] = country_ie[1]; - - if (country_ie[2] == 'I') - env = ENVIRON_INDOOR; - else if (country_ie[2] == 'O') - env = ENVIRON_OUTDOOR; - - /* - * We will run this only upon a successful connection on cfg80211. - * We leave conflict resolution to the workqueue, where can hold - * cfg80211_mutex. - */ - if (likely(last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE && - wiphy_idx_valid(last_request->wiphy_idx))) - goto out; - - request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); - if (!request) - goto out; - - request->wiphy_idx = get_wiphy_idx(wiphy); - request->alpha2[0] = alpha2[0]; - request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; - request->country_ie_env = env; - - mutex_unlock(®_mutex); - - queue_regulatory_request(request); - - return; - -out: - mutex_unlock(®_mutex); -} - -static void restore_alpha2(char *alpha2, bool reset_user) -{ - /* indicates there is no alpha2 to consider for restoration */ - alpha2[0] = '9'; - alpha2[1] = '7'; - - /* The user setting has precedence over the module parameter */ - if (is_user_regdom_saved()) { - /* Unless we're asked to ignore it and reset it */ - if (reset_user) { - REG_DBG_PRINT("Restoring regulatory settings " - "including user preference\n"); - user_alpha2[0] = '9'; - user_alpha2[1] = '7'; - - /* - * If we're ignoring user settings, we still need to - * check the module parameter to ensure we put things - * back as they were for a full restore. - */ - if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); - alpha2[0] = ieee80211_regdom[0]; - alpha2[1] = ieee80211_regdom[1]; - } - } else { - REG_DBG_PRINT("Restoring regulatory settings " - "while preserving user preference for: %c%c\n", - user_alpha2[0], - user_alpha2[1]); - alpha2[0] = user_alpha2[0]; - alpha2[1] = user_alpha2[1]; - } - } else if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("Keeping preference on " - "module parameter ieee80211_regdom: %c%c\n", - ieee80211_regdom[0], - ieee80211_regdom[1]); - alpha2[0] = ieee80211_regdom[0]; - alpha2[1] = ieee80211_regdom[1]; - } else - REG_DBG_PRINT("Restoring regulatory settings\n"); -} - -/* - * Restoring regulatory settings involves ingoring any - * possibly stale country IE information and user regulatory - * settings if so desired, this includes any beacon hints - * learned as we could have traveled outside to another country - * after disconnection. To restore regulatory settings we do - * exactly what we did at bootup: - * - * - send a core regulatory hint - * - send a user regulatory hint if applicable - * - * Device drivers that send a regulatory hint for a specific country - * keep their own regulatory domain on wiphy->regd so that does does - * not need to be remembered. - */ -static void restore_regulatory_settings(bool reset_user) -{ - char alpha2[2]; - struct reg_beacon *reg_beacon, *btmp; - struct regulatory_request *reg_request, *tmp; - LIST_HEAD(tmp_reg_req_list); - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - reset_regdomains(); - restore_alpha2(alpha2, reset_user); - - /* - * If there's any pending requests we simply - * stash them to a temporary pending queue and - * add then after we've restored regulatory - * settings. - */ - spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - if (reg_request->initiator != - NL80211_REGDOM_SET_BY_USER) - continue; - list_del(®_request->list); - list_add_tail(®_request->list, &tmp_reg_req_list); - } - } - spin_unlock(®_requests_lock); - - /* Clear beacon hints */ - spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - spin_unlock_bh(®_pending_beacons_lock); - - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - - /* First restore to the basic regulatory settings */ - cfg80211_regdomain = cfg80211_world_regdom; - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); - - regulatory_hint_core(cfg80211_regdomain->alpha2); - - /* - * This restores the ieee80211_regdom module parameter - * preference or the last user requested regulatory - * settings, user regulatory settings takes precedence. - */ - if (is_an_alpha2(alpha2)) - regulatory_hint_user(user_alpha2); - - if (list_empty(&tmp_reg_req_list)) - return; - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - spin_lock(®_requests_lock); - list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { - REG_DBG_PRINT("Adding request for country %c%c back " - "into the queue\n", - reg_request->alpha2[0], - reg_request->alpha2[1]); - list_del(®_request->list); - list_add_tail(®_request->list, ®_requests_list); - } - spin_unlock(®_requests_lock); - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); - - REG_DBG_PRINT("Kicking the queue\n"); - - schedule_work(®_work); -} - -void regulatory_hint_disconnect(void) -{ - REG_DBG_PRINT("All devices are disconnected, going to " - "restore regulatory settings\n"); - restore_regulatory_settings(false); -} - -static bool freq_is_chan_12_13_14(u16 freq) -{ - if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) || - freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) || - freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ)) - return true; - return false; -} - -int regulatory_hint_found_beacon(struct wiphy *wiphy, - struct ieee80211_channel *beacon_chan, - gfp_t gfp) -{ - struct reg_beacon *reg_beacon; - - if (likely((beacon_chan->beacon_found || - (beacon_chan->flags & IEEE80211_CHAN_RADAR) || - (beacon_chan->band == IEEE80211_BAND_2GHZ && - !freq_is_chan_12_13_14(beacon_chan->center_freq))))) - return 0; - - reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); - if (!reg_beacon) - return -ENOMEM; - - REG_DBG_PRINT("Found new beacon on " - "frequency: %d MHz (Ch %d) on %s\n", - beacon_chan->center_freq, - ieee80211_frequency_to_channel(beacon_chan->center_freq), - wiphy_name(wiphy)); - - memcpy(®_beacon->chan, beacon_chan, - sizeof(struct ieee80211_channel)); - - - /* - * Since we can be called from BH or and non-BH context - * we must use spin_lock_bh() - */ - spin_lock_bh(®_pending_beacons_lock); - list_add_tail(®_beacon->list, ®_pending_beacons); - spin_unlock_bh(®_pending_beacons_lock); - - schedule_work(®_work); - - return 0; -} - -static void print_rd_rules(const struct ieee80211_regdomain *rd) -{ - unsigned int i; - const struct ieee80211_reg_rule *reg_rule = NULL; - const struct ieee80211_freq_range *freq_range = NULL; - const struct ieee80211_power_rule *power_rule = NULL; - - pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); - - for (i = 0; i < rd->n_reg_rules; i++) { - reg_rule = &rd->reg_rules[i]; - freq_range = ®_rule->freq_range; - power_rule = ®_rule->power_rule; - - /* - * There may not be documentation for max antenna gain - * in certain regions - */ - if (power_rule->max_antenna_gain) - pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - power_rule->max_antenna_gain, - power_rule->max_eirp); - else - pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", - freq_range->start_freq_khz, - freq_range->end_freq_khz, - freq_range->max_bandwidth_khz, - power_rule->max_eirp); - } -} - -static void print_regdomain(const struct ieee80211_regdomain *rd) -{ - - if (is_intersected_alpha2(rd->alpha2)) { - - if (last_request->initiator == - NL80211_REGDOM_SET_BY_COUNTRY_IE) { - struct cfg80211_registered_device *rdev; - rdev = cfg80211_rdev_by_wiphy_idx( - last_request->wiphy_idx); - if (rdev) { - pr_info("Current regulatory domain updated by AP to: %c%c\n", - rdev->country_ie_alpha2[0], - rdev->country_ie_alpha2[1]); - } else - pr_info("Current regulatory domain intersected:\n"); - } else - pr_info("Current regulatory domain intersected:\n"); - } else if (is_world_regdom(rd->alpha2)) - pr_info("World regulatory domain updated:\n"); - else { - if (is_unknown_alpha2(rd->alpha2)) - pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); - else - pr_info("Regulatory domain changed to country: %c%c\n", - rd->alpha2[0], rd->alpha2[1]); - } - print_rd_rules(rd); -} - -static void print_regdomain_info(const struct ieee80211_regdomain *rd) -{ - pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); - print_rd_rules(rd); -} - -/* Takes ownership of rd only if it doesn't fail */ -static int __set_regdom(const struct ieee80211_regdomain *rd) -{ - const struct ieee80211_regdomain *intersected_rd = NULL; - struct cfg80211_registered_device *rdev = NULL; - struct wiphy *request_wiphy; - /* Some basic sanity checks first */ - - if (is_world_regdom(rd->alpha2)) { - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; - update_world_regdomain(rd); - return 0; - } - - if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && - !is_unknown_alpha2(rd->alpha2)) - return -EINVAL; - - if (!last_request) - return -EINVAL; - - /* - * Lets only bother proceeding on the same alpha2 if the current - * rd is non static (it means CRDA was present and was used last) - * and the pending request came in from a country IE - */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - /* - * If someone else asked us to change the rd lets only bother - * checking if the alpha2 changes if CRDA was already called - */ - if (!regdom_changes(rd->alpha2)) - return -EINVAL; - } - - /* - * Now lets set the regulatory domain, update all driver channels - * and finally inform them of what we have done, in case they want - * to review or adjust their own settings based on their own - * internal EEPROM data - */ - - if (WARN_ON(!reg_is_valid_request(rd->alpha2))) - return -EINVAL; - - if (!is_valid_rd(rd)) { - pr_err("Invalid regulatory domain detected:\n"); - print_regdomain_info(rd); - return -EINVAL; - } - - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - if (!last_request->intersect) { - int r; - - if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { - reset_regdomains(); - cfg80211_regdomain = rd; - return 0; - } - - /* - * For a driver hint, lets copy the regulatory domain the - * driver wanted to the wiphy to deal with conflicts - */ - - /* - * Userspace could have sent two replies with only - * one kernel request. - */ - if (request_wiphy->regd) - return -EALREADY; - - r = reg_copy_regd(&request_wiphy->regd, rd); - if (r) - return r; - - reset_regdomains(); - cfg80211_regdomain = rd; - return 0; - } - - /* Intersection requires a bit more work */ - - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - - intersected_rd = regdom_intersect(rd, cfg80211_regdomain); - if (!intersected_rd) - return -EINVAL; - - /* - * We can trash what CRDA provided now. - * However if a driver requested this specific regulatory - * domain we keep it for its private use - */ - if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) - request_wiphy->regd = rd; - else - kfree(rd); - - rd = NULL; - - reset_regdomains(); - cfg80211_regdomain = intersected_rd; - - return 0; - } - - if (!intersected_rd) - return -EINVAL; - - rdev = wiphy_to_dev(request_wiphy); - - rdev->country_ie_alpha2[0] = rd->alpha2[0]; - rdev->country_ie_alpha2[1] = rd->alpha2[1]; - rdev->env = last_request->country_ie_env; - - BUG_ON(intersected_rd == rd); - - kfree(rd); - rd = NULL; - - reset_regdomains(); - cfg80211_regdomain = intersected_rd; - - return 0; -} - - -/* - * Use this call to set the current regulatory domain. Conflicts with - * multiple drivers can be ironed out later. Caller must've already - * kmalloc'd the rd structure. Caller must hold cfg80211_mutex - */ -int set_regdom(const struct ieee80211_regdomain *rd) -{ - int r; - - assert_cfg80211_lock(); - - mutex_lock(®_mutex); - - /* Note that this doesn't update the wiphys, this is done below */ - r = __set_regdom(rd); - if (r) { - kfree(rd); - mutex_unlock(®_mutex); - return r; - } - - /* This would make this whole thing pointless */ - if (!last_request->intersect) - BUG_ON(rd != cfg80211_regdomain); - - /* update all wiphys now with the new established regulatory domain */ - update_all_wiphy_regulatory(last_request->initiator); - - print_regdomain(cfg80211_regdomain); - - nl80211_send_reg_change_event(last_request); - - reg_set_request_processed(); - - mutex_unlock(®_mutex); - - return r; -} - -#ifdef CONFIG_HOTPLUG -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - if (last_request && !last_request->processed) { - if (add_uevent_var(env, "COUNTRY=%c%c", - last_request->alpha2[0], - last_request->alpha2[1])) - return -ENOMEM; - } - - return 0; -} -#else -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - return -ENODEV; -} -#endif /* CONFIG_HOTPLUG */ - -/* Caller must hold cfg80211_mutex */ -void reg_device_remove(struct wiphy *wiphy) -{ - struct wiphy *request_wiphy = NULL; - - assert_cfg80211_lock(); - - mutex_lock(®_mutex); - - kfree(wiphy->regd); - - if (last_request) - request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); - - if (!request_wiphy || request_wiphy != wiphy) - goto out; - - last_request->wiphy_idx = WIPHY_IDX_STALE; - last_request->country_ie_env = ENVIRON_ANY; -out: - mutex_unlock(®_mutex); -} - -static void reg_timeout_work(struct work_struct *work) -{ - REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " - "restoring regulatory settings\n"); - restore_regulatory_settings(true); -} - -int __init regulatory_init(void) -{ - int err = 0; - - reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0); - if (IS_ERR(reg_pdev)) - return PTR_ERR(reg_pdev); - - reg_pdev->dev.type = ®_device_type; - - spin_lock_init(®_requests_lock); - spin_lock_init(®_pending_beacons_lock); - - cfg80211_regdomain = cfg80211_world_regdom; - - user_alpha2[0] = '9'; - user_alpha2[1] = '7'; - - /* We always try to get an update for the static regdomain */ - err = regulatory_hint_core(cfg80211_regdomain->alpha2); - if (err) { - if (err == -ENOMEM) - return err; - /* - * N.B. kobject_uevent_env() can fail mainly for when we're out - * memory which is handled and propagated appropriately above - * but it can also fail during a netlink_broadcast() or during - * early boot for call_usermodehelper(). For now treat these - * errors as non-fatal. - */ - pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); -#ifdef CONFIG_CFG80211_REG_DEBUG - /* We want to find out exactly why when debugging */ - WARN_ON(err); -#endif - } - - /* - * Finally, if the user set the module parameter treat it - * as a user hint. - */ - if (!is_world_regdom(ieee80211_regdom)) - regulatory_hint_user(ieee80211_regdom); - - return 0; -} - -void /* __init_or_exit */ regulatory_exit(void) -{ - struct regulatory_request *reg_request, *tmp; - struct reg_beacon *reg_beacon, *btmp; - - cancel_work_sync(®_work); - cancel_delayed_work_sync(®_timeout); - - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - - reset_regdomains(); - - kfree(last_request); - - last_request = NULL; - dev_set_uevent_suppress(®_pdev->dev, true); - - platform_device_unregister(reg_pdev); - - spin_lock_bh(®_pending_beacons_lock); - if (!list_empty(®_pending_beacons)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_pending_beacons, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - spin_unlock_bh(®_pending_beacons_lock); - - if (!list_empty(®_beacon_list)) { - list_for_each_entry_safe(reg_beacon, btmp, - ®_beacon_list, list) { - list_del(®_beacon->list); - kfree(reg_beacon); - } - } - - spin_lock(®_requests_lock); - if (!list_empty(®_requests_list)) { - list_for_each_entry_safe(reg_request, tmp, - ®_requests_list, list) { - list_del(®_request->list); - kfree(reg_request); - } - } - spin_unlock(®_requests_lock); - - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); -} diff --git a/net/wireless_ath/reg.h b/net/wireless_ath/reg.h deleted file mode 100755 index 4a56799..0000000 --- a/net/wireless_ath/reg.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef __NET_WIRELESS_REG_H -#define __NET_WIRELESS_REG_H - -extern const struct ieee80211_regdomain *cfg80211_regdomain; - -bool is_world_regdom(const char *alpha2); -bool reg_is_valid_request(const char *alpha2); - -int regulatory_hint_user(const char *alpha2); - -int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env); -void reg_device_remove(struct wiphy *wiphy); - -int __init regulatory_init(void); -void regulatory_exit(void); - -int set_regdom(const struct ieee80211_regdomain *rd); - -void regulatory_update(struct wiphy *wiphy, enum nl80211_reg_initiator setby); - -/** - * regulatory_hint_found_beacon - hints a beacon was found on a channel - * @wiphy: the wireless device where the beacon was found on - * @beacon_chan: the channel on which the beacon was found on - * @gfp: context flags - * - * This informs the wireless core that a beacon from an AP was found on - * the channel provided. This allows the wireless core to make educated - * guesses on regulatory to help with world roaming. This is only used for - * world roaming -- when we do not know our current location. This is - * only useful on channels 12, 13 and 14 on the 2 GHz band as channels - * 1-11 are already enabled by the world regulatory domain; and on - * non-radar 5 GHz channels. - * - * Drivers do not need to call this, cfg80211 will do it for after a scan - * on a newly found BSS. If you cannot make use of this feature you can - * set the wiphy->disable_beacon_hints to true. - */ -int regulatory_hint_found_beacon(struct wiphy *wiphy, - struct ieee80211_channel *beacon_chan, - gfp_t gfp); - -/** - * regulatory_hint_11d - hints a country IE as a regulatory domain - * @wiphy: the wireless device giving the hint (used only for reporting - * conflicts) - * @band: the band on which the country IE was received on. This determines - * the band we'll process the country IE channel triplets for. - * @country_ie: pointer to the country IE - * @country_ie_len: length of the country IE - * - * We will intersect the rd with the what CRDA tells us should apply - * for the alpha2 this country IE belongs to, this prevents APs from - * sending us incorrect or outdated information against a country. - * - * The AP is expected to provide Country IE channel triplets for the - * band it is on. It is technically possible for APs to send channel - * country IE triplets even for channels outside of the band they are - * in but for that they would have to use the regulatory extension - * in combination with a triplet but this behaviour is currently - * not observed. For this reason if a triplet is seen with channel - * information for a band the BSS is not present in it will be ignored. - */ -void regulatory_hint_11d(struct wiphy *wiphy, - enum ieee80211_band band, - u8 *country_ie, - u8 country_ie_len); - -/** - * regulatory_hint_disconnect - informs all devices have been disconneted - * - * Regulotory rules can be enhanced further upon scanning and upon - * connection to an AP. These rules become stale if we disconnect - * and go to another country, whether or not we suspend and resume. - * If we suspend, go to another country and resume we'll automatically - * get disconnected shortly after resuming and things will be reset as well. - * This routine is a helper to restore regulatory settings to how they were - * prior to our first connect attempt. This includes ignoring country IE and - * beacon regulatory hints. The ieee80211_regdom module parameter will always - * be respected but if a user had set the regulatory domain that will take - * precedence. - * - * Must be called from process context. - */ -void regulatory_hint_disconnect(void); - -#endif /* __NET_WIRELESS_REG_H */ diff --git a/net/wireless_ath/regdb.h b/net/wireless_ath/regdb.h deleted file mode 100755 index 818222c..0000000 --- a/net/wireless_ath/regdb.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __REGDB_H__ -#define __REGDB_H__ - -extern const struct ieee80211_regdomain *reg_regdb[]; -extern int reg_regdb_size; - -#endif /* __REGDB_H__ */ diff --git a/net/wireless_ath/scan.c b/net/wireless_ath/scan.c deleted file mode 100755 index 8f5fa19..0000000 --- a/net/wireless_ath/scan.c +++ /dev/null @@ -1,1381 +0,0 @@ -/* - * cfg80211 scan result handling - * - * Copyright 2008 Johannes Berg <johannes@sipsolutions.net> - */ -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/wireless.h> -#include <linux/nl80211.h> -#include <linux/etherdevice.h> -#include <net/arp.h> -#include <net/cfg80211.h> -#include <net/cfg80211-wext.h> -#include <net/iw_handler.h> -#include "core.h" -#include "nl80211.h" -#include "wext-compat.h" - -#define IEEE80211_SCAN_RESULT_EXPIRE (15 * HZ) - -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) -{ - struct cfg80211_scan_request *request; - struct net_device *dev; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - ASSERT_RDEV_LOCK(rdev); - - request = rdev->scan_req; - - if (!request) - return; - - dev = request->dev; - - /* - * This must be before sending the other events! - * Otherwise, wpa_supplicant gets completely confused with - * wext events. - */ - cfg80211_sme_scan_done(dev); - - if (request->aborted) - nl80211_send_scan_aborted(rdev, dev); - else - nl80211_send_scan_done(rdev, dev); - -#ifdef CONFIG_CFG80211_WEXT - if (!request->aborted) { - memset(&wrqu, 0, sizeof(wrqu)); - - wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); - } -#endif - - dev_put(dev); - - rdev->scan_req = NULL; - - /* - * OK. If this is invoked with "leak" then we can't - * free this ... but we've cleaned it up anyway. The - * driver failed to call the scan_done callback, so - * all bets are off, it might still be trying to use - * the scan request or not ... if it accesses the dev - * in there (it shouldn't anyway) then it may crash. - */ - if (!leak) - kfree(request); -} - -void __cfg80211_scan_done(struct work_struct *wk) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(wk, struct cfg80211_registered_device, - scan_done_wk); - - cfg80211_lock_rdev(rdev); - ___cfg80211_scan_done(rdev, false); - cfg80211_unlock_rdev(rdev); -} - -void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) -{ - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); - - request->aborted = aborted; - queue_work(cfg80211_wq, &wiphy_to_dev(request->wiphy)->scan_done_wk); -} -EXPORT_SYMBOL(cfg80211_scan_done); - -void __cfg80211_sched_scan_results(struct work_struct *wk) -{ - struct cfg80211_registered_device *rdev; - - rdev = container_of(wk, struct cfg80211_registered_device, - sched_scan_results_wk); - - mutex_lock(&rdev->sched_scan_mtx); - - /* we don't have sched_scan_req anymore if the scan is stopping */ - if (rdev->sched_scan_req) - nl80211_send_sched_scan_results(rdev, - rdev->sched_scan_req->dev); - - mutex_unlock(&rdev->sched_scan_mtx); -} - -void cfg80211_sched_scan_results(struct wiphy *wiphy) -{ - /* ignore if we're not scanning */ - if (wiphy_to_dev(wiphy)->sched_scan_req) - queue_work(cfg80211_wq, - &wiphy_to_dev(wiphy)->sched_scan_results_wk); -} -EXPORT_SYMBOL(cfg80211_sched_scan_results); - -void cfg80211_sched_scan_stopped(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - mutex_lock(&rdev->sched_scan_mtx); - __cfg80211_stop_sched_scan(rdev, true); - mutex_unlock(&rdev->sched_scan_mtx); -} -EXPORT_SYMBOL(cfg80211_sched_scan_stopped); - -int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, - bool driver_initiated) -{ - struct net_device *dev; - - lockdep_assert_held(&rdev->sched_scan_mtx); - - if (!rdev->sched_scan_req) - return -ENOENT; - - dev = rdev->sched_scan_req->dev; - - if (!driver_initiated) { - int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev); - if (err) - return err; - } - - nl80211_send_sched_scan(rdev, dev, NL80211_CMD_SCHED_SCAN_STOPPED); - - kfree(rdev->sched_scan_req); - rdev->sched_scan_req = NULL; - - return 0; -} - -static void bss_release(struct kref *ref) -{ - struct cfg80211_internal_bss *bss; - - bss = container_of(ref, struct cfg80211_internal_bss, ref); - if (bss->pub.free_priv) - bss->pub.free_priv(&bss->pub); - - if (bss->beacon_ies_allocated) - kfree(bss->pub.beacon_ies); - if (bss->proberesp_ies_allocated) - kfree(bss->pub.proberesp_ies); - - BUG_ON(atomic_read(&bss->hold)); - - kfree(bss); -} - -/* must hold dev->bss_lock! */ -void cfg80211_bss_age(struct cfg80211_registered_device *dev, - unsigned long age_secs) -{ - struct cfg80211_internal_bss *bss; - unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); - - list_for_each_entry(bss, &dev->bss_list, list) { - bss->ts -= age_jiffies; - } -} - -/* must hold dev->bss_lock! */ -static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *bss) -{ - list_del_init(&bss->list); - rb_erase(&bss->rbn, &dev->bss_tree); - kref_put(&bss->ref, bss_release); -} - -/* must hold dev->bss_lock! */ -void cfg80211_bss_expire(struct cfg80211_registered_device *dev) -{ - struct cfg80211_internal_bss *bss, *tmp; - bool expired = false; - - list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - if (atomic_read(&bss->hold)) - continue; - if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE)) - continue; - __cfg80211_unlink_bss(dev, bss); - expired = true; - } - - if (expired) - dev->bss_generation++; -} - -/* must hold dev->bss_lock! */ -void cfg80211_bss_expire_all(struct cfg80211_registered_device *dev) -{ - struct cfg80211_internal_bss *bss, *tmp; - bool expired = false; - - printk("%s() Enter - steven", __func__); - - list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) { - __cfg80211_unlink_bss(dev, bss); - expired = true; - } - - if (expired) - dev->bss_generation++; -} - -const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) -{ - while (len > 2 && ies[0] != eid) { - len -= ies[1] + 2; - ies += ies[1] + 2; - } - if (len < 2) - return NULL; - if (len < 2 + ies[1]) - return NULL; - return ies; -} -EXPORT_SYMBOL(cfg80211_find_ie); - -const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, - const u8 *ies, int len) -{ - struct ieee80211_vendor_ie *ie; - const u8 *pos = ies, *end = ies + len; - int ie_oui; - - while (pos < end) { - pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, - end - pos); - if (!pos) - return NULL; - - if (end - pos < sizeof(*ie)) - return NULL; - - ie = (struct ieee80211_vendor_ie *)pos; - ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; - if (ie_oui == oui && ie->oui_type == oui_type) - return pos; - - pos += 2 + ie->len; - } - return NULL; -} -EXPORT_SYMBOL(cfg80211_find_vendor_ie); - -static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) -{ - const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); - const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); - - /* equal if both missing */ - if (!ie1 && !ie2) - return 0; - /* sort missing IE before (left of) present IE */ - if (!ie1) - return -1; - if (!ie2) - return 1; - - /* sort by length first, then by contents */ - if (ie1[1] != ie2[1]) - return ie2[1] - ie1[1]; - return memcmp(ie1 + 2, ie2 + 2, ie1[1]); -} - -static bool is_bss(struct cfg80211_bss *a, - const u8 *bssid, - const u8 *ssid, size_t ssid_len) -{ - const u8 *ssidie; - - if (bssid && compare_ether_addr(a->bssid, bssid)) - return false; - - if (!ssid) - return true; - - ssidie = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); - if (!ssidie) - return false; - if (ssidie[1] != ssid_len) - return false; - return memcmp(ssidie + 2, ssid, ssid_len) == 0; -} - -static bool is_mesh_bss(struct cfg80211_bss *a) -{ - const u8 *ie; - - if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - - return true; -} - -static bool is_mesh(struct cfg80211_bss *a, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg) -{ - const u8 *ie; - - if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - if (ie[1] != meshidlen) - return false; - if (memcmp(ie + 2, meshid, meshidlen)) - return false; - - ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements); - if (!ie) - return false; - if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) - return false; - - /* - * Ignore mesh capability (last two bytes of the IE) when - * comparing since that may differ between stations taking - * part in the same mesh. - */ - return memcmp(ie + 2, meshcfg, - sizeof(struct ieee80211_meshconf_ie) - 2) == 0; -} - -static int cmp_bss_core(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - int r; - - if (a->channel != b->channel) - return b->channel->center_freq - a->channel->center_freq; - - if (is_mesh_bss(a) && is_mesh_bss(b)) { - r = cmp_ies(WLAN_EID_MESH_ID, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); - if (r) - return r; - return cmp_ies(WLAN_EID_MESH_CONFIG, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); - } - - return memcmp(a->bssid, b->bssid, ETH_ALEN); -} - -static int cmp_bss(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - int r; - - r = cmp_bss_core(a, b); - if (r) - return r; - - return cmp_ies(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements, - b->information_elements, - b->len_information_elements); -} - -static int cmp_hidden_bss(struct cfg80211_bss *a, - struct cfg80211_bss *b) -{ - const u8 *ie1; - const u8 *ie2; - int i; - int r; - - r = cmp_bss_core(a, b); - if (r) - return r; - - ie1 = cfg80211_find_ie(WLAN_EID_SSID, - a->information_elements, - a->len_information_elements); - ie2 = cfg80211_find_ie(WLAN_EID_SSID, - b->information_elements, - b->len_information_elements); - - /* Key comparator must use same algorithm in any rb-tree - * search function (order is important), otherwise ordering - * of items in the tree is broken and search gives incorrect - * results. This code uses same order as cmp_ies() does. */ - - /* sort missing IE before (left of) present IE */ - if (!ie1) - return -1; - if (!ie2) - return 1; - - /* zero-size SSID is used as an indication of the hidden bss */ - if (!ie2[1]) - return 0; - - /* sort by length first, then by contents */ - if (ie1[1] != ie2[1]) - return ie2[1] - ie1[1]; - - /* zeroed SSID ie is another indication of a hidden bss */ - for (i = 0; i < ie2[1]; i++) - if (ie2[i + 2]) - return -1; - - return 0; -} - -struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *bssid, - const u8 *ssid, size_t ssid_len, - u16 capa_mask, u16 capa_val) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss, *res = NULL; - unsigned long now = jiffies; - - spin_lock_bh(&dev->bss_lock); - - list_for_each_entry(bss, &dev->bss_list, list) { - if ((bss->pub.capability & capa_mask) != capa_val) - continue; - if (channel && bss->pub.channel != channel) - continue; - /* Don't get expired BSS structs */ - if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) && - !atomic_read(&bss->hold)) - continue; - if (is_bss(&bss->pub, bssid, ssid, ssid_len)) { - res = bss; - kref_get(&res->ref); - break; - } - } - - spin_unlock_bh(&dev->bss_lock); - if (!res) - return NULL; - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_get_bss); - -struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *meshid, size_t meshidlen, - const u8 *meshcfg) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss, *res = NULL; - - spin_lock_bh(&dev->bss_lock); - - list_for_each_entry(bss, &dev->bss_list, list) { - if (channel && bss->pub.channel != channel) - continue; - if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) { - res = bss; - kref_get(&res->ref); - break; - } - } - - spin_unlock_bh(&dev->bss_lock); - if (!res) - return NULL; - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_get_mesh); - - -static void rb_insert_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *bss) -{ - struct rb_node **p = &dev->bss_tree.rb_node; - struct rb_node *parent = NULL; - struct cfg80211_internal_bss *tbss; - int cmp; - - while (*p) { - parent = *p; - tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn); - - cmp = cmp_bss(&bss->pub, &tbss->pub); - - if (WARN_ON(!cmp)) { - /* will sort of leak this BSS */ - return; - } - - if (cmp < 0) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - - rb_link_node(&bss->rbn, parent, p); - rb_insert_color(&bss->rbn, &dev->bss_tree); -} - -static struct cfg80211_internal_bss * -rb_find_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct rb_node *n = dev->bss_tree.rb_node; - struct cfg80211_internal_bss *bss; - int r; - - while (n) { - bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_bss(&res->pub, &bss->pub); - - if (r == 0) - return bss; - else if (r < 0) - n = n->rb_left; - else - n = n->rb_right; - } - - return NULL; -} - -static struct cfg80211_internal_bss * -rb_find_hidden_bss(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct rb_node *n = dev->bss_tree.rb_node; - struct cfg80211_internal_bss *bss; - int r; - - while (n) { - bss = rb_entry(n, struct cfg80211_internal_bss, rbn); - r = cmp_hidden_bss(&res->pub, &bss->pub); - - if (r == 0) - return bss; - else if (r < 0) - n = n->rb_left; - else - n = n->rb_right; - } - - return NULL; -} - -static void -copy_hidden_ies(struct cfg80211_internal_bss *res, - struct cfg80211_internal_bss *hidden) -{ - if (unlikely(res->pub.beacon_ies)) - return; - if (WARN_ON(!hidden->pub.beacon_ies)) - return; - - res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC); - if (unlikely(!res->pub.beacon_ies)) - return; - - res->beacon_ies_allocated = true; - res->pub.len_beacon_ies = hidden->pub.len_beacon_ies; - memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies, - res->pub.len_beacon_ies); -} - -static struct cfg80211_internal_bss * -cfg80211_bss_update(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res) -{ - struct cfg80211_internal_bss *found = NULL; - - /* - * The reference to "res" is donated to this function. - */ - - if (WARN_ON(!res->pub.channel)) { - kref_put(&res->ref, bss_release); - return NULL; - } - - res->ts = jiffies; - - spin_lock_bh(&dev->bss_lock); - - found = rb_find_bss(dev, res); - - if (found) { - found->pub.beacon_interval = res->pub.beacon_interval; - found->pub.tsf = res->pub.tsf; - found->pub.signal = res->pub.signal; - found->pub.capability = res->pub.capability; - found->ts = res->ts; - - /* Update IEs */ - if (res->pub.proberesp_ies) { - size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_proberesp_ies; - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) - if (0) { - used = 0; /* just to shut up the compiler */ -#else - if (found->pub.proberesp_ies && - !found->proberesp_ies_allocated && - ksize(found) >= used + ielen) { -#endif - memcpy(found->pub.proberesp_ies, - res->pub.proberesp_ies, ielen); - found->pub.len_proberesp_ies = ielen; - } else { - u8 *ies = found->pub.proberesp_ies; - - if (found->proberesp_ies_allocated) - ies = krealloc(ies, ielen, GFP_ATOMIC); - else - ies = kmalloc(ielen, GFP_ATOMIC); - - if (ies) { - memcpy(ies, res->pub.proberesp_ies, - ielen); - found->proberesp_ies_allocated = true; - found->pub.proberesp_ies = ies; - found->pub.len_proberesp_ies = ielen; - } - } - - /* Override possible earlier Beacon frame IEs */ - found->pub.information_elements = - found->pub.proberesp_ies; - found->pub.len_information_elements = - found->pub.len_proberesp_ies; - } - if (res->pub.beacon_ies) { - size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_beacon_ies; - bool information_elements_is_beacon_ies = - (found->pub.information_elements == - found->pub.beacon_ies); - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,28) - if (0) { - used = 0; /* just to shut up the compiler */ -#else - if (found->pub.beacon_ies && - !found->beacon_ies_allocated && - ksize(found) >= used + ielen) { -#endif - memcpy(found->pub.beacon_ies, - res->pub.beacon_ies, ielen); - found->pub.len_beacon_ies = ielen; - } else { - u8 *ies = found->pub.beacon_ies; - - if (found->beacon_ies_allocated) - ies = krealloc(ies, ielen, GFP_ATOMIC); - else - ies = kmalloc(ielen, GFP_ATOMIC); - - if (ies) { - memcpy(ies, res->pub.beacon_ies, - ielen); - found->beacon_ies_allocated = true; - found->pub.beacon_ies = ies; - found->pub.len_beacon_ies = ielen; - } - } - - /* Override IEs if they were from a beacon before */ - if (information_elements_is_beacon_ies) { - found->pub.information_elements = - found->pub.beacon_ies; - found->pub.len_information_elements = - found->pub.len_beacon_ies; - } - } - - kref_put(&res->ref, bss_release); - } else { - struct cfg80211_internal_bss *hidden; - - /* First check if the beacon is a probe response from - * a hidden bss. If so, copy beacon ies (with nullified - * ssid) into the probe response bss entry (with real ssid). - * It is required basically for PSM implementation - * (probe responses do not contain tim ie) */ - - /* TODO: The code is not trying to update existing probe - * response bss entries when beacon ies are - * getting changed. */ - hidden = rb_find_hidden_bss(dev, res); - if (hidden) - copy_hidden_ies(res, hidden); - - /* this "consumes" the reference */ - list_add_tail(&res->list, &dev->bss_list); - rb_insert_bss(dev, res); - found = res; - } - - dev->bss_generation++; - spin_unlock_bh(&dev->bss_lock); - - kref_get(&found->ref); - return found; -} - -struct cfg80211_bss* -cfg80211_inform_bss(struct wiphy *wiphy, - struct ieee80211_channel *channel, - const u8 *bssid, - u64 timestamp, u16 capability, u16 beacon_interval, - const u8 *ie, size_t ielen, - s32 signal, gfp_t gfp) -{ - struct cfg80211_internal_bss *res; - size_t privsz; - - if (WARN_ON(!wiphy)) - return NULL; - - privsz = wiphy->bss_priv_size; - - if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && - (signal < 0 || signal > 100))) - return NULL; - - res = kzalloc(sizeof(*res) + privsz + ielen, gfp); - if (!res) - return NULL; - - memcpy(res->pub.bssid, bssid, ETH_ALEN); - res->pub.channel = channel; - res->pub.signal = signal; - res->pub.tsf = timestamp; - res->pub.beacon_interval = beacon_interval; - res->pub.capability = capability; - /* - * Since we do not know here whether the IEs are from a Beacon or Probe - * Response frame, we need to pick one of the options and only use it - * with the driver that does not provide the full Beacon/Probe Response - * frame. Use Beacon frame pointer to avoid indicating that this should - * override the information_elements pointer should we have received an - * earlier indication of Probe Response data. - * - * The initial buffer for the IEs is allocated with the BSS entry and - * is located after the private area. - */ - res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; - memcpy(res->pub.beacon_ies, ie, ielen); - res->pub.len_beacon_ies = ielen; - res->pub.information_elements = res->pub.beacon_ies; - res->pub.len_information_elements = res->pub.len_beacon_ies; - - kref_init(&res->ref); - - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); - if (!res) - return NULL; - - if (res->pub.capability & WLAN_CAPABILITY_ESS) - regulatory_hint_found_beacon(wiphy, channel, gfp); - - /* cfg80211_bss_update gives us a referenced result */ - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_inform_bss); - -struct cfg80211_bss * -cfg80211_inform_bss_frame(struct wiphy *wiphy, - struct ieee80211_channel *channel, - struct ieee80211_mgmt *mgmt, size_t len, - s32 signal, gfp_t gfp) -{ - struct cfg80211_internal_bss *res; - size_t ielen = len - offsetof(struct ieee80211_mgmt, - u.probe_resp.variable); - size_t privsz; - - if (WARN_ON(!mgmt)) - return NULL; - - if (WARN_ON(!wiphy)) - return NULL; - - if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC && - (signal < 0 || signal > 100))) - return NULL; - - if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable))) - return NULL; - - privsz = wiphy->bss_priv_size; - - res = kzalloc(sizeof(*res) + privsz + ielen, gfp); - if (!res) - return NULL; - - memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN); - res->pub.channel = channel; - res->pub.signal = signal; - res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); - res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); - res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - /* - * The initial buffer for the IEs is allocated with the BSS entry and - * is located after the private area. - */ - if (ieee80211_is_probe_resp(mgmt->frame_control)) { - res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; - memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, - ielen); - res->pub.len_proberesp_ies = ielen; - res->pub.information_elements = res->pub.proberesp_ies; - res->pub.len_information_elements = res->pub.len_proberesp_ies; - } else { - res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; - memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); - res->pub.len_beacon_ies = ielen; - res->pub.information_elements = res->pub.beacon_ies; - res->pub.len_information_elements = res->pub.len_beacon_ies; - } - - kref_init(&res->ref); - - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); - if (!res) - return NULL; - - if (res->pub.capability & WLAN_CAPABILITY_ESS) - regulatory_hint_found_beacon(wiphy, channel, gfp); - - /* cfg80211_bss_update gives us a referenced result */ - return &res->pub; -} -EXPORT_SYMBOL(cfg80211_inform_bss_frame); - -void cfg80211_put_bss(struct cfg80211_bss *pub) -{ - struct cfg80211_internal_bss *bss; - - if (!pub) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - kref_put(&bss->ref, bss_release); -} -EXPORT_SYMBOL(cfg80211_put_bss); - -void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - struct cfg80211_internal_bss *bss; - - if (WARN_ON(!pub)) - return; - - bss = container_of(pub, struct cfg80211_internal_bss, pub); - - spin_lock_bh(&dev->bss_lock); - if (!list_empty(&bss->list)) { - __cfg80211_unlink_bss(dev, bss); - dev->bss_generation++; - } - spin_unlock_bh(&dev->bss_lock); -} -EXPORT_SYMBOL(cfg80211_unlink_bss); - -void cfg80211_unlink_allbss(struct wiphy *wiphy) -{ - struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy); - - printk("%s() Enter - steven", __func__); - - spin_lock_bh(&dev->bss_lock); - cfg80211_bss_expire_all(dev); - spin_unlock_bh(&dev->bss_lock); -} -EXPORT_SYMBOL(cfg80211_unlink_allbss); - -#ifdef CONFIG_CFG80211_WEXT -int cfg80211_wext_siwscan(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct cfg80211_registered_device *rdev; - struct wiphy *wiphy; - struct iw_scan_req *wreq = NULL; - struct cfg80211_scan_request *creq = NULL; - int i, err, n_channels = 0; - enum ieee80211_band band; - - if (!netif_running(dev)) - return -ENETDOWN; - - if (wrqu->data.length == sizeof(struct iw_scan_req)) - wreq = (struct iw_scan_req *)extra; - - rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); - - if (IS_ERR(rdev)) - return PTR_ERR(rdev); - - if (rdev->scan_req) { - err = -EBUSY; - goto out; - } - - wiphy = &rdev->wiphy; - - /* Determine number of channels, needed to allocate creq */ - if (wreq && wreq->num_channels) - n_channels = wreq->num_channels; - else { - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + - n_channels * sizeof(void *), - GFP_ATOMIC); - if (!creq) { - err = -ENOMEM; - goto out; - } - - creq->wiphy = wiphy; - creq->dev = dev; - /* SSIDs come after channels */ - creq->ssids = (void *)&creq->channels[n_channels]; - creq->n_channels = n_channels; - creq->n_ssids = 1; - - /* translate "Scan on frequencies" request */ - i = 0; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - int j; - - if (!wiphy->bands[band]) - continue; - - for (j = 0; j < wiphy->bands[band]->n_channels; j++) { - /* ignore disabled channels */ - if (wiphy->bands[band]->channels[j].flags & - IEEE80211_CHAN_DISABLED) - continue; - - /* If we have a wireless request structure and the - * wireless request specifies frequencies, then search - * for the matching hardware channel. - */ - if (wreq && wreq->num_channels) { - int k; - int wiphy_freq = wiphy->bands[band]->channels[j].center_freq; - for (k = 0; k < wreq->num_channels; k++) { - int wext_freq = cfg80211_wext_freq(wiphy, &wreq->channel_list[k]); - if (wext_freq == wiphy_freq) - goto wext_freq_found; - } - goto wext_freq_not_found; - } - - wext_freq_found: - creq->channels[i] = &wiphy->bands[band]->channels[j]; - i++; - wext_freq_not_found: ; - } - } - /* No channels found? */ - if (!i) { - err = -EINVAL; - goto out; - } - - /* Set real number of channels specified in creq->channels[] */ - creq->n_channels = i; - - /* translate "Scan for SSID" request */ - if (wreq) { - if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { - if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) { - err = -EINVAL; - goto out; - } - memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len); - creq->ssids[0].ssid_len = wreq->essid_len; - } - if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE) - creq->n_ssids = 0; - } - - for (i = 0; i < IEEE80211_NUM_BANDS; i++) - if (wiphy->bands[i]) - creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; - - rdev->scan_req = creq; - err = rdev->ops->scan(wiphy, dev, creq); - if (err) { - rdev->scan_req = NULL; - /* creq will be freed below */ - } else { - nl80211_send_scan_start(rdev, dev); - /* creq now owned by driver */ - creq = NULL; - dev_hold(dev); - } - out: - kfree(creq); - cfg80211_unlock_rdev(rdev); - return err; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan); - -static void ieee80211_scan_add_ies(struct iw_request_info *info, - struct cfg80211_bss *bss, - char **current_ev, char *end_buf) -{ - u8 *pos, *end, *next; - struct iw_event iwe; - - if (!bss->information_elements || - !bss->len_information_elements) - return; - - /* - * If needed, fragment the IEs buffer (at IE boundaries) into short - * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. - */ - pos = bss->information_elements; - end = pos + bss->len_information_elements; - - while (end - pos > IW_GENERIC_IE_MAX) { - next = pos + 2 + pos[1]; - while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) - next = next + 2 + next[1]; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = next - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - - pos = next; - } - - if (end > pos) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVGENIE; - iwe.u.data.length = end - pos; - *current_ev = iwe_stream_add_point(info, *current_ev, - end_buf, &iwe, pos); - } -} - -static inline unsigned int elapsed_jiffies_msecs(unsigned long start) -{ - unsigned long end = jiffies; - - if (end >= start) - return jiffies_to_msecs(end - start); - - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); -} - -static char * -ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, - struct cfg80211_internal_bss *bss, char *current_ev, - char *end_buf) -{ - struct iw_event iwe; - u8 *buf, *cfg, *p; - u8 *ie = bss->pub.information_elements; - int rem = bss->pub.len_information_elements, i, sig; - bool ismesh = false; - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_ADDR_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq); - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = bss->pub.channel->center_freq; - iwe.u.freq.e = 6; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, - IW_EV_FREQ_LEN); - - if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED | - IW_QUAL_NOISE_INVALID | - IW_QUAL_QUAL_UPDATED; - switch (wiphy->signal_type) { - case CFG80211_SIGNAL_TYPE_MBM: - sig = bss->pub.signal / 100; - iwe.u.qual.level = sig; - iwe.u.qual.updated |= IW_QUAL_DBM; - if (sig < -110) /* rather bad */ - sig = -110; - else if (sig > -40) /* perfect */ - sig = -40; - /* will give a range of 0 .. 70 */ - iwe.u.qual.qual = sig + 110; - break; - case CFG80211_SIGNAL_TYPE_UNSPEC: - iwe.u.qual.level = bss->pub.signal; - /* will give range 0 .. 100 */ - iwe.u.qual.qual = bss->pub.signal; - break; - default: - /* not reached */ - break; - } - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - } - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWENCODE; - if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ""); - - while (rem >= 2) { - /* invalid data */ - if (ie[1] > rem - 2) - break; - - switch (ie[0]) { - case WLAN_EID_SSID: - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = ie[1]; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie + 2); - break; - case WLAN_EID_MESH_ID: - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWESSID; - iwe.u.data.length = ie[1]; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie + 2); - break; - case WLAN_EID_MESH_CONFIG: - ismesh = true; - if (ie[1] != sizeof(struct ieee80211_meshconf_ie)) - break; - buf = kmalloc(50, GFP_ATOMIC); - if (!buf) - break; - cfg = ie + 2; - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "Mesh Network Path Selection Protocol ID: " - "0x%02X", cfg[0]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Path Selection Metric ID: 0x%02X", - cfg[1]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Congestion Control Mode ID: 0x%02X", - cfg[2]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Synchronization ID: 0x%02X", cfg[3]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Authentication ID: 0x%02X", cfg[4]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Formation Info: 0x%02X", cfg[5]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - sprintf(buf, "Capabilities: 0x%02X", cfg[6]); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, - &iwe, buf); - kfree(buf); - break; - case WLAN_EID_SUPP_RATES: - case WLAN_EID_EXT_SUPP_RATES: - /* display all supported rates in readable format */ - p = current_ev + iwe_stream_lcp_len(info); - - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - for (i = 0; i < ie[1]; i++) { - iwe.u.bitrate.value = - ((ie[i + 2] & 0x7f) * 500000); - p = iwe_stream_add_value(info, current_ev, p, - end_buf, &iwe, IW_EV_PARAM_LEN); - } - current_ev = p; - break; - } - rem -= ie[1] + 2; - ie += ie[1] + 2; - } - - if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) || - ismesh) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = SIOCGIWMODE; - if (ismesh) - iwe.u.mode = IW_MODE_MESH; - else if (bss->pub.capability & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - buf = kmalloc(30, GFP_ATOMIC); - if (buf) { - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, buf); - memset(&iwe, 0, sizeof(iwe)); - iwe.cmd = IWEVCUSTOM; - sprintf(buf, " Last beacon: %ums ago", - elapsed_jiffies_msecs(bss->ts)); - iwe.u.data.length = strlen(buf); - current_ev = iwe_stream_add_point(info, current_ev, - end_buf, &iwe, buf); - kfree(buf); - } - - ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf); - - return current_ev; -} - - -static int ieee80211_scan_results(struct cfg80211_registered_device *dev, - struct iw_request_info *info, - char *buf, size_t len) -{ - char *current_ev = buf; - char *end_buf = buf + len; - struct cfg80211_internal_bss *bss; - - spin_lock_bh(&dev->bss_lock); - cfg80211_bss_expire(dev); - - list_for_each_entry(bss, &dev->bss_list, list) { - if (buf + len - current_ev <= IW_EV_ADDR_LEN) { - spin_unlock_bh(&dev->bss_lock); - return -E2BIG; - } - current_ev = ieee80211_bss(&dev->wiphy, info, bss, - current_ev, end_buf); - } - spin_unlock_bh(&dev->bss_lock); - return current_ev - buf; -} - - -int cfg80211_wext_giwscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct cfg80211_registered_device *rdev; - int res; - - if (!netif_running(dev)) - return -ENETDOWN; - - rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); - - if (IS_ERR(rdev)) - return PTR_ERR(rdev); - - if (rdev->scan_req) { - res = -EAGAIN; - goto out; - } - - res = ieee80211_scan_results(rdev, info, extra, data->length); - data->length = 0; - if (res >= 0) { - data->length = res; - res = 0; - } - - out: - cfg80211_unlock_rdev(rdev); - return res; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwscan); -#endif diff --git a/net/wireless_ath/sme.c b/net/wireless_ath/sme.c deleted file mode 100755 index 38d3248..0000000 --- a/net/wireless_ath/sme.c +++ /dev/null @@ -1,1041 +0,0 @@ -/* - * SME code for cfg80211's connect emulation. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2009 Intel Corporation. All rights reserved. - */ - -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/slab.h> -#include <linux/workqueue.h> -#include <linux/wireless.h> -#include <linux/export.h> -#include <net/iw_handler.h> -#include <net/cfg80211.h> -#include <net/rtnetlink.h> -#include "nl80211.h" -#include "reg.h" - -struct cfg80211_conn { - struct cfg80211_connect_params params; - /* these are sub-states of the _CONNECTING sme_state */ - enum { - CFG80211_CONN_IDLE, - CFG80211_CONN_SCANNING, - CFG80211_CONN_SCAN_AGAIN, - CFG80211_CONN_AUTHENTICATE_NEXT, - CFG80211_CONN_AUTHENTICATING, - CFG80211_CONN_ASSOCIATE_NEXT, - CFG80211_CONN_ASSOCIATING, - CFG80211_CONN_DEAUTH_ASSOC_FAIL, - } state; - u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; - u8 *ie; - size_t ie_len; - bool auto_auth, prev_bssid_valid; -}; - -static bool cfg80211_is_all_idle(void) -{ - struct cfg80211_registered_device *rdev; - struct wireless_dev *wdev; - bool is_all_idle = true; - - mutex_lock(&cfg80211_mutex); - - /* - * All devices must be idle as otherwise if you are actively - * scanning some new beacon hints could be learned and would - * count as new regulatory hints. - */ - list_for_each_entry(rdev, &cfg80211_rdev_list, list) { - cfg80211_lock_rdev(rdev); - list_for_each_entry(wdev, &rdev->netdev_list, list) { - wdev_lock(wdev); - if (wdev->sme_state != CFG80211_SME_IDLE) - is_all_idle = false; - wdev_unlock(wdev); - } - cfg80211_unlock_rdev(rdev); - } - - mutex_unlock(&cfg80211_mutex); - - return is_all_idle; -} - -static void disconnect_work(struct work_struct *work) -{ - if (!cfg80211_is_all_idle()) - return; - - regulatory_hint_disconnect(); -} - -static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); - -static int cfg80211_conn_scan(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_scan_request *request; - int n_channels, err; - - ASSERT_RTNL(); - ASSERT_RDEV_LOCK(rdev); - ASSERT_WDEV_LOCK(wdev); - - if (rdev->scan_req) - return -EBUSY; - - if (wdev->conn->params.channel) { - n_channels = 1; - } else { - enum ieee80211_band band; - n_channels = 0; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wdev->wiphy->bands[band]) - continue; - n_channels += wdev->wiphy->bands[band]->n_channels; - } - } - request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + - sizeof(request->channels[0]) * n_channels, - GFP_KERNEL); - if (!request) - return -ENOMEM; - - if (wdev->conn->params.channel) - request->channels[0] = wdev->conn->params.channel; - else { - int i = 0, j; - enum ieee80211_band band; - struct ieee80211_supported_band *bands; - struct ieee80211_channel *channel; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - bands = wdev->wiphy->bands[band]; - if (!bands) - continue; - for (j = 0; j < bands->n_channels; j++) { - channel = &bands->channels[j]; - if (channel->flags & IEEE80211_CHAN_DISABLED) - continue; - request->channels[i++] = channel; - } - request->rates[band] = (1 << bands->n_bitrates) - 1; - } - n_channels = i; - } - request->n_channels = n_channels; - request->ssids = (void *)&request->channels[n_channels]; - request->n_ssids = 1; - - memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, - wdev->conn->params.ssid_len); - request->ssids[0].ssid_len = wdev->conn->params.ssid_len; - - request->dev = wdev->netdev; - request->wiphy = &rdev->wiphy; - - rdev->scan_req = request; - - err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); - if (!err) { - wdev->conn->state = CFG80211_CONN_SCANNING; - nl80211_send_scan_start(rdev, wdev->netdev); - dev_hold(wdev->netdev); - } else { - rdev->scan_req = NULL; - kfree(request); - } - return err; -} - -static int cfg80211_conn_do_work(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_connect_params *params; - const u8 *prev_bssid = NULL; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->conn) - return 0; - - params = &wdev->conn->params; - - switch (wdev->conn->state) { - case CFG80211_CONN_SCAN_AGAIN: - return cfg80211_conn_scan(wdev); - case CFG80211_CONN_AUTHENTICATE_NEXT: - BUG_ON(!rdev->ops->auth); - wdev->conn->state = CFG80211_CONN_AUTHENTICATING; - return __cfg80211_mlme_auth(rdev, wdev->netdev, - params->channel, params->auth_type, - params->bssid, - params->ssid, params->ssid_len, - NULL, 0, - params->key, params->key_len, - params->key_idx, false); - case CFG80211_CONN_ASSOCIATE_NEXT: - BUG_ON(!rdev->ops->assoc); - wdev->conn->state = CFG80211_CONN_ASSOCIATING; - if (wdev->conn->prev_bssid_valid) - prev_bssid = wdev->conn->prev_bssid; - err = __cfg80211_mlme_assoc(rdev, wdev->netdev, - params->channel, params->bssid, - prev_bssid, - params->ssid, params->ssid_len, - params->ie, params->ie_len, - false, ¶ms->crypto); - if (err) - __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, - NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, - false); - return err; - case CFG80211_CONN_DEAUTH_ASSOC_FAIL: - __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, - NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, false); - /* return an error so that we call __cfg80211_connect_result() */ - return -EINVAL; - default: - return 0; - } -} - -void cfg80211_conn_work(struct work_struct *work) -{ - struct cfg80211_registered_device *rdev = - container_of(work, struct cfg80211_registered_device, conn_work); - struct wireless_dev *wdev; - u8 bssid_buf[ETH_ALEN], *bssid = NULL; - - rtnl_lock(); - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - wdev_lock(wdev); - if (!netif_running(wdev->netdev)) { - wdev_unlock(wdev); - continue; - } - if (wdev->sme_state != CFG80211_SME_CONNECTING) { - wdev_unlock(wdev); - continue; - } - if (wdev->conn->params.bssid) { - memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); - bssid = bssid_buf; - } - if (cfg80211_conn_do_work(wdev)) - __cfg80211_connect_result( - wdev->netdev, bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - wdev_unlock(wdev); - } - - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - rtnl_unlock(); -} - -static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_bss *bss; - u16 capa = WLAN_CAPABILITY_ESS; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->conn->params.privacy) - capa |= WLAN_CAPABILITY_PRIVACY; - - bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, - wdev->conn->params.bssid, - wdev->conn->params.ssid, - wdev->conn->params.ssid_len, - WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, - capa); - if (!bss) - return NULL; - - memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); - wdev->conn->params.bssid = wdev->conn->bssid; - wdev->conn->params.channel = bss->channel; - wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - schedule_work(&rdev->conn_work); - - return bss; -} - -static void __cfg80211_sme_scan_done(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_bss *bss; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - - if (!wdev->conn) - return; - - if (wdev->conn->state != CFG80211_CONN_SCANNING && - wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) - return; - - bss = cfg80211_get_conn_bss(wdev); - if (bss) { - cfg80211_put_bss(bss); - } else { - /* not found */ - if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) - schedule_work(&rdev->conn_work); - else - __cfg80211_connect_result( - wdev->netdev, - wdev->conn->params.bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - false, NULL); - } -} - -void cfg80211_sme_scan_done(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); - wdev_lock(wdev); - __cfg80211_sme_scan_done(dev); - wdev_unlock(wdev); - mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); -} - -void cfg80211_sme_rx_auth(struct net_device *dev, - const u8 *buf, size_t len) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; - u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); - - ASSERT_WDEV_LOCK(wdev); - - /* should only RX auth frames when connecting */ - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - - if (WARN_ON(!wdev->conn)) - return; - - if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && - wdev->conn->auto_auth && - wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { - /* select automatically between only open, shared, leap */ - switch (wdev->conn->params.auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - if (wdev->connect_keys) - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_SHARED_KEY; - else - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_NETWORK_EAP; - break; - case NL80211_AUTHTYPE_SHARED_KEY: - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_NETWORK_EAP; - break; - default: - /* huh? */ - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_OPEN_SYSTEM; - break; - } - wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - schedule_work(&rdev->conn_work); - } else if (status_code != WLAN_STATUS_SUCCESS) { - __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, - status_code, false, NULL); - } else if (wdev->sme_state == CFG80211_SME_CONNECTING && - wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { - wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; - schedule_work(&rdev->conn_work); - } -} - -bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (WARN_ON(!wdev->conn)) - return false; - - if (!wdev->conn->prev_bssid_valid) - return false; - - /* - * Some stupid APs don't accept reassoc, so we - * need to fall back to trying regular assoc. - */ - wdev->conn->prev_bssid_valid = false; - wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; - schedule_work(&rdev->conn_work); - - return true; -} - -void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) -{ - struct wiphy *wiphy = wdev->wiphy; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; - schedule_work(&rdev->conn_work); -} - -void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, bool wextev, - struct cfg80211_bss *bss) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - u8 *country_ie; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - ASSERT_WDEV_LOCK(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) - return; - - if (wdev->sme_state != CFG80211_SME_CONNECTING) - return; - - nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, - bssid, req_ie, req_ie_len, - resp_ie, resp_ie_len, - status, GFP_KERNEL); - -#ifdef CONFIG_CFG80211_WEXT - if (wextev) { - if (req_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); - } - - if (resp_ie && status == WLAN_STATUS_SUCCESS) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); - } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - if (bssid && status == WLAN_STATUS_SUCCESS) { - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); - wdev->wext.prev_bssid_valid = true; - } - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); - } -#endif - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - } - - if (wdev->conn) - wdev->conn->state = CFG80211_CONN_IDLE; - - if (status != WLAN_STATUS_SUCCESS) { - wdev->sme_state = CFG80211_SME_IDLE; - if (wdev->conn) - kfree(wdev->conn->ie); - kfree(wdev->conn); - wdev->conn = NULL; - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - wdev->ssid_len = 0; - return; - } - - if (!bss) - bss = cfg80211_get_bss(wdev->wiphy, - wdev->conn ? wdev->conn->params.channel : - NULL, - bssid, - wdev->ssid, wdev->ssid_len, - WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - - if (WARN_ON(!bss)) - return; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - wdev->sme_state = CFG80211_SME_CONNECTED; - cfg80211_upload_connect_keys(wdev); - - country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); - - if (!country_ie) - return; - - /* - * ieee80211_bss_get_ie() ensures we can access: - * - country_ie + 2, the start of the country ie data, and - * - and country_ie[1] which is the IE length - */ - regulatory_hint_11d(wdev->wiphy, - bss->channel->band, - country_ie + 2, - country_ie[1]); -} - -void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, - u16 status, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); - - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); - if (!ev) - return; - - ev->type = EVENT_CONNECT_RESULT; - if (bssid) - memcpy(ev->cr.bssid, bssid, ETH_ALEN); - if (req_ie_len) { - ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->cr.req_ie_len = req_ie_len; - memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); - } - if (resp_ie_len) { - ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->cr.resp_ie_len = resp_ie_len; - memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); - } - ev->cr.status = status; - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_connect_result); - -void __cfg80211_roamed(struct wireless_dev *wdev, - struct cfg80211_bss *bss, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len) -{ -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - ASSERT_WDEV_LOCK(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) - goto out; - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - goto out; - - /* internal error -- how did we get to CONNECTED w/o BSS? */ - if (WARN_ON(!wdev->current_bss)) { - goto out; - } - - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - wdev->current_bss = NULL; - - cfg80211_hold_bss(bss_from_pub(bss)); - wdev->current_bss = bss_from_pub(bss); - - nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, - req_ie, req_ie_len, resp_ie, resp_ie_len, - GFP_KERNEL); - -#ifdef CONFIG_CFG80211_WEXT - if (req_ie) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = req_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCREQIE, - &wrqu, req_ie); - } - - if (resp_ie) { - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.data.length = resp_ie_len; - wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, - &wrqu, resp_ie); - } - - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); - memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); - wdev->wext.prev_bssid_valid = true; - wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); -#endif - - return; -out: - cfg80211_put_bss(bss); -} - -void cfg80211_roamed(struct net_device *dev, - struct ieee80211_channel *channel, - const u8 *bssid, - const u8 *req_ie, size_t req_ie_len, - const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); - - bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, - wdev->ssid_len, WLAN_CAPABILITY_ESS, - WLAN_CAPABILITY_ESS); - if (WARN_ON(!bss)) - return; - - cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, - resp_ie_len, gfp); -} -EXPORT_SYMBOL(cfg80211_roamed); - -void cfg80211_roamed_bss(struct net_device *dev, - struct cfg80211_bss *bss, const u8 *req_ie, - size_t req_ie_len, const u8 *resp_ie, - size_t resp_ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); - - if (WARN_ON(!bss)) - return; - - ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); - if (!ev) { - cfg80211_put_bss(bss); - return; - } - - ev->type = EVENT_ROAMED; - ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); - ev->rm.req_ie_len = req_ie_len; - memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); - ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; - ev->rm.resp_ie_len = resp_ie_len; - memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); - ev->rm.bss = bss; - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_roamed_bss); - -void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, - size_t ie_len, u16 reason, bool from_ap) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int i; -#ifdef CONFIG_CFG80211_WEXT - union iwreq_data wrqu; -#endif - - ASSERT_WDEV_LOCK(wdev); - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) - return; - - if (wdev->sme_state != CFG80211_SME_CONNECTED) - return; - - if (wdev->current_bss) { - cfg80211_unhold_bss(wdev->current_bss); - cfg80211_put_bss(&wdev->current_bss->pub); - } - - wdev->current_bss = NULL; - wdev->sme_state = CFG80211_SME_IDLE; - wdev->ssid_len = 0; - - if (wdev->conn) { - const u8 *bssid; - int ret; - - kfree(wdev->conn->ie); - wdev->conn->ie = NULL; - kfree(wdev->conn); - wdev->conn = NULL; - - /* - * If this disconnect was due to a disassoc, we - * we might still have an auth BSS around. For - * the userspace SME that's currently expected, - * but for the kernel SME (nl80211 CONNECT or - * wireless extensions) we want to clear up all - * state. - */ - for (i = 0; i < MAX_AUTH_BSSES; i++) { - if (!wdev->auth_bsses[i]) - continue; - bssid = wdev->auth_bsses[i]->pub.bssid; - ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, - WLAN_REASON_DEAUTH_LEAVING, - false); - WARN(ret, "deauth failed: %d\n", ret); - } - } - - nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); - - /* - * Delete all the keys ... pairwise keys can't really - * exist any more anyway, but default keys might. - */ - if (rdev->ops->del_key) - for (i = 0; i < 6; i++) - rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); - -#ifdef CONFIG_CFG80211_WEXT - memset(&wrqu, 0, sizeof(wrqu)); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); - wdev->wext.connect.ssid_len = 0; -#endif - - schedule_work(&cfg80211_disconnect_work); -} - -void cfg80211_disconnected(struct net_device *dev, u16 reason, - u8 *ie, size_t ie_len, gfp_t gfp) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_event *ev; - unsigned long flags; - - CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); - - ev = kzalloc(sizeof(*ev) + ie_len, gfp); - if (!ev) - return; - - ev->type = EVENT_DISCONNECTED; - ev->dc.ie = ((u8 *)ev) + sizeof(*ev); - ev->dc.ie_len = ie_len; - memcpy((void *)ev->dc.ie, ie, ie_len); - ev->dc.reason = reason; - - spin_lock_irqsave(&wdev->event_lock, flags); - list_add_tail(&ev->list, &wdev->event_list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - queue_work(cfg80211_wq, &rdev->event_work); -} -EXPORT_SYMBOL(cfg80211_disconnected); - -int __cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys, - const u8 *prev_bssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss = NULL; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state != CFG80211_SME_IDLE) - return -EALREADY; - - if (WARN_ON(wdev->connect_keys)) { - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - } - - if (connkeys && connkeys->def >= 0) { - int idx; - u32 cipher; - - idx = connkeys->def; - cipher = connkeys->params[idx].cipher; - /* If given a WEP key we may need it for shared key auth */ - if (cipher == WLAN_CIPHER_SUITE_WEP40 || - cipher == WLAN_CIPHER_SUITE_WEP104) { - connect->key_idx = idx; - connect->key = connkeys->params[idx].key; - connect->key_len = connkeys->params[idx].key_len; - - /* - * If ciphers are not set (e.g. when going through - * iwconfig), we have to set them appropriately here. - */ - if (connect->crypto.cipher_group == 0) - connect->crypto.cipher_group = cipher; - - if (connect->crypto.n_ciphers_pairwise == 0) { - connect->crypto.n_ciphers_pairwise = 1; - connect->crypto.ciphers_pairwise[0] = cipher; - } - } - } - - if (!rdev->ops->connect) { - if (!rdev->ops->auth || !rdev->ops->assoc) - return -EOPNOTSUPP; - - if (WARN_ON(wdev->conn)) - return -EINPROGRESS; - - wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); - if (!wdev->conn) - return -ENOMEM; - - /* - * Copy all parameters, and treat explicitly IEs, BSSID, SSID. - */ - memcpy(&wdev->conn->params, connect, sizeof(*connect)); - if (connect->bssid) { - wdev->conn->params.bssid = wdev->conn->bssid; - memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); - } - - if (connect->ie) { - wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, - GFP_KERNEL); - wdev->conn->params.ie = wdev->conn->ie; - if (!wdev->conn->ie) { - kfree(wdev->conn); - wdev->conn = NULL; - return -ENOMEM; - } - } - - if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { - wdev->conn->auto_auth = true; - /* start with open system ... should mostly work */ - wdev->conn->params.auth_type = - NL80211_AUTHTYPE_OPEN_SYSTEM; - } else { - wdev->conn->auto_auth = false; - } - - memcpy(wdev->ssid, connect->ssid, connect->ssid_len); - wdev->ssid_len = connect->ssid_len; - wdev->conn->params.ssid = wdev->ssid; - wdev->conn->params.ssid_len = connect->ssid_len; - - /* see if we have the bss already */ - bss = cfg80211_get_conn_bss(wdev); - - wdev->sme_state = CFG80211_SME_CONNECTING; - wdev->connect_keys = connkeys; - - if (prev_bssid) { - memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); - wdev->conn->prev_bssid_valid = true; - } - - /* we're good if we have a matching bss struct */ - if (bss) { - wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; - err = cfg80211_conn_do_work(wdev); - cfg80211_put_bss(bss); - } else { - /* otherwise we'll need to scan for the AP first */ - err = cfg80211_conn_scan(wdev); - /* - * If we can't scan right now, then we need to scan again - * after the current scan finished, since the parameters - * changed (unless we find a good AP anyway). - */ - if (err == -EBUSY) { - err = 0; - wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; - } - } - if (err) { - kfree(wdev->conn->ie); - kfree(wdev->conn); - wdev->conn = NULL; - wdev->sme_state = CFG80211_SME_IDLE; - wdev->connect_keys = NULL; - wdev->ssid_len = 0; - } - - return err; - } else { - wdev->sme_state = CFG80211_SME_CONNECTING; - wdev->connect_keys = connkeys; - err = rdev->ops->connect(&rdev->wiphy, dev, connect); - if (err) { - wdev->connect_keys = NULL; - wdev->sme_state = CFG80211_SME_IDLE; - return err; - } - - memcpy(wdev->ssid, connect->ssid, connect->ssid_len); - wdev->ssid_len = connect->ssid_len; - - return 0; - } -} - -int cfg80211_connect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - struct cfg80211_connect_params *connect, - struct cfg80211_cached_keys *connkeys) -{ - int err; - - mutex_lock(&rdev->devlist_mtx); - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); - wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, u16 reason, bool wextev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err; - - ASSERT_WDEV_LOCK(wdev); - - if (wdev->sme_state == CFG80211_SME_IDLE) - return -EINVAL; - - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; - - if (!rdev->ops->disconnect) { - if (!rdev->ops->deauth) - return -EOPNOTSUPP; - - /* was it connected by userspace SME? */ - if (!wdev->conn) { - cfg80211_mlme_down(rdev, dev); - return 0; - } - - if (wdev->sme_state == CFG80211_SME_CONNECTING && - (wdev->conn->state == CFG80211_CONN_SCANNING || - wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { - wdev->sme_state = CFG80211_SME_IDLE; - kfree(wdev->conn->ie); - kfree(wdev->conn); - wdev->conn = NULL; - wdev->ssid_len = 0; - return 0; - } - - /* wdev->conn->params.bssid must be set if > SCANNING */ - err = __cfg80211_mlme_deauth(rdev, dev, - wdev->conn->params.bssid, - NULL, 0, reason, false); - if (err) - return err; - } else { - err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); - if (err) - return err; - } - - if (wdev->sme_state == CFG80211_SME_CONNECTED) - __cfg80211_disconnected(dev, NULL, 0, 0, false); - else if (wdev->sme_state == CFG80211_SME_CONNECTING) - __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - wextev, NULL); - - return 0; -} - -int cfg80211_disconnect(struct cfg80211_registered_device *rdev, - struct net_device *dev, - u16 reason, bool wextev) -{ - int err; - - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_disconnect(rdev, dev, reason, wextev); - wdev_unlock(dev->ieee80211_ptr); - - return err; -} - -void cfg80211_sme_disassoc(struct net_device *dev, int idx) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 bssid[ETH_ALEN]; - - ASSERT_WDEV_LOCK(wdev); - - if (!wdev->conn) - return; - - if (wdev->conn->state == CFG80211_CONN_IDLE) - return; - - /* - * Ok, so the association was made by this SME -- we don't - * want it any more so deauthenticate too. - */ - - if (!wdev->auth_bsses[idx]) - return; - - memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); - if (__cfg80211_mlme_deauth(rdev, dev, bssid, - NULL, 0, WLAN_REASON_DEAUTH_LEAVING, - false)) { - /* whatever -- assume gone anyway */ - cfg80211_unhold_bss(wdev->auth_bsses[idx]); - cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); - wdev->auth_bsses[idx] = NULL; - } -} diff --git a/net/wireless_ath/sysfs.c b/net/wireless_ath/sysfs.c deleted file mode 100755 index 2f8af6e..0000000 --- a/net/wireless_ath/sysfs.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This file provides /sys/class/ieee80211/<wiphy name>/ - * and some default attributes. - * - * Copyright 2005-2006 Jiri Benc <jbenc@suse.cz> - * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> - * - * This file is GPLv2 as found in COPYING. - */ - -#include <linux/device.h> -#include <linux/module.h> -#include <linux/netdevice.h> -#include <linux/nl80211.h> -#include <linux/rtnetlink.h> -#include <net/cfg80211.h> -#include "sysfs.h" -#include "core.h" - -static inline struct cfg80211_registered_device *dev_to_rdev( - struct device *dev) -{ - return container_of(dev, struct cfg80211_registered_device, wiphy.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); \ -} - -SHOW_FMT(index, "%d", wiphy_idx); -SHOW_FMT(macaddress, "%pM", wiphy.perm_addr); -SHOW_FMT(address_mask, "%pM", wiphy.addr_mask); - -static ssize_t name_show(struct device *dev, - struct device_attribute *attr, - char *buf) { - struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; - return sprintf(buf, "%s\n", dev_name(&wiphy->dev)); -} - - -static ssize_t addresses_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy; - char *start = buf; - int i; - - if (!wiphy->addresses) - return sprintf(buf, "%pM\n", wiphy->perm_addr); - - for (i = 0; i < wiphy->n_addresses; i++) - buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr); - - return buf - start; -} - -static struct device_attribute ieee80211_dev_attrs[] = { - __ATTR_RO(index), - __ATTR_RO(macaddress), - __ATTR_RO(address_mask), - __ATTR_RO(addresses), - __ATTR_RO(name), - {} -}; - -static void wiphy_dev_release(struct device *dev) -{ - struct cfg80211_registered_device *rdev = dev_to_rdev(dev); - - cfg80211_dev_free(rdev); -} - -#ifdef CONFIG_HOTPLUG -static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - /* TODO, we probably need stuff here */ - return 0; -} -#endif - -static int wiphy_suspend(struct device *dev, pm_message_t state) -{ - struct cfg80211_registered_device *rdev = dev_to_rdev(dev); - int ret = 0; - - rdev->suspend_at = get_seconds(); - - if (rdev->ops->suspend) { - rtnl_lock(); - if (rdev->wiphy.registered) - ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan); - rtnl_unlock(); - } - - return ret; -} - -static int wiphy_resume(struct device *dev) -{ - struct cfg80211_registered_device *rdev = dev_to_rdev(dev); - int ret = 0; - - /* Age scan results with time spent in suspend */ - spin_lock_bh(&rdev->bss_lock); - cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at); - spin_unlock_bh(&rdev->bss_lock); - - if (rdev->ops->resume) { - rtnl_lock(); - if (rdev->wiphy.registered) - ret = rdev->ops->resume(&rdev->wiphy); - rtnl_unlock(); - } - - return ret; -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) -static const void *wiphy_namespace(struct device *d) -{ - struct wiphy *wiphy = container_of(d, struct wiphy, dev); - - return wiphy_net(wiphy); -} -#endif - -struct class ieee80211_class = { - .name = "ieee80211", - .owner = THIS_MODULE, - .dev_release = wiphy_dev_release, - .dev_attrs = ieee80211_dev_attrs, -#ifdef CONFIG_HOTPLUG - .dev_uevent = wiphy_uevent, -#endif - .suspend = wiphy_suspend, - .resume = wiphy_resume, -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35)) - .ns_type = &net_ns_type_operations, - .namespace = wiphy_namespace, -#endif -}; - -int wiphy_sysfs_init(void) -{ - return class_register(&ieee80211_class); -} - -void wiphy_sysfs_exit(void) -{ - class_unregister(&ieee80211_class); -} diff --git a/net/wireless_ath/sysfs.h b/net/wireless_ath/sysfs.h deleted file mode 100755 index 65acbeb..0000000 --- a/net/wireless_ath/sysfs.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __WIRELESS_SYSFS_H -#define __WIRELESS_SYSFS_H - -extern int wiphy_sysfs_init(void); -extern void wiphy_sysfs_exit(void); - -extern struct class ieee80211_class; - -#endif /* __WIRELESS_SYSFS_H */ diff --git a/net/wireless_ath/util.c b/net/wireless_ath/util.c deleted file mode 100755 index a7c53d9..0000000 --- a/net/wireless_ath/util.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * Wireless utility functions - * - * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net> - */ -#include <linux/export.h> -#include <linux/bitops.h> -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <linux/crc32.h> -#include <net/cfg80211.h> -#include <net/ip.h> -#include "core.h" - -struct ieee80211_rate * -ieee80211_get_response_rate(struct ieee80211_supported_band *sband, - u32 basic_rates, int bitrate) -{ - struct ieee80211_rate *result = &sband->bitrates[0]; - int i; - - for (i = 0; i < sband->n_bitrates; i++) { - if (!(basic_rates & BIT(i))) - continue; - if (sband->bitrates[i].bitrate > bitrate) - continue; - result = &sband->bitrates[i]; - } - - return result; -} -EXPORT_SYMBOL(ieee80211_get_response_rate); - -int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band) -{ - /* see 802.11 17.3.8.3.2 and Annex J - * there are overlapping channel numbers in 5GHz and 2GHz bands */ - if (band == IEEE80211_BAND_5GHZ) { - if (chan >= 182 && chan <= 196) - return 4000 + chan * 5; - else - return 5000 + chan * 5; - } else { /* IEEE80211_BAND_2GHZ */ - if (chan == 14) - return 2484; - else if (chan < 14) - return 2407 + chan * 5; - else - return 0; /* not supported */ - } -} -EXPORT_SYMBOL(ieee80211_channel_to_frequency); - -int ieee80211_frequency_to_channel(int freq) -{ - /* see 802.11 17.3.8.3.2 and Annex J */ - if (freq == 2484) - return 14; - else if (freq < 2484) - return (freq - 2407) / 5; - else if (freq >= 4910 && freq <= 4980) - return (freq - 4000) / 5; - else - return (freq - 5000) / 5; -} -EXPORT_SYMBOL(ieee80211_frequency_to_channel); - -struct ieee80211_channel *__ieee80211_get_channel(struct wiphy *wiphy, - int freq) -{ - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - int i; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wiphy->bands[band]; - - if (!sband) - continue; - - for (i = 0; i < sband->n_channels; i++) { - if (sband->channels[i].center_freq == freq) - return &sband->channels[i]; - } - } - - return NULL; -} -EXPORT_SYMBOL(__ieee80211_get_channel); - -static void set_mandatory_flags_band(struct ieee80211_supported_band *sband, - enum ieee80211_band band) -{ - int i, want; - - switch (band) { - case IEEE80211_BAND_5GHZ: - want = 3; - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == 60 || - sband->bitrates[i].bitrate == 120 || - sband->bitrates[i].bitrate == 240) { - sband->bitrates[i].flags |= - IEEE80211_RATE_MANDATORY_A; - want--; - } - } - WARN_ON(want); - break; - case IEEE80211_BAND_2GHZ: - want = 7; - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == 10) { - sband->bitrates[i].flags |= - IEEE80211_RATE_MANDATORY_B | - IEEE80211_RATE_MANDATORY_G; - want--; - } - - if (sband->bitrates[i].bitrate == 20 || - sband->bitrates[i].bitrate == 55 || - sband->bitrates[i].bitrate == 110 || - sband->bitrates[i].bitrate == 60 || - sband->bitrates[i].bitrate == 120 || - sband->bitrates[i].bitrate == 240) { - sband->bitrates[i].flags |= - IEEE80211_RATE_MANDATORY_G; - want--; - } - - if (sband->bitrates[i].bitrate != 10 && - sband->bitrates[i].bitrate != 20 && - sband->bitrates[i].bitrate != 55 && - sband->bitrates[i].bitrate != 110) - sband->bitrates[i].flags |= - IEEE80211_RATE_ERP_G; - } - WARN_ON(want != 0 && want != 3 && want != 6); - break; - case IEEE80211_NUM_BANDS: - WARN_ON(1); - break; - } -} - -void ieee80211_set_bitrate_flags(struct wiphy *wiphy) -{ - enum ieee80211_band band; - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) - if (wiphy->bands[band]) - set_mandatory_flags_band(wiphy->bands[band], band); -} - -bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher) -{ - int i; - for (i = 0; i < wiphy->n_cipher_suites; i++) - if (cipher == wiphy->cipher_suites[i]) - return true; - return false; -} - -int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, - struct key_params *params, int key_idx, - bool pairwise, const u8 *mac_addr) -{ - if (key_idx > 5) - return -EINVAL; - - if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) - return -EINVAL; - - if (pairwise && !mac_addr) - return -EINVAL; - - /* - * Disallow pairwise keys with non-zero index unless it's WEP - * or a vendor specific cipher (because current deployments use - * pairwise WEP keys with non-zero indices and for vendor specific - * ciphers this should be validated in the driver or hardware level - * - but 802.11i clearly specifies to use zero) - */ - if (pairwise && key_idx && - ((params->cipher == WLAN_CIPHER_SUITE_TKIP) || - (params->cipher == WLAN_CIPHER_SUITE_CCMP) || - (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC))) - return -EINVAL; - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - if (params->key_len != WLAN_KEY_LEN_WEP40) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_TKIP: - if (params->key_len != WLAN_KEY_LEN_TKIP) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (params->key_len != WLAN_KEY_LEN_CCMP) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_WEP104: - if (params->key_len != WLAN_KEY_LEN_WEP104) - return -EINVAL; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - if (params->key_len != WLAN_KEY_LEN_AES_CMAC) - return -EINVAL; - break; - default: - /* - * We don't know anything about this algorithm, - * allow using it -- but the driver must check - * all parameters! We still check below whether - * or not the driver supports this algorithm, - * of course. - */ - break; - } - - if (params->seq) { - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - /* These ciphers do not use key sequence */ - return -EINVAL; - case WLAN_CIPHER_SUITE_TKIP: - case WLAN_CIPHER_SUITE_CCMP: - case WLAN_CIPHER_SUITE_AES_CMAC: - if (params->seq_len != 6) - return -EINVAL; - break; - } - } - - if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher)) - return -EINVAL; - - return 0; -} - -/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ -/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -const unsigned char rfc1042_header[] __aligned(2) = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; -EXPORT_SYMBOL(rfc1042_header); - -/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -const unsigned char bridge_tunnel_header[] __aligned(2) = - { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; -EXPORT_SYMBOL(bridge_tunnel_header); - -unsigned int __attribute_const__ ieee80211_hdrlen(__le16 fc) -{ - unsigned int hdrlen = 24; - - if (ieee80211_is_data(fc)) { - if (ieee80211_has_a4(fc)) - hdrlen = 30; - if (ieee80211_is_data_qos(fc)) { - hdrlen += IEEE80211_QOS_CTL_LEN; - if (ieee80211_has_order(fc)) - hdrlen += IEEE80211_HT_CTL_LEN; - } - goto out; - } - - if (ieee80211_is_ctl(fc)) { - /* - * ACK and CTS are 10 bytes, all others 16. To see how - * to get this condition consider - * subtype mask: 0b0000000011110000 (0x00F0) - * ACK subtype: 0b0000000011010000 (0x00D0) - * CTS subtype: 0b0000000011000000 (0x00C0) - * bits that matter: ^^^ (0x00E0) - * value of those: 0b0000000011000000 (0x00C0) - */ - if ((fc & cpu_to_le16(0x00E0)) == cpu_to_le16(0x00C0)) - hdrlen = 10; - else - hdrlen = 16; - } -out: - return hdrlen; -} -EXPORT_SYMBOL(ieee80211_hdrlen); - -unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb) -{ - const struct ieee80211_hdr *hdr = - (const struct ieee80211_hdr *)skb->data; - unsigned int hdrlen; - - if (unlikely(skb->len < 10)) - return 0; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - if (unlikely(hdrlen > skb->len)) - return 0; - return hdrlen; -} -EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb); - -static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr) -{ - int ae = meshhdr->flags & MESH_FLAGS_AE; - /* 7.1.3.5a.2 */ - switch (ae) { - case 0: - return 6; - case MESH_FLAGS_AE_A4: - return 12; - case MESH_FLAGS_AE_A5_A6: - return 18; - case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6): - return 24; - default: - return 6; - } -} - -int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 hdrlen, ethertype; - u8 *payload; - u8 dst[ETH_ALEN]; - u8 src[ETH_ALEN] __aligned(2); - - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) - return -1; - - hdrlen = ieee80211_hdrlen(hdr->frame_control); - - /* convert IEEE 802.11 header + possible LLC headers into Ethernet - * header - * IEEE 802.11 address fields: - * ToDS FromDS Addr1 Addr2 Addr3 Addr4 - * 0 0 DA SA BSSID n/a - * 0 1 DA BSSID SA n/a - * 1 0 BSSID SA DA n/a - * 1 1 RA TA DA SA - */ - memcpy(dst, ieee80211_get_DA(hdr), ETH_ALEN); - memcpy(src, ieee80211_get_SA(hdr), ETH_ALEN); - - switch (hdr->frame_control & - cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) { - case cpu_to_le16(IEEE80211_FCTL_TODS): - if (unlikely(iftype != NL80211_IFTYPE_AP && - iftype != NL80211_IFTYPE_AP_VLAN && - iftype != NL80211_IFTYPE_P2P_GO)) - return -1; - break; - case cpu_to_le16(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS): - if (unlikely(iftype != NL80211_IFTYPE_WDS && - iftype != NL80211_IFTYPE_MESH_POINT && - iftype != NL80211_IFTYPE_AP_VLAN && - iftype != NL80211_IFTYPE_STATION)) - return -1; - if (iftype == NL80211_IFTYPE_MESH_POINT) { - struct ieee80211s_hdr *meshdr = - (struct ieee80211s_hdr *) (skb->data + hdrlen); - /* make sure meshdr->flags is on the linear part */ - if (!pskb_may_pull(skb, hdrlen + 1)) - return -1; - if (meshdr->flags & MESH_FLAGS_AE_A5_A6) { - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr1), - dst, ETH_ALEN); - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr2), - src, ETH_ALEN); - } - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); - } - break; - case cpu_to_le16(IEEE80211_FCTL_FROMDS): - if ((iftype != NL80211_IFTYPE_STATION && - iftype != NL80211_IFTYPE_P2P_CLIENT && - iftype != NL80211_IFTYPE_MESH_POINT) || - (is_multicast_ether_addr(dst) && - !compare_ether_addr(src, addr))) - return -1; - if (iftype == NL80211_IFTYPE_MESH_POINT) { - struct ieee80211s_hdr *meshdr = - (struct ieee80211s_hdr *) (skb->data + hdrlen); - /* make sure meshdr->flags is on the linear part */ - if (!pskb_may_pull(skb, hdrlen + 1)) - return -1; - if (meshdr->flags & MESH_FLAGS_AE_A4) - skb_copy_bits(skb, hdrlen + - offsetof(struct ieee80211s_hdr, eaddr1), - src, ETH_ALEN); - hdrlen += ieee80211_get_mesh_hdrlen(meshdr); - } - break; - case cpu_to_le16(0): - if (iftype != NL80211_IFTYPE_ADHOC && - iftype != NL80211_IFTYPE_STATION) - return -1; - break; - } - - if (!pskb_may_pull(skb, hdrlen + 8)) - return -1; - - payload = skb->data + hdrlen; - ethertype = (payload[6] << 8) | payload[7]; - - if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - compare_ether_addr(payload, bridge_tunnel_header) == 0)) { - /* remove RFC1042 or Bridge-Tunnel encapsulation and - * replace EtherType */ - skb_pull(skb, hdrlen + 6); - memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); - } else { - struct ethhdr *ehdr; - __be16 len; - - skb_pull(skb, hdrlen); - len = htons(skb->len); - ehdr = (struct ethhdr *) skb_push(skb, sizeof(struct ethhdr)); - memcpy(ehdr->h_dest, dst, ETH_ALEN); - memcpy(ehdr->h_source, src, ETH_ALEN); - ehdr->h_proto = len; - } - return 0; -} -EXPORT_SYMBOL(ieee80211_data_to_8023); - -int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, u8 *bssid, bool qos) -{ - struct ieee80211_hdr hdr; - u16 hdrlen, ethertype; - __le16 fc; - const u8 *encaps_data; - int encaps_len, skip_header_bytes; - int nh_pos, h_pos; - int head_need; - - if (unlikely(skb->len < ETH_HLEN)) - return -EINVAL; - - nh_pos = skb_network_header(skb) - skb->data; - h_pos = skb_transport_header(skb) - skb->data; - - /* convert Ethernet header to proper 802.11 header (based on - * operation mode) */ - ethertype = (skb->data[12] << 8) | skb->data[13]; - fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); - - switch (iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); - /* DA BSSID SA */ - memcpy(hdr.addr1, skb->data, ETH_ALEN); - memcpy(hdr.addr2, addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); - hdrlen = 24; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - fc |= cpu_to_le16(IEEE80211_FCTL_TODS); - /* BSSID SA DA */ - memcpy(hdr.addr1, bssid, ETH_ALEN); - memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - hdrlen = 24; - break; - case NL80211_IFTYPE_ADHOC: - /* DA SA BSSID */ - memcpy(hdr.addr1, skb->data, ETH_ALEN); - memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(hdr.addr3, bssid, ETH_ALEN); - hdrlen = 24; - break; - default: - return -EOPNOTSUPP; - } - - if (qos) { - fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); - hdrlen += 2; - } - - hdr.frame_control = fc; - hdr.duration_id = 0; - hdr.seq_ctrl = 0; - - skip_header_bytes = ETH_HLEN; - if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { - encaps_data = bridge_tunnel_header; - encaps_len = sizeof(bridge_tunnel_header); - skip_header_bytes -= 2; - } else if (ethertype > 0x600) { - encaps_data = rfc1042_header; - encaps_len = sizeof(rfc1042_header); - skip_header_bytes -= 2; - } else { - encaps_data = NULL; - encaps_len = 0; - } - - skb_pull(skb, skip_header_bytes); - nh_pos -= skip_header_bytes; - h_pos -= skip_header_bytes; - - head_need = hdrlen + encaps_len - skb_headroom(skb); - - if (head_need > 0 || skb_cloned(skb)) { - head_need = max(head_need, 0); - if (head_need) - skb_orphan(skb); - - if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) - return -ENOMEM; - - skb->truesize += head_need; - } - - if (encaps_data) { - memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); - nh_pos += encaps_len; - h_pos += encaps_len; - } - - memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); - - nh_pos += hdrlen; - h_pos += hdrlen; - - /* Update skb pointers to various headers since this modified frame - * is going to go through Linux networking code that may potentially - * need things like pointer to IP header. */ - skb_set_mac_header(skb, 0); - skb_set_network_header(skb, nh_pos); - skb_set_transport_header(skb, h_pos); - - return 0; -} -EXPORT_SYMBOL(ieee80211_data_from_8023); - - -void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, - const u8 *addr, enum nl80211_iftype iftype, - const unsigned int extra_headroom, - bool has_80211_header) -{ - struct sk_buff *frame = NULL; - u16 ethertype; - u8 *payload; - const struct ethhdr *eth; - int remaining, err; - u8 dst[ETH_ALEN], src[ETH_ALEN]; - - if (has_80211_header) { - err = ieee80211_data_to_8023(skb, addr, iftype); - if (err) - goto out; - - /* skip the wrapping header */ - eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); - if (!eth) - goto out; - } else { - eth = (struct ethhdr *) skb->data; - } - - while (skb != frame) { - u8 padding; - __be16 len = eth->h_proto; - unsigned int subframe_len = sizeof(struct ethhdr) + ntohs(len); - - remaining = skb->len; - memcpy(dst, eth->h_dest, ETH_ALEN); - memcpy(src, eth->h_source, ETH_ALEN); - - padding = (4 - subframe_len) & 0x3; - /* the last MSDU has no padding */ - if (subframe_len > remaining) - goto purge; - - skb_pull(skb, sizeof(struct ethhdr)); - /* reuse skb for the last subframe */ - if (remaining <= subframe_len + padding) - frame = skb; - else { - unsigned int hlen = ALIGN(extra_headroom, 4); - /* - * Allocate and reserve two bytes more for payload - * alignment since sizeof(struct ethhdr) is 14. - */ - frame = dev_alloc_skb(hlen + subframe_len + 2); - if (!frame) - goto purge; - - skb_reserve(frame, hlen + sizeof(struct ethhdr) + 2); - memcpy(skb_put(frame, ntohs(len)), skb->data, - ntohs(len)); - - eth = (struct ethhdr *)skb_pull(skb, ntohs(len) + - padding); - if (!eth) { - dev_kfree_skb(frame); - goto purge; - } - } - - skb_reset_network_header(frame); - frame->dev = skb->dev; - frame->priority = skb->priority; - - payload = frame->data; - ethertype = (payload[6] << 8) | payload[7]; - - if (likely((compare_ether_addr(payload, rfc1042_header) == 0 && - ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - compare_ether_addr(payload, - bridge_tunnel_header) == 0)) { - /* remove RFC1042 or Bridge-Tunnel - * encapsulation and replace EtherType */ - skb_pull(frame, 6); - memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); - } else { - memcpy(skb_push(frame, sizeof(__be16)), &len, - sizeof(__be16)); - memcpy(skb_push(frame, ETH_ALEN), src, ETH_ALEN); - memcpy(skb_push(frame, ETH_ALEN), dst, ETH_ALEN); - } - __skb_queue_tail(list, frame); - } - - return; - - purge: - __skb_queue_purge(list); - out: - dev_kfree_skb(skb); -} -EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); - -/* Given a data frame determine the 802.1p/1d tag to use. */ -unsigned int cfg80211_classify8021d(struct sk_buff *skb) -{ - unsigned int dscp; - - /* skb->priority values from 256->263 are magic values to - * directly indicate a specific 802.1d priority. This is used - * to allow 802.1d priority to be passed directly in from VLAN - * tags, etc. - */ - if (skb->priority >= 256 && skb->priority <= 263) - return skb->priority - 256; - - switch (skb->protocol) { - case htons(ETH_P_IP): - dscp = ip_hdr(skb)->tos & 0xfc; - break; - default: - return 0; - } - - return dscp >> 5; -} -EXPORT_SYMBOL(cfg80211_classify8021d); - -const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie) -{ - u8 *end, *pos; - - pos = bss->information_elements; - if (pos == NULL) - return NULL; - end = pos + bss->len_information_elements; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} -EXPORT_SYMBOL(ieee80211_bss_get_ie); - -void cfg80211_upload_connect_keys(struct wireless_dev *wdev) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct net_device *dev = wdev->netdev; - int i; - - if (!wdev->connect_keys) - return; - - for (i = 0; i < 6; i++) { - if (!wdev->connect_keys->params[i].cipher) - continue; - if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, - &wdev->connect_keys->params[i])) { - netdev_err(dev, "failed to set key %d\n", i); - continue; - } - if (wdev->connect_keys->def == i) - if (rdev->ops->set_default_key(wdev->wiphy, dev, - i, true, true)) { - netdev_err(dev, "failed to set defkey %d\n", i); - continue; - } - if (wdev->connect_keys->defmgmt == i) - if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) - netdev_err(dev, "failed to set mgtdef %d\n", i); - } - - kfree(wdev->connect_keys); - wdev->connect_keys = NULL; -} - -static void cfg80211_process_wdev_events(struct wireless_dev *wdev) -{ - struct cfg80211_event *ev; - unsigned long flags; - const u8 *bssid = NULL; - - spin_lock_irqsave(&wdev->event_lock, flags); - while (!list_empty(&wdev->event_list)) { - ev = list_first_entry(&wdev->event_list, - struct cfg80211_event, list); - list_del(&ev->list); - spin_unlock_irqrestore(&wdev->event_lock, flags); - - wdev_lock(wdev); - switch (ev->type) { - case EVENT_CONNECT_RESULT: - if (!is_zero_ether_addr(ev->cr.bssid)) - bssid = ev->cr.bssid; - __cfg80211_connect_result( - wdev->netdev, bssid, - ev->cr.req_ie, ev->cr.req_ie_len, - ev->cr.resp_ie, ev->cr.resp_ie_len, - ev->cr.status, - ev->cr.status == WLAN_STATUS_SUCCESS, - NULL); - break; - case EVENT_ROAMED: - __cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie, - ev->rm.req_ie_len, ev->rm.resp_ie, - ev->rm.resp_ie_len); - break; - case EVENT_DISCONNECTED: - __cfg80211_disconnected(wdev->netdev, - ev->dc.ie, ev->dc.ie_len, - ev->dc.reason, true); - break; - case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid); - break; - } - wdev_unlock(wdev); - - kfree(ev); - - spin_lock_irqsave(&wdev->event_lock, flags); - } - spin_unlock_irqrestore(&wdev->event_lock, flags); -} - -void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev) -{ - struct wireless_dev *wdev; - - ASSERT_RTNL(); - ASSERT_RDEV_LOCK(rdev); - - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) - cfg80211_process_wdev_events(wdev); - - mutex_unlock(&rdev->devlist_mtx); -} - -int cfg80211_change_iface(struct cfg80211_registered_device *rdev, - struct net_device *dev, enum nl80211_iftype ntype, - u32 *flags, struct vif_params *params) -{ - int err; - enum nl80211_iftype otype = dev->ieee80211_ptr->iftype; - - ASSERT_RDEV_LOCK(rdev); - - /* don't support changing VLANs, you just re-create them */ - if (otype == NL80211_IFTYPE_AP_VLAN) - return -EOPNOTSUPP; - - if (!rdev->ops->change_virtual_intf || - !(rdev->wiphy.interface_modes & (1 << ntype))) - return -EOPNOTSUPP; - - /* if it's part of a bridge, reject changing type to station/ibss */ - if (br_port_exists(dev) && - (ntype == NL80211_IFTYPE_ADHOC || - ntype == NL80211_IFTYPE_STATION || - ntype == NL80211_IFTYPE_P2P_CLIENT)) - return -EBUSY; - - if (ntype != otype) { - err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, - ntype); - if (err) - return err; - - dev->ieee80211_ptr->use_4addr = false; - dev->ieee80211_ptr->mesh_id_up_len = 0; - - switch (otype) { - case NL80211_IFTYPE_ADHOC: - cfg80211_leave_ibss(rdev, dev, false); - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, true); - break; - case NL80211_IFTYPE_MESH_POINT: - /* mesh should be handled? */ - break; - default: - break; - } - - cfg80211_process_rdev_events(rdev); - } - - err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, - ntype, flags, params); - - WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype); - - if (!err && params && params->use_4addr != -1) - dev->ieee80211_ptr->use_4addr = params->use_4addr; - - if (!err) { - dev->priv_flags &= ~IFF_DONT_BRIDGE; - switch (ntype) { - case NL80211_IFTYPE_STATION: - if (dev->ieee80211_ptr->use_4addr) - break; - /* fall through */ - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_ADHOC: - dev->priv_flags |= IFF_DONT_BRIDGE; - break; - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MESH_POINT: - /* bridging OK */ - break; - case NL80211_IFTYPE_MONITOR: - /* monitor can't bridge anyway */ - break; - case NL80211_IFTYPE_UNSPECIFIED: - case NUM_NL80211_IFTYPES: - /* not happening */ - break; - } - } - - return err; -} - -u16 cfg80211_calculate_bitrate(struct rate_info *rate) -{ - int modulation, streams, bitrate; - - if (!(rate->flags & RATE_INFO_FLAGS_MCS)) - return rate->legacy; - - /* the formula below does only work for MCS values smaller than 32 */ - if (rate->mcs >= 32) - return 0; - - modulation = rate->mcs & 7; - streams = (rate->mcs >> 3) + 1; - - bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? - 13500000 : 6500000; - - if (modulation < 4) - bitrate *= (modulation + 1); - else if (modulation == 4) - bitrate *= (modulation + 2); - else - bitrate *= (modulation + 3); - - bitrate *= streams; - - if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) - bitrate = (bitrate / 9) * 10; - - /* do NOT round down here */ - return (bitrate + 50000) / 100000; -} - -int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int) -{ - struct wireless_dev *wdev; - int res = 0; - - if (!beacon_int) - return -EINVAL; - - mutex_lock(&rdev->devlist_mtx); - - list_for_each_entry(wdev, &rdev->netdev_list, list) { - if (!wdev->beacon_interval) - continue; - if (wdev->beacon_interval != beacon_int) { - res = -EINVAL; - break; - } - } - - mutex_unlock(&rdev->devlist_mtx); - - return res; -} - -int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, - enum nl80211_iftype iftype) -{ - struct wireless_dev *wdev_iter; - int num[NUM_NL80211_IFTYPES]; - int total = 1; - int i, j; - - ASSERT_RTNL(); - - /* Always allow software iftypes */ - if (rdev->wiphy.software_iftypes & BIT(iftype)) - return 0; - - /* - * Drivers will gradually all set this flag, until all - * have it we only enforce for those that set it. - */ - if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) - return 0; - - memset(num, 0, sizeof(num)); - - num[iftype] = 1; - - mutex_lock(&rdev->devlist_mtx); - list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { - if (wdev_iter == wdev) - continue; - if (!netif_running(wdev_iter->netdev)) - continue; - - if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) - continue; - - num[wdev_iter->iftype]++; - total++; - } - mutex_unlock(&rdev->devlist_mtx); - - for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { - const struct ieee80211_iface_combination *c; - struct ieee80211_iface_limit *limits; - - c = &rdev->wiphy.iface_combinations[i]; - - limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, - GFP_KERNEL); - if (!limits) - return -ENOMEM; - if (total > c->max_interfaces) - goto cont; - - for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { - if (rdev->wiphy.software_iftypes & BIT(iftype)) - continue; - for (j = 0; j < c->n_limits; j++) { - if (!(limits[j].types & iftype)) - continue; - if (limits[j].max < num[iftype]) - goto cont; - limits[j].max -= num[iftype]; - } - } - /* yay, it fits */ - kfree(limits); - return 0; - cont: - kfree(limits); - } - - return -EBUSY; -} - -int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, - const u8 *rates, unsigned int n_rates, - u32 *mask) -{ - int i, j; - - if (!sband) - return -EINVAL; - - if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES) - return -EINVAL; - - *mask = 0; - - for (i = 0; i < n_rates; i++) { - int rate = (rates[i] & 0x7f) * 5; - bool found = false; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - found = true; - *mask |= BIT(j); - break; - } - } - if (!found) - return -EINVAL; - } - - /* - * mask must have at least one bit set here since we - * didn't accept a 0-length rates array nor allowed - * entries in the array that didn't exist - */ - - return 0; -} - -u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, - struct ieee802_11_elems *elems, - u64 filter, u32 crc) -{ - size_t left = len; - u8 *pos = start; - bool calc_crc = filter != 0; - - memset(elems, 0, sizeof(*elems)); - elems->ie_start = start; - elems->total_len = len; - - while (left >= 2) { - u8 id, elen; - - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) - break; - - if (calc_crc && id < 64 && (filter & (1ULL << id))) - crc = crc32_be(crc, pos - 2, elen + 2); - - switch (id) { - case WLAN_EID_SSID: - elems->ssid = pos; - elems->ssid_len = elen; - break; - case WLAN_EID_SUPP_RATES: - elems->supp_rates = pos; - elems->supp_rates_len = elen; - break; - case WLAN_EID_FH_PARAMS: - elems->fh_params = pos; - elems->fh_params_len = elen; - break; - case WLAN_EID_DS_PARAMS: - elems->ds_params = pos; - elems->ds_params_len = elen; - break; - case WLAN_EID_CF_PARAMS: - elems->cf_params = pos; - elems->cf_params_len = elen; - break; - case WLAN_EID_TIM: - if (elen >= sizeof(struct ieee80211_tim_ie)) { - elems->tim = (void *)pos; - elems->tim_len = elen; - } - break; - case WLAN_EID_IBSS_PARAMS: - elems->ibss_params = pos; - elems->ibss_params_len = elen; - break; - case WLAN_EID_CHALLENGE: - elems->challenge = pos; - elems->challenge_len = elen; - break; - case WLAN_EID_VENDOR_SPECIFIC: - if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 && - pos[2] == 0xf2) { - /* Microsoft OUI (00:50:F2) */ - - if (calc_crc) - crc = crc32_be(crc, pos - 2, elen + 2); - - if (pos[3] == 1) { - /* OUI Type 1 - WPA IE */ - elems->wpa = pos; - elems->wpa_len = elen; - } else if (elen >= 5 && pos[3] == 2) { - /* OUI Type 2 - WMM IE */ - if (pos[4] == 0) { - elems->wmm_info = pos; - elems->wmm_info_len = elen; - } else if (pos[4] == 1) { - elems->wmm_param = pos; - elems->wmm_param_len = elen; - } - } - } - break; - case WLAN_EID_RSN: - elems->rsn = pos; - elems->rsn_len = elen; - break; - case WLAN_EID_ERP_INFO: - elems->erp_info = pos; - elems->erp_info_len = elen; - break; - case WLAN_EID_EXT_SUPP_RATES: - elems->ext_supp_rates = pos; - elems->ext_supp_rates_len = elen; - break; - case WLAN_EID_HT_CAPABILITY: - if (elen >= sizeof(struct ieee80211_ht_cap)) - elems->ht_cap_elem = (void *)pos; - break; - case WLAN_EID_HT_INFORMATION: - if (elen >= sizeof(struct ieee80211_ht_info)) - elems->ht_info_elem = (void *)pos; - break; - case WLAN_EID_MESH_ID: - elems->mesh_id = pos; - elems->mesh_id_len = elen; - break; - case WLAN_EID_MESH_CONFIG: - if (elen >= sizeof(struct ieee80211_meshconf_ie)) - elems->mesh_config = (void *)pos; - break; - case WLAN_EID_PEER_MGMT: - elems->peering = pos; - elems->peering_len = elen; - break; - case WLAN_EID_PREQ: - elems->preq = pos; - elems->preq_len = elen; - break; - case WLAN_EID_PREP: - elems->prep = pos; - elems->prep_len = elen; - break; - case WLAN_EID_PERR: - elems->perr = pos; - elems->perr_len = elen; - break; - case WLAN_EID_RANN: - if (elen >= sizeof(struct ieee80211_rann_ie)) - elems->rann = (void *)pos; - break; - case WLAN_EID_CHANNEL_SWITCH: - elems->ch_switch_elem = pos; - elems->ch_switch_elem_len = elen; - break; - case WLAN_EID_QUIET: - if (!elems->quiet_elem) { - elems->quiet_elem = pos; - elems->quiet_elem_len = elen; - } - elems->num_of_quiet_elem++; - break; - case WLAN_EID_COUNTRY: - elems->country_elem = pos; - elems->country_elem_len = elen; - break; - case WLAN_EID_PWR_CONSTRAINT: - elems->pwr_constr_elem = pos; - elems->pwr_constr_elem_len = elen; - break; - case WLAN_EID_TIMEOUT_INTERVAL: - elems->timeout_int = pos; - elems->timeout_int_len = elen; - break; - default: - break; - } - - left -= elen; - pos += elen; - } - - return crc; -} -EXPORT_SYMBOL(ieee802_11_parse_elems_crc); diff --git a/net/wireless_ath/wext-compat.c b/net/wireless_ath/wext-compat.c deleted file mode 100755 index 3c24eb9..0000000 --- a/net/wireless_ath/wext-compat.c +++ /dev/null @@ -1,1531 +0,0 @@ -/* - * cfg80211 - wext compat code - * - * This is temporary code until all wireless functionality is migrated - * into cfg80211, when that happens all the exports here go away and - * we directly assign the wireless handlers of wireless interfaces. - * - * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net> - */ - -#include <linux/export.h> -#include <linux/wireless.h> -#include <linux/nl80211.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> -#include <linux/slab.h> -#include <net/iw_handler.h> -#include <net/cfg80211.h> -#include <net/cfg80211-wext.h> -#include "wext-compat.h" -#include "core.h" - -int cfg80211_wext_giwname(struct net_device *dev, - struct iw_request_info *info, - char *name, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_supported_band *sband; - bool is_ht = false, is_a = false, is_b = false, is_g = false; - - if (!wdev) - return -EOPNOTSUPP; - - sband = wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; - if (sband) { - is_a = true; - is_ht |= sband->ht_cap.ht_supported; - } - - sband = wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; - if (sband) { - int i; - /* Check for mandatory rates */ - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].bitrate == 10) - is_b = true; - if (sband->bitrates[i].bitrate == 60) - is_g = true; - } - is_ht |= sband->ht_cap.ht_supported; - } - - strcpy(name, "IEEE 802.11"); - if (is_a) - strcat(name, "a"); - if (is_b) - strcat(name, "b"); - if (is_g) - strcat(name, "g"); - if (is_ht) - strcat(name, "n"); - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwname); - -int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev; - struct vif_params vifparams; - enum nl80211_iftype type; - int ret; - - rdev = wiphy_to_dev(wdev->wiphy); - - switch (*mode) { - case IW_MODE_INFRA: - type = NL80211_IFTYPE_STATION; - break; - case IW_MODE_ADHOC: - type = NL80211_IFTYPE_ADHOC; - break; - case IW_MODE_REPEAT: - type = NL80211_IFTYPE_WDS; - break; - case IW_MODE_MONITOR: - type = NL80211_IFTYPE_MONITOR; - break; - default: - return -EINVAL; - } - - if (type == wdev->iftype) - return 0; - - memset(&vifparams, 0, sizeof(vifparams)); - - cfg80211_lock_rdev(rdev); - ret = cfg80211_change_iface(rdev, dev, type, NULL, &vifparams); - cfg80211_unlock_rdev(rdev); - - return ret; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwmode); - -int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info, - u32 *mode, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (!wdev) - return -EOPNOTSUPP; - - switch (wdev->iftype) { - case NL80211_IFTYPE_AP: - *mode = IW_MODE_MASTER; - break; - case NL80211_IFTYPE_STATION: - *mode = IW_MODE_INFRA; - break; - case NL80211_IFTYPE_ADHOC: - *mode = IW_MODE_ADHOC; - break; - case NL80211_IFTYPE_MONITOR: - *mode = IW_MODE_MONITOR; - break; - case NL80211_IFTYPE_WDS: - *mode = IW_MODE_REPEAT; - break; - case NL80211_IFTYPE_AP_VLAN: - *mode = IW_MODE_SECOND; /* FIXME */ - break; - default: - *mode = IW_MODE_AUTO; - break; - } - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwmode); - - -int cfg80211_wext_giwrange(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_range *range = (struct iw_range *) extra; - enum ieee80211_band band; - int i, c = 0; - - if (!wdev) - return -EOPNOTSUPP; - - data->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 21; - range->retry_capa = IW_RETRY_LIMIT; - range->retry_flags = IW_RETRY_LIMIT; - range->min_retry = 0; - range->max_retry = 255; - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - range->max_encoding_tokens = 4; - - range->max_qual.updated = IW_QUAL_NOISE_INVALID; - - switch (wdev->wiphy->signal_type) { - case CFG80211_SIGNAL_TYPE_NONE: - break; - case CFG80211_SIGNAL_TYPE_MBM: - range->max_qual.level = -110; - range->max_qual.qual = 70; - range->avg_qual.qual = 35; - range->max_qual.updated |= IW_QUAL_DBM; - range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; - range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; - break; - case CFG80211_SIGNAL_TYPE_UNSPEC: - range->max_qual.level = 100; - range->max_qual.qual = 100; - range->avg_qual.qual = 50; - range->max_qual.updated |= IW_QUAL_QUAL_UPDATED; - range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED; - break; - } - - range->avg_qual.level = range->max_qual.level / 2; - range->avg_qual.noise = range->max_qual.noise / 2; - range->avg_qual.updated = range->max_qual.updated; - - for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { - switch (wdev->wiphy->cipher_suites[i]) { - case WLAN_CIPHER_SUITE_TKIP: - range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP | - IW_ENC_CAPA_WPA); - break; - - case WLAN_CIPHER_SUITE_CCMP: - range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP | - IW_ENC_CAPA_WPA2); - break; - - case WLAN_CIPHER_SUITE_WEP40: - range->encoding_size[range->num_encoding_sizes++] = - WLAN_KEY_LEN_WEP40; - break; - - case WLAN_CIPHER_SUITE_WEP104: - range->encoding_size[range->num_encoding_sizes++] = - WLAN_KEY_LEN_WEP104; - break; - } - } - - for (band = 0; band < IEEE80211_NUM_BANDS; band ++) { - struct ieee80211_supported_band *sband; - - sband = wdev->wiphy->bands[band]; - - if (!sband) - continue; - - for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { - struct ieee80211_channel *chan = &sband->channels[i]; - - if (!(chan->flags & IEEE80211_CHAN_DISABLED)) { - range->freq[c].i = - ieee80211_frequency_to_channel( - chan->center_freq); - range->freq[c].m = chan->center_freq; - range->freq[c].e = 6; - c++; - } - } - } - range->num_channels = c; - range->num_frequency = c; - - IW_EVENT_CAPA_SET_KERNEL(range->event_capa); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - - if (wdev->wiphy->max_scan_ssids > 0) - range->scan_capa |= IW_SCAN_CAPA_ESSID; - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwrange); - - -/** - * cfg80211_wext_freq - get wext frequency for non-"auto" - * @wiphy: the wiphy - * @freq: the wext freq encoding - * - * Returns a frequency, or a negative error code, or 0 for auto. - */ -int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq) -{ - /* - * Parse frequency - return 0 for auto and - * -EINVAL for impossible things. - */ - if (freq->e == 0) { - enum ieee80211_band band = IEEE80211_BAND_2GHZ; - if (freq->m < 0) - return 0; - if (freq->m > 14) - band = IEEE80211_BAND_5GHZ; - return ieee80211_channel_to_frequency(freq->m, band); - } else { - int i, div = 1000000; - for (i = 0; i < freq->e; i++) - div /= 10; - if (div <= 0) - return -EINVAL; - return freq->m / div; - } -} - -int cfg80211_wext_siwrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u32 orts = wdev->wiphy->rts_threshold; - int err; - - if (rts->disabled || !rts->fixed) - wdev->wiphy->rts_threshold = (u32) -1; - else if (rts->value < 0) - return -EINVAL; - else - wdev->wiphy->rts_threshold = rts->value; - - err = rdev->ops->set_wiphy_params(wdev->wiphy, - WIPHY_PARAM_RTS_THRESHOLD); - if (err) - wdev->wiphy->rts_threshold = orts; - - return err; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwrts); - -int cfg80211_wext_giwrts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rts, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - rts->value = wdev->wiphy->rts_threshold; - rts->disabled = rts->value == (u32) -1; - rts->fixed = 1; - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwrts); - -int cfg80211_wext_siwfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u32 ofrag = wdev->wiphy->frag_threshold; - int err; - - if (frag->disabled || !frag->fixed) - wdev->wiphy->frag_threshold = (u32) -1; - else if (frag->value < 256) - return -EINVAL; - else { - /* Fragment length must be even, so strip LSB. */ - wdev->wiphy->frag_threshold = frag->value & ~0x1; - } - - err = rdev->ops->set_wiphy_params(wdev->wiphy, - WIPHY_PARAM_FRAG_THRESHOLD); - if (err) - wdev->wiphy->frag_threshold = ofrag; - - return err; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_siwfrag); - -int cfg80211_wext_giwfrag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *frag, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - frag->value = wdev->wiphy->frag_threshold; - frag->disabled = frag->value == (u32) -1; - frag->fixed = 1; - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwfrag); - -static int cfg80211_wext_siwretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *retry, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u32 changed = 0; - u8 olong = wdev->wiphy->retry_long; - u8 oshort = wdev->wiphy->retry_short; - int err; - - if (retry->disabled || - (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) - return -EINVAL; - - if (retry->flags & IW_RETRY_LONG) { - wdev->wiphy->retry_long = retry->value; - changed |= WIPHY_PARAM_RETRY_LONG; - } else if (retry->flags & IW_RETRY_SHORT) { - wdev->wiphy->retry_short = retry->value; - changed |= WIPHY_PARAM_RETRY_SHORT; - } else { - wdev->wiphy->retry_short = retry->value; - wdev->wiphy->retry_long = retry->value; - changed |= WIPHY_PARAM_RETRY_LONG; - changed |= WIPHY_PARAM_RETRY_SHORT; - } - - if (!changed) - return 0; - - err = rdev->ops->set_wiphy_params(wdev->wiphy, changed); - if (err) { - wdev->wiphy->retry_short = oshort; - wdev->wiphy->retry_long = olong; - } - - return err; -} - -int cfg80211_wext_giwretry(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *retry, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - retry->disabled = 0; - - if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) { - /* - * First return short value, iwconfig will ask long value - * later if needed - */ - retry->flags |= IW_RETRY_LIMIT; - retry->value = wdev->wiphy->retry_short; - if (wdev->wiphy->retry_long != wdev->wiphy->retry_short) - retry->flags |= IW_RETRY_LONG; - - return 0; - } - - if (retry->flags & IW_RETRY_LONG) { - retry->flags = IW_RETRY_LIMIT | IW_RETRY_LONG; - retry->value = wdev->wiphy->retry_long; - } - - return 0; -} -EXPORT_SYMBOL_GPL(cfg80211_wext_giwretry); - -static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool pairwise, - const u8 *addr, bool remove, bool tx_key, - int idx, struct key_params *params) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int err, i; - bool rejoin = false; - - if (pairwise && !addr) - return -EINVAL; - - if (!wdev->wext.keys) { - wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys), - GFP_KERNEL); - if (!wdev->wext.keys) - return -ENOMEM; - for (i = 0; i < 6; i++) - wdev->wext.keys->params[i].key = - wdev->wext.keys->data[i]; - } - - if (wdev->iftype != NL80211_IFTYPE_ADHOC && - wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { - if (!wdev->current_bss) - return -ENOLINK; - - if (!rdev->ops->set_default_mgmt_key) - return -EOPNOTSUPP; - - if (idx < 4 || idx > 5) - return -EINVAL; - } else if (idx < 0 || idx > 3) - return -EINVAL; - - if (remove) { - err = 0; - if (wdev->current_bss) { - /* - * If removing the current TX key, we will need to - * join a new IBSS without the privacy bit clear. - */ - if (idx == wdev->wext.default_key && - wdev->iftype == NL80211_IFTYPE_ADHOC) { - __cfg80211_leave_ibss(rdev, wdev->netdev, true); - rejoin = true; - } - - if (!pairwise && addr && - !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) - err = -ENOENT; - else - err = rdev->ops->del_key(&rdev->wiphy, dev, idx, - pairwise, addr); - } - wdev->wext.connect.privacy = false; - /* - * Applications using wireless extensions expect to be - * able to delete keys that don't exist, so allow that. - */ - if (err == -ENOENT) - err = 0; - if (!err) { - if (!addr) { - wdev->wext.keys->params[idx].key_len = 0; - wdev->wext.keys->params[idx].cipher = 0; - } - if (idx == wdev->wext.default_key) - wdev->wext.default_key = -1; - else if (idx == wdev->wext.default_mgmt_key) - wdev->wext.default_mgmt_key = -1; - } - - if (!err && rejoin) - err = cfg80211_ibss_wext_join(rdev, wdev); - - return err; - } - - if (addr) - tx_key = false; - - if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) - return -EINVAL; - - err = 0; - if (wdev->current_bss) - err = rdev->ops->add_key(&rdev->wiphy, dev, idx, - pairwise, addr, params); - if (err) - return err; - - if (!addr) { - wdev->wext.keys->params[idx] = *params; - memcpy(wdev->wext.keys->data[idx], - params->key, params->key_len); - wdev->wext.keys->params[idx].key = - wdev->wext.keys->data[idx]; - } - - if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 || - params->cipher == WLAN_CIPHER_SUITE_WEP104) && - (tx_key || (!addr && wdev->wext.default_key == -1))) { - if (wdev->current_bss) { - /* - * If we are getting a new TX key from not having - * had one before we need to join a new IBSS with - * the privacy bit set. - */ - if (wdev->iftype == NL80211_IFTYPE_ADHOC && - wdev->wext.default_key == -1) { - __cfg80211_leave_ibss(rdev, wdev->netdev, true); - rejoin = true; - } - err = rdev->ops->set_default_key(&rdev->wiphy, dev, - idx, true, true); - } - if (!err) { - wdev->wext.default_key = idx; - if (rejoin) - err = cfg80211_ibss_wext_join(rdev, wdev); - } - return err; - } - - if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC && - (tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) { - if (wdev->current_bss) - err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, - dev, idx); - if (!err) - wdev->wext.default_mgmt_key = idx; - return err; - } - - return 0; -} - -static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev, - struct net_device *dev, bool pairwise, - const u8 *addr, bool remove, bool tx_key, - int idx, struct key_params *params) -{ - int err; - - /* devlist mutex needed for possible IBSS re-join */ - mutex_lock(&rdev->devlist_mtx); - wdev_lock(dev->ieee80211_ptr); - err = __cfg80211_set_encryption(rdev, dev, pairwise, addr, - remove, tx_key, idx, params); - wdev_unlock(dev->ieee80211_ptr); - mutex_unlock(&rdev->devlist_mtx); - - return err; -} - -static int cfg80211_wext_siwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *keybuf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int idx, err; - bool remove = false; - struct key_params params; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - /* no use -- only MFP (set_default_mgmt_key) is optional */ - if (!rdev->ops->del_key || - !rdev->ops->add_key || - !rdev->ops->set_default_key) - return -EOPNOTSUPP; - - idx = erq->flags & IW_ENCODE_INDEX; - if (idx == 0) { - idx = wdev->wext.default_key; - if (idx < 0) - idx = 0; - } else if (idx < 1 || idx > 4) - return -EINVAL; - else - idx--; - - if (erq->flags & IW_ENCODE_DISABLED) - remove = true; - else if (erq->length == 0) { - /* No key data - just set the default TX key index */ - err = 0; - wdev_lock(wdev); - if (wdev->current_bss) - err = rdev->ops->set_default_key(&rdev->wiphy, dev, - idx, true, true); - if (!err) - wdev->wext.default_key = idx; - wdev_unlock(wdev); - return err; - } - - memset(¶ms, 0, sizeof(params)); - params.key = keybuf; - params.key_len = erq->length; - if (erq->length == 5) - params.cipher = WLAN_CIPHER_SUITE_WEP40; - else if (erq->length == 13) - params.cipher = WLAN_CIPHER_SUITE_WEP104; - else if (!remove) - return -EINVAL; - - return cfg80211_set_encryption(rdev, dev, false, NULL, remove, - wdev->wext.default_key == -1, - idx, ¶ms); -} - -static int cfg80211_wext_siwencodeext(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; - const u8 *addr; - int idx; - bool remove = false; - struct key_params params; - u32 cipher; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - /* no use -- only MFP (set_default_mgmt_key) is optional */ - if (!rdev->ops->del_key || - !rdev->ops->add_key || - !rdev->ops->set_default_key) - return -EOPNOTSUPP; - - switch (ext->alg) { - case IW_ENCODE_ALG_NONE: - remove = true; - cipher = 0; - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len == 5) - cipher = WLAN_CIPHER_SUITE_WEP40; - else if (ext->key_len == 13) - cipher = WLAN_CIPHER_SUITE_WEP104; - else - return -EINVAL; - break; - case IW_ENCODE_ALG_TKIP: - cipher = WLAN_CIPHER_SUITE_TKIP; - break; - case IW_ENCODE_ALG_CCMP: - cipher = WLAN_CIPHER_SUITE_CCMP; - break; - case IW_ENCODE_ALG_AES_CMAC: - cipher = WLAN_CIPHER_SUITE_AES_CMAC; - break; - default: - return -EOPNOTSUPP; - } - - if (erq->flags & IW_ENCODE_DISABLED) - remove = true; - - idx = erq->flags & IW_ENCODE_INDEX; - if (cipher == WLAN_CIPHER_SUITE_AES_CMAC) { - if (idx < 4 || idx > 5) { - idx = wdev->wext.default_mgmt_key; - if (idx < 0) - return -EINVAL; - } else - idx--; - } else { - if (idx < 1 || idx > 4) { - idx = wdev->wext.default_key; - if (idx < 0) - return -EINVAL; - } else - idx--; - } - - addr = ext->addr.sa_data; - if (is_broadcast_ether_addr(addr)) - addr = NULL; - - memset(¶ms, 0, sizeof(params)); - params.key = ext->key; - params.key_len = ext->key_len; - params.cipher = cipher; - - if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { - params.seq = ext->rx_seq; - params.seq_len = 6; - } - - return cfg80211_set_encryption( - rdev, dev, - !(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY), - addr, remove, - ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, - idx, ¶ms); -} - -static int cfg80211_wext_giwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *keybuf) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - int idx; - - if (wdev->iftype != NL80211_IFTYPE_STATION && - wdev->iftype != NL80211_IFTYPE_ADHOC) - return -EOPNOTSUPP; - - idx = erq->flags & IW_ENCODE_INDEX; - if (idx == 0) { - idx = wdev->wext.default_key; - if (idx < 0) - idx = 0; - } else if (idx < 1 || idx > 4) - return -EINVAL; - else - idx--; - - erq->flags = idx + 1; - - if (!wdev->wext.keys || !wdev->wext.keys->params[idx].cipher) { - erq->flags |= IW_ENCODE_DISABLED; - erq->length = 0; - return 0; - } - - erq->length = min_t(size_t, erq->length, - wdev->wext.keys->params[idx].key_len); - memcpy(keybuf, wdev->wext.keys->params[idx].key, erq->length); - erq->flags |= IW_ENCODE_ENABLED; - - return 0; -} - -static int cfg80211_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int freq, err; - - switch (wdev->iftype) { - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra); - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra); - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MESH_POINT: - freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); - if (freq < 0) - return freq; - if (freq == 0) - return -EINVAL; - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - return err; - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - - switch (wdev->iftype) { - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra); - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); - case NL80211_IFTYPE_MONITOR: - if (!rdev->ops->get_channel) - return -EINVAL; - - chan = rdev->ops->get_channel(wdev->wiphy); - if (!chan) - return -EINVAL; - freq->m = chan->center_freq; - freq->e = 6; - return 0; - default: - if (!wdev->channel) - return -EINVAL; - freq->m = wdev->channel->center_freq; - freq->e = 6; - return 0; - } -} - -static int cfg80211_wext_siwtxpower(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - enum nl80211_tx_power_setting type; - int dbm = 0; - - if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) - return -EINVAL; - if (data->txpower.flags & IW_TXPOW_RANGE) - return -EINVAL; - - if (!rdev->ops->set_tx_power) - return -EOPNOTSUPP; - - /* only change when not disabling */ - if (!data->txpower.disabled) { - rfkill_set_sw_state(rdev->rfkill, false); - - if (data->txpower.fixed) { - /* - * wext doesn't support negative values, see - * below where it's for automatic - */ - if (data->txpower.value < 0) - return -EINVAL; - dbm = data->txpower.value; - type = NL80211_TX_POWER_FIXED; - /* TODO: do regulatory check! */ - } else { - /* - * Automatic power level setting, max being the value - * passed in from userland. - */ - if (data->txpower.value < 0) { - type = NL80211_TX_POWER_AUTOMATIC; - } else { - dbm = data->txpower.value; - type = NL80211_TX_POWER_LIMITED; - } - } - } else { - rfkill_set_sw_state(rdev->rfkill, true); - schedule_work(&rdev->rfkill_sync); - return 0; - } - - return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm)); -} - -static int cfg80211_wext_giwtxpower(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int err, val; - - if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) - return -EINVAL; - if (data->txpower.flags & IW_TXPOW_RANGE) - return -EINVAL; - - if (!rdev->ops->get_tx_power) - return -EOPNOTSUPP; - - err = rdev->ops->get_tx_power(wdev->wiphy, &val); - if (err) - return err; - - /* well... oh well */ - data->txpower.fixed = 1; - data->txpower.disabled = rfkill_blocked(rdev->rfkill); - data->txpower.value = val; - data->txpower.flags = IW_TXPOW_DBM; - - return 0; -} - -static int cfg80211_set_auth_alg(struct wireless_dev *wdev, - s32 auth_alg) -{ - int nr_alg = 0; - - if (!auth_alg) - return -EINVAL; - - if (auth_alg & ~(IW_AUTH_ALG_OPEN_SYSTEM | - IW_AUTH_ALG_SHARED_KEY | - IW_AUTH_ALG_LEAP)) - return -EINVAL; - - if (auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) { - nr_alg++; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - } - - if (auth_alg & IW_AUTH_ALG_SHARED_KEY) { - nr_alg++; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_SHARED_KEY; - } - - if (auth_alg & IW_AUTH_ALG_LEAP) { - nr_alg++; - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_NETWORK_EAP; - } - - if (nr_alg > 1) - wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; - - return 0; -} - -static int cfg80211_set_wpa_version(struct wireless_dev *wdev, u32 wpa_versions) -{ - if (wpa_versions & ~(IW_AUTH_WPA_VERSION_WPA | - IW_AUTH_WPA_VERSION_WPA2| - IW_AUTH_WPA_VERSION_DISABLED)) - return -EINVAL; - - if ((wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) && - (wpa_versions & (IW_AUTH_WPA_VERSION_WPA| - IW_AUTH_WPA_VERSION_WPA2))) - return -EINVAL; - - if (wpa_versions & IW_AUTH_WPA_VERSION_DISABLED) - wdev->wext.connect.crypto.wpa_versions &= - ~(NL80211_WPA_VERSION_1|NL80211_WPA_VERSION_2); - - if (wpa_versions & IW_AUTH_WPA_VERSION_WPA) - wdev->wext.connect.crypto.wpa_versions |= - NL80211_WPA_VERSION_1; - - if (wpa_versions & IW_AUTH_WPA_VERSION_WPA2) - wdev->wext.connect.crypto.wpa_versions |= - NL80211_WPA_VERSION_2; - - return 0; -} - -static int cfg80211_set_cipher_group(struct wireless_dev *wdev, u32 cipher) -{ - if (cipher & IW_AUTH_CIPHER_WEP40) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_WEP40; - else if (cipher & IW_AUTH_CIPHER_WEP104) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_WEP104; - else if (cipher & IW_AUTH_CIPHER_TKIP) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_TKIP; - else if (cipher & IW_AUTH_CIPHER_CCMP) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_CCMP; - else if (cipher & IW_AUTH_CIPHER_AES_CMAC) - wdev->wext.connect.crypto.cipher_group = - WLAN_CIPHER_SUITE_AES_CMAC; - else if (cipher & IW_AUTH_CIPHER_NONE) - wdev->wext.connect.crypto.cipher_group = 0; - else - return -EINVAL; - - return 0; -} - -static int cfg80211_set_cipher_pairwise(struct wireless_dev *wdev, u32 cipher) -{ - int nr_ciphers = 0; - u32 *ciphers_pairwise = wdev->wext.connect.crypto.ciphers_pairwise; - - if (cipher & IW_AUTH_CIPHER_WEP40) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP40; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_WEP104) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_WEP104; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_TKIP) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_TKIP; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_CCMP) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_CCMP; - nr_ciphers++; - } - - if (cipher & IW_AUTH_CIPHER_AES_CMAC) { - ciphers_pairwise[nr_ciphers] = WLAN_CIPHER_SUITE_AES_CMAC; - nr_ciphers++; - } - - BUILD_BUG_ON(NL80211_MAX_NR_CIPHER_SUITES < 5); - - wdev->wext.connect.crypto.n_ciphers_pairwise = nr_ciphers; - - return 0; -} - - -static int cfg80211_set_key_mgt(struct wireless_dev *wdev, u32 key_mgt) -{ - int nr_akm_suites = 0; - - if (key_mgt & ~(IW_AUTH_KEY_MGMT_802_1X | - IW_AUTH_KEY_MGMT_PSK)) - return -EINVAL; - - if (key_mgt & IW_AUTH_KEY_MGMT_802_1X) { - wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = - WLAN_AKM_SUITE_8021X; - nr_akm_suites++; - } - - if (key_mgt & IW_AUTH_KEY_MGMT_PSK) { - wdev->wext.connect.crypto.akm_suites[nr_akm_suites] = - WLAN_AKM_SUITE_PSK; - nr_akm_suites++; - } - - wdev->wext.connect.crypto.n_akm_suites = nr_akm_suites; - - return 0; -} - -static int cfg80211_wext_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_PRIVACY_INVOKED: - wdev->wext.connect.privacy = data->value; - return 0; - case IW_AUTH_WPA_VERSION: - return cfg80211_set_wpa_version(wdev, data->value); - case IW_AUTH_CIPHER_GROUP: - return cfg80211_set_cipher_group(wdev, data->value); - case IW_AUTH_KEY_MGMT: - return cfg80211_set_key_mgt(wdev, data->value); - case IW_AUTH_CIPHER_PAIRWISE: - return cfg80211_set_cipher_pairwise(wdev, data->value); - case IW_AUTH_80211_AUTH_ALG: - return cfg80211_set_auth_alg(wdev, data->value); - case IW_AUTH_WPA_ENABLED: - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - case IW_AUTH_DROP_UNENCRYPTED: - case IW_AUTH_MFP: - return 0; - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - /* XXX: what do we need? */ - - return -EOPNOTSUPP; -} - -static int cfg80211_wext_siwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - bool ps = wdev->ps; - int timeout = wdev->ps_timeout; - int err; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (!rdev->ops->set_power_mgmt) - return -EOPNOTSUPP; - - if (wrq->disabled) { - ps = false; - } else { - switch (wrq->flags & IW_POWER_MODE) { - case IW_POWER_ON: /* If not specified */ - case IW_POWER_MODE: /* If set all mask */ - case IW_POWER_ALL_R: /* If explicitely state all */ - ps = true; - break; - default: /* Otherwise we ignore */ - return -EINVAL; - } - - if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) - return -EINVAL; - - if (wrq->flags & IW_POWER_TIMEOUT) - timeout = wrq->value / 1000; - } - - err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout); - if (err) - return err; - - wdev->ps = ps; - wdev->ps_timeout = timeout; - - return 0; - -} - -static int cfg80211_wext_giwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - wrq->disabled = !wdev->ps; - - return 0; -} - -static int cfg80211_wds_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - int err; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) - return -EINVAL; - - if (addr->sa_family != ARPHRD_ETHER) - return -EINVAL; - - if (netif_running(dev)) - return -EBUSY; - - if (!rdev->ops->set_wds_peer) - return -EOPNOTSUPP; - - err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data); - if (err) - return err; - - memcpy(&wdev->wext.bssid, (u8 *) &addr->sa_data, ETH_ALEN); - - return 0; -} - -static int cfg80211_wds_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_WDS)) - return -EINVAL; - - addr->sa_family = ARPHRD_ETHER; - memcpy(&addr->sa_data, wdev->wext.bssid, ETH_ALEN); - - return 0; -} - -static int cfg80211_wext_siwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_bitrate_mask mask; - u32 fixed, maxrate; - struct ieee80211_supported_band *sband; - int band, ridx; - bool match = false; - - if (!rdev->ops->set_bitrate_mask) - return -EOPNOTSUPP; - - memset(&mask, 0, sizeof(mask)); - fixed = 0; - maxrate = (u32)-1; - - if (rate->value < 0) { - /* nothing */ - } else if (rate->fixed) { - fixed = rate->value / 100000; - } else { - maxrate = rate->value / 100000; - } - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - sband = wdev->wiphy->bands[band]; - if (sband == NULL) - continue; - for (ridx = 0; ridx < sband->n_bitrates; ridx++) { - struct ieee80211_rate *srate = &sband->bitrates[ridx]; - if (fixed == srate->bitrate) { - mask.control[band].legacy = 1 << ridx; - match = true; - break; - } - if (srate->bitrate <= maxrate) { - mask.control[band].legacy |= 1 << ridx; - match = true; - } - } - } - - if (!match) - return -EINVAL; - - return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); -} - -static int cfg80211_wext_giwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - /* we are under RTNL - globally locked - so can use a static struct */ - static struct station_info sinfo; - u8 addr[ETH_ALEN]; - int err; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - if (!rdev->ops->get_station) - return -EOPNOTSUPP; - - err = 0; - wdev_lock(wdev); - if (wdev->current_bss) - memcpy(addr, wdev->current_bss->pub.bssid, ETH_ALEN); - else - err = -EOPNOTSUPP; - wdev_unlock(wdev); - if (err) - return err; - - err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo); - if (err) - return err; - - if (!(sinfo.filled & STATION_INFO_TX_BITRATE)) - return -EOPNOTSUPP; - - rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); - - return 0; -} - -/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ -static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - /* we are under RTNL - globally locked - so can use static structs */ - static struct iw_statistics wstats; - static struct station_info sinfo; - u8 bssid[ETH_ALEN]; - - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) - return NULL; - - if (!rdev->ops->get_station) - return NULL; - - /* Grab BSSID of current BSS, if any */ - wdev_lock(wdev); - if (!wdev->current_bss) { - wdev_unlock(wdev); - return NULL; - } - memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN); - wdev_unlock(wdev); - - if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo)) - return NULL; - - memset(&wstats, 0, sizeof(wstats)); - - switch (rdev->wiphy.signal_type) { - case CFG80211_SIGNAL_TYPE_MBM: - if (sinfo.filled & STATION_INFO_SIGNAL) { - int sig = sinfo.signal; - wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; - wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; - wstats.qual.updated |= IW_QUAL_DBM; - wstats.qual.level = sig; - if (sig < -110) - sig = -110; - else if (sig > -40) - sig = -40; - wstats.qual.qual = sig + 110; - break; - } - case CFG80211_SIGNAL_TYPE_UNSPEC: - if (sinfo.filled & STATION_INFO_SIGNAL) { - wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED; - wstats.qual.updated |= IW_QUAL_QUAL_UPDATED; - wstats.qual.level = sinfo.signal; - wstats.qual.qual = sinfo.signal; - break; - } - default: - wstats.qual.updated |= IW_QUAL_LEVEL_INVALID; - wstats.qual.updated |= IW_QUAL_QUAL_INVALID; - } - - wstats.qual.updated |= IW_QUAL_NOISE_INVALID; - if (sinfo.filled & STATION_INFO_RX_DROP_MISC) - wstats.discard.misc = sinfo.rx_dropped_misc; - if (sinfo.filled & STATION_INFO_TX_FAILED) - wstats.discard.retries = sinfo.tx_failed; - - return &wstats; -} - -static int cfg80211_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_WDS: - return cfg80211_wds_wext_siwap(dev, info, ap_addr, extra); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra); - case NL80211_IFTYPE_WDS: - return cfg80211_wds_wext_giwap(dev, info, ap_addr, extra); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_siwessid(dev, info, data, ssid); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - data->flags = 0; - data->length = 0; - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - case NL80211_IFTYPE_STATION: - return cfg80211_mgd_wext_giwessid(dev, info, data, ssid); - default: - return -EOPNOTSUPP; - } -} - -static int cfg80211_wext_siwpmksa(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct cfg80211_pmksa cfg_pmksa; - struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; - - memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa)); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - cfg_pmksa.bssid = pmksa->bssid.sa_data; - cfg_pmksa.pmkid = pmksa->pmkid; - - switch (pmksa->cmd) { - case IW_PMKSA_ADD: - if (!rdev->ops->set_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa); - - case IW_PMKSA_REMOVE: - if (!rdev->ops->del_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa); - - case IW_PMKSA_FLUSH: - if (!rdev->ops->flush_pmksa) - return -EOPNOTSUPP; - - return rdev->ops->flush_pmksa(&rdev->wiphy, dev); - - default: - return -EOPNOTSUPP; - } -} - -static const iw_handler cfg80211_handlers[] = { - [IW_IOCTL_IDX(SIOCGIWNAME)] = (iw_handler) cfg80211_wext_giwname, - [IW_IOCTL_IDX(SIOCSIWFREQ)] = (iw_handler) cfg80211_wext_siwfreq, - [IW_IOCTL_IDX(SIOCGIWFREQ)] = (iw_handler) cfg80211_wext_giwfreq, - [IW_IOCTL_IDX(SIOCSIWMODE)] = (iw_handler) cfg80211_wext_siwmode, - [IW_IOCTL_IDX(SIOCGIWMODE)] = (iw_handler) cfg80211_wext_giwmode, - [IW_IOCTL_IDX(SIOCGIWRANGE)] = (iw_handler) cfg80211_wext_giwrange, - [IW_IOCTL_IDX(SIOCSIWAP)] = (iw_handler) cfg80211_wext_siwap, - [IW_IOCTL_IDX(SIOCGIWAP)] = (iw_handler) cfg80211_wext_giwap, - [IW_IOCTL_IDX(SIOCSIWMLME)] = (iw_handler) cfg80211_wext_siwmlme, - [IW_IOCTL_IDX(SIOCSIWSCAN)] = (iw_handler) cfg80211_wext_siwscan, - [IW_IOCTL_IDX(SIOCGIWSCAN)] = (iw_handler) cfg80211_wext_giwscan, - [IW_IOCTL_IDX(SIOCSIWESSID)] = (iw_handler) cfg80211_wext_siwessid, - [IW_IOCTL_IDX(SIOCGIWESSID)] = (iw_handler) cfg80211_wext_giwessid, - [IW_IOCTL_IDX(SIOCSIWRATE)] = (iw_handler) cfg80211_wext_siwrate, - [IW_IOCTL_IDX(SIOCGIWRATE)] = (iw_handler) cfg80211_wext_giwrate, - [IW_IOCTL_IDX(SIOCSIWRTS)] = (iw_handler) cfg80211_wext_siwrts, - [IW_IOCTL_IDX(SIOCGIWRTS)] = (iw_handler) cfg80211_wext_giwrts, - [IW_IOCTL_IDX(SIOCSIWFRAG)] = (iw_handler) cfg80211_wext_siwfrag, - [IW_IOCTL_IDX(SIOCGIWFRAG)] = (iw_handler) cfg80211_wext_giwfrag, - [IW_IOCTL_IDX(SIOCSIWTXPOW)] = (iw_handler) cfg80211_wext_siwtxpower, - [IW_IOCTL_IDX(SIOCGIWTXPOW)] = (iw_handler) cfg80211_wext_giwtxpower, - [IW_IOCTL_IDX(SIOCSIWRETRY)] = (iw_handler) cfg80211_wext_siwretry, - [IW_IOCTL_IDX(SIOCGIWRETRY)] = (iw_handler) cfg80211_wext_giwretry, - [IW_IOCTL_IDX(SIOCSIWENCODE)] = (iw_handler) cfg80211_wext_siwencode, - [IW_IOCTL_IDX(SIOCGIWENCODE)] = (iw_handler) cfg80211_wext_giwencode, - [IW_IOCTL_IDX(SIOCSIWPOWER)] = (iw_handler) cfg80211_wext_siwpower, - [IW_IOCTL_IDX(SIOCGIWPOWER)] = (iw_handler) cfg80211_wext_giwpower, - [IW_IOCTL_IDX(SIOCSIWGENIE)] = (iw_handler) cfg80211_wext_siwgenie, - [IW_IOCTL_IDX(SIOCSIWAUTH)] = (iw_handler) cfg80211_wext_siwauth, - [IW_IOCTL_IDX(SIOCGIWAUTH)] = (iw_handler) cfg80211_wext_giwauth, - [IW_IOCTL_IDX(SIOCSIWENCODEEXT)]= (iw_handler) cfg80211_wext_siwencodeext, - [IW_IOCTL_IDX(SIOCSIWPMKSA)] = (iw_handler) cfg80211_wext_siwpmksa, -}; - -const struct iw_handler_def cfg80211_wext_handler = { - .num_standard = ARRAY_SIZE(cfg80211_handlers), - .standard = cfg80211_handlers, - .get_wireless_stats = cfg80211_wireless_stats, -}; diff --git a/net/wireless_ath/wext-compat.h b/net/wireless_ath/wext-compat.h deleted file mode 100755 index 5d766b0..0000000 --- a/net/wireless_ath/wext-compat.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef __WEXT_COMPAT -#define __WEXT_COMPAT - -#include <net/iw_handler.h> -#include <linux/wireless.h> - -int cfg80211_ibss_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_ibss_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_ibss_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_ibss_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_ibss_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); -int cfg80211_ibss_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); - -int cfg80211_mgd_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_mgd_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra); -int cfg80211_mgd_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_mgd_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra); -int cfg80211_mgd_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); -int cfg80211_mgd_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid); - -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra); -int cfg80211_wext_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra); - - -int cfg80211_wext_freq(struct wiphy *wiphy, struct iw_freq *freq); - - -extern const struct iw_handler_def cfg80211_wext_handler; -#endif /* __WEXT_COMPAT */ diff --git a/net/wireless_ath/wext-core.c b/net/wireless_ath/wext-core.c deleted file mode 100755 index ae45584..0000000 --- a/net/wireless_ath/wext-core.c +++ /dev/null @@ -1,1123 +0,0 @@ -/* - * This file implement the Wireless Extensions core API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * - * (As all part of the Linux kernel, this file is GPL) - */ -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <linux/slab.h> -#include <linux/wireless.h> -#include <linux/uaccess.h> -#include <linux/export.h> -#include <net/cfg80211.h> -#include <net/iw_handler.h> -#include <net/netlink.h> -#include <net/wext.h> -#include <net/net_namespace.h> - -typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, - unsigned int, struct iw_request_info *, - iw_handler); - - -/* - * Meta-data about all the standard Wireless Extension request we - * know about. - */ -static const struct iw_ioctl_description standard_ioctl[] = { - [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWNAME)] = { - .header_type = IW_HEADER_TYPE_CHAR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWNWID)] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWNWID)] = { - .header_type = IW_HEADER_TYPE_PARAM, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWFREQ)] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWFREQ)] = { - .header_type = IW_HEADER_TYPE_FREQ, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWMODE)] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWMODE)] = { - .header_type = IW_HEADER_TYPE_UINT, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWSENS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWSENS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWRANGE)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWRANGE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_range), - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWPRIV)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_priv_args), - .max_tokens = 16, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [IW_IOCTL_IDX(SIOCSIWSTATS)] = { - .header_type = IW_HEADER_TYPE_NULL, - }, - [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_statistics), - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr), - .max_tokens = IW_MAX_SPY, - }, - [IW_IOCTL_IDX(SIOCGIWSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_SPY, - }, - [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct iw_thrspy), - .min_tokens = 1, - .max_tokens = 1, - }, - [IW_IOCTL_IDX(SIOCSIWAP)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_IOCTL_IDX(SIOCGIWAP)] = { - .header_type = IW_HEADER_TYPE_ADDR, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWMLME)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_mlme), - .max_tokens = sizeof(struct iw_mlme), - }, - [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = sizeof(struct sockaddr) + - sizeof(struct iw_quality), - .max_tokens = IW_MAX_AP, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [IW_IOCTL_IDX(SIOCSIWSCAN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = 0, - .max_tokens = sizeof(struct iw_scan_req), - }, - [IW_IOCTL_IDX(SIOCGIWSCAN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_SCAN_MAX_DATA, - .flags = IW_DESCR_FLAG_NOMAX, - }, - [IW_IOCTL_IDX(SIOCSIWESSID)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - .flags = IW_DESCR_FLAG_EVENT, - }, - [IW_IOCTL_IDX(SIOCGIWESSID)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - .flags = IW_DESCR_FLAG_DUMP, - }, - [IW_IOCTL_IDX(SIOCSIWNICKN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - }, - [IW_IOCTL_IDX(SIOCGIWNICKN)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ESSID_MAX_SIZE, - }, - [IW_IOCTL_IDX(SIOCSIWRATE)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWRATE)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWRTS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWRTS)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWFRAG)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWFRAG)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWRETRY)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWRETRY)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWENCODE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, - }, - [IW_IOCTL_IDX(SIOCGIWENCODE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_ENCODING_TOKEN_MAX, - .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, - }, - [IW_IOCTL_IDX(SIOCSIWPOWER)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWPOWER)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWGENIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_IOCTL_IDX(SIOCGIWGENIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_IOCTL_IDX(SIOCSIWAUTH)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCGIWAUTH)] = { - .header_type = IW_HEADER_TYPE_PARAM, - }, - [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_encode_ext), - .max_tokens = sizeof(struct iw_encode_ext) + - IW_ENCODING_TOKEN_MAX, - }, - [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_encode_ext), - .max_tokens = sizeof(struct iw_encode_ext) + - IW_ENCODING_TOKEN_MAX, - }, - [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .min_tokens = sizeof(struct iw_pmksa), - .max_tokens = sizeof(struct iw_pmksa), - }, -}; -static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); - -/* - * Meta-data about all the additional standard Wireless Extension events - * we know about. - */ -static const struct iw_ioctl_description standard_event[] = { - [IW_EVENT_IDX(IWEVTXDROP)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_EVENT_IDX(IWEVQUAL)] = { - .header_type = IW_HEADER_TYPE_QUAL, - }, - [IW_EVENT_IDX(IWEVCUSTOM)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_CUSTOM_MAX, - }, - [IW_EVENT_IDX(IWEVREGISTERED)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_EVENT_IDX(IWEVEXPIRED)] = { - .header_type = IW_HEADER_TYPE_ADDR, - }, - [IW_EVENT_IDX(IWEVGENIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_michaelmicfailure), - }, - [IW_EVENT_IDX(IWEVASSOCREQIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = IW_GENERIC_IE_MAX, - }, - [IW_EVENT_IDX(IWEVPMKIDCAND)] = { - .header_type = IW_HEADER_TYPE_POINT, - .token_size = 1, - .max_tokens = sizeof(struct iw_pmkid_cand), - }, -}; -static const unsigned standard_event_num = ARRAY_SIZE(standard_event); - -/* Size (in bytes) of various events */ -static const int event_type_size[] = { - IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_POINT_LEN, /* Without variable payload */ - IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ -}; - -#ifdef CONFIG_COMPAT -static const int compat_event_type_size[] = { - IW_EV_COMPAT_LCP_LEN, /* IW_HEADER_TYPE_NULL */ - 0, - IW_EV_COMPAT_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, - IW_EV_COMPAT_UINT_LEN, /* IW_HEADER_TYPE_UINT */ - IW_EV_COMPAT_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ - IW_EV_COMPAT_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, - IW_EV_COMPAT_POINT_LEN, /* Without variable payload */ - IW_EV_COMPAT_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ - IW_EV_COMPAT_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ -}; -#endif - - -/* IW event code */ - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) -static int __net_init wext_pernet_init(struct net *net) -{ - skb_queue_head_init(&net->wext_nlevents); - return 0; -} - -static void __net_exit wext_pernet_exit(struct net *net) -{ - skb_queue_purge(&net->wext_nlevents); -} - -static struct pernet_operations wext_pernet_ops = { - .init = wext_pernet_init, - .exit = wext_pernet_exit, -}; - -static int __init wireless_nlevent_init(void) -{ - return register_pernet_subsys(&wext_pernet_ops); -} - -subsys_initcall(wireless_nlevent_init); - -/* Process events generated by the wireless layer or the driver. */ -static void wireless_nlevent_process(struct work_struct *work) -{ - struct sk_buff *skb; - struct net *net; - - rtnl_lock(); - - for_each_net(net) { - while ((skb = skb_dequeue(&net->wext_nlevents))) - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, - GFP_KERNEL); - } - - rtnl_unlock(); -} - -static DECLARE_WORK(wireless_nlevent_work, wireless_nlevent_process); - -#else -/* Older kernels get the old way of doing stuff*/ -static struct sk_buff_head wireless_nlevent_queue; - -static int __init wireless_nlevent_init(void) -{ - skb_queue_head_init(&wireless_nlevent_queue); - return 0; -} - -subsys_initcall(wireless_nlevent_init); - -static void wireless_nlevent_process(unsigned long data) -{ - struct sk_buff *skb; - while ((skb = skb_dequeue(&wireless_nlevent_queue))) -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); -#else - rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); -#endif -} - -static DECLARE_TASKLET(wireless_nlevent_tasklet, wireless_nlevent_process, 0); - -#endif - -static struct nlmsghdr *rtnetlink_ifinfo_prep(struct net_device *dev, - struct sk_buff *skb) -{ - struct ifinfomsg *r; - struct nlmsghdr *nlh; - - nlh = nlmsg_put(skb, 0, 0, RTM_NEWLINK, sizeof(*r), 0); - if (!nlh) - return NULL; - - r = nlmsg_data(nlh); - r->ifi_family = AF_UNSPEC; - r->__ifi_pad = 0; - r->ifi_type = dev->type; - r->ifi_index = dev->ifindex; - r->ifi_flags = dev_get_flags(dev); - r->ifi_change = 0; /* Wireless changes don't affect those flags */ - - NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); - - return nlh; - nla_put_failure: - nlmsg_cancel(skb, nlh); - return NULL; -} - - -/* - * Main event dispatcher. Called from other parts and drivers. - * Send the event on the appropriate channels. - * May be called from interrupt context. - */ -void wireless_send_event(struct net_device * dev, - unsigned int cmd, - union iwreq_data * wrqu, - const char * extra) -{ - const struct iw_ioctl_description * descr = NULL; - int extra_len = 0; - struct iw_event *event; /* Mallocated whole event */ - int event_len; /* Its size */ - int hdr_len; /* Size of the event header */ - int wrqu_off = 0; /* Offset in wrqu */ - /* Don't "optimise" the following variable, it will crash */ - unsigned cmd_index; /* *MUST* be unsigned */ - struct sk_buff *skb; - struct nlmsghdr *nlh; - struct nlattr *nla; -#ifdef CONFIG_COMPAT - struct __compat_iw_event *compat_event; - struct compat_iw_point compat_wrqu; - struct sk_buff *compskb; -#endif - - /* - * Nothing in the kernel sends scan events with data, be safe. - * This is necessary because we cannot fix up scan event data - * for compat, due to being contained in 'extra', but normally - * applications are required to retrieve the scan data anyway - * and no data is included in the event, this codifies that - * practice. - */ - if (WARN_ON(cmd == SIOCGIWSCAN && extra)) - extra = NULL; - - /* Get the description of the Event */ - if (cmd <= SIOCIWLAST) { - cmd_index = IW_IOCTL_IDX(cmd); - if (cmd_index < standard_ioctl_num) - descr = &(standard_ioctl[cmd_index]); - } else { - cmd_index = IW_EVENT_IDX(cmd); - if (cmd_index < standard_event_num) - descr = &(standard_event[cmd_index]); - } - /* Don't accept unknown events */ - if (descr == NULL) { - /* Note : we don't return an error to the driver, because - * the driver would not know what to do about it. It can't - * return an error to the user, because the event is not - * initiated by a user request. - * The best the driver could do is to log an error message. - * We will do it ourselves instead... - */ - netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", - cmd); - return; - } - - /* Check extra parameters and set extra_len */ - if (descr->header_type == IW_HEADER_TYPE_POINT) { - /* Check if number of token fits within bounds */ - if (wrqu->data.length > descr->max_tokens) { - netdev_err(dev, "(WE) : Wireless Event too big (%d)\n", - wrqu->data.length); - return; - } - if (wrqu->data.length < descr->min_tokens) { - netdev_err(dev, "(WE) : Wireless Event too small (%d)\n", - wrqu->data.length); - return; - } - /* Calculate extra_len - extra is NULL for restricted events */ - if (extra != NULL) - extra_len = wrqu->data.length * descr->token_size; - /* Always at an offset in wrqu */ - wrqu_off = IW_EV_POINT_OFF; - } - - /* Total length of the event */ - hdr_len = event_type_size[descr->header_type]; - event_len = hdr_len + extra_len; - - /* - * The problem for 64/32 bit. - * - * On 64-bit, a regular event is laid out as follows: - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - * | event.len | event.cmd | p a d d i n g | - * | wrqu data ... (with the correct size) | - * - * This padding exists because we manipulate event->u, - * and 'event' is not packed. - * - * An iw_point event is laid out like this instead: - * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - * | event.len | event.cmd | p a d d i n g | - * | iwpnt.len | iwpnt.flg | p a d d i n g | - * | extra data ... - * - * The second padding exists because struct iw_point is extended, - * but this depends on the platform... - * - * On 32-bit, all the padding shouldn't be there. - */ - - skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!skb) - return; - - /* Send via the RtNetlink event channel */ - nlh = rtnetlink_ifinfo_prep(dev, skb); - if (WARN_ON(!nlh)) { - kfree_skb(skb); - return; - } - - /* Add the wireless events in the netlink packet */ - nla = nla_reserve(skb, IFLA_WIRELESS, event_len); - if (!nla) { - kfree_skb(skb); - return; - } - event = nla_data(nla); - - /* Fill event - first clear to avoid data leaking */ - memset(event, 0, hdr_len); - event->len = event_len; - event->cmd = cmd; - memcpy(&event->u, ((char *) wrqu) + wrqu_off, hdr_len - IW_EV_LCP_LEN); - if (extra_len) - memcpy(((char *) event) + hdr_len, extra, extra_len); - - nlmsg_end(skb, nlh); -#ifdef CONFIG_COMPAT - hdr_len = compat_event_type_size[descr->header_type]; - event_len = hdr_len + extra_len; - - compskb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); - if (!compskb) { - kfree_skb(skb); - return; - } - - /* Send via the RtNetlink event channel */ - nlh = rtnetlink_ifinfo_prep(dev, compskb); - if (WARN_ON(!nlh)) { - kfree_skb(skb); - kfree_skb(compskb); - return; - } - - /* Add the wireless events in the netlink packet */ - nla = nla_reserve(compskb, IFLA_WIRELESS, event_len); - if (!nla) { - kfree_skb(skb); - kfree_skb(compskb); - return; - } - compat_event = nla_data(nla); - - compat_event->len = event_len; - compat_event->cmd = cmd; - if (descr->header_type == IW_HEADER_TYPE_POINT) { - compat_wrqu.length = wrqu->data.length; - compat_wrqu.flags = wrqu->data.flags; - memcpy(&compat_event->pointer, - ((char *) &compat_wrqu) + IW_EV_COMPAT_POINT_OFF, - hdr_len - IW_EV_COMPAT_LCP_LEN); - if (extra_len) - memcpy(((char *) compat_event) + hdr_len, - extra, extra_len); - } else { - /* extra_len must be zero, so no if (extra) needed */ - memcpy(&compat_event->pointer, wrqu, - hdr_len - IW_EV_COMPAT_LCP_LEN); - } - - nlmsg_end(compskb, nlh); - - skb_shinfo(skb)->frag_list = compskb; -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)) - skb_queue_tail(&dev_net(dev)->wext_nlevents, skb); - schedule_work(&wireless_nlevent_work); -#else - skb_queue_tail(&wireless_nlevent_queue, skb); - tasklet_schedule(&wireless_nlevent_tasklet); -#endif -} -EXPORT_SYMBOL(wireless_send_event); - - - -/* IW handlers */ - -struct iw_statistics *get_wireless_stats(struct net_device *dev) -{ -#ifdef CONFIG_WIRELESS_EXT - if ((dev->wireless_handlers != NULL) && - (dev->wireless_handlers->get_wireless_stats != NULL)) - return dev->wireless_handlers->get_wireless_stats(dev); -#endif - -#ifdef CONFIG_CFG80211_WEXT - if (dev->ieee80211_ptr && - dev->ieee80211_ptr->wiphy && - dev->ieee80211_ptr->wiphy->wext && - dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) - return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev); -#endif - - /* not found */ - return NULL; -} - -static int iw_handler_get_iwstats(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - /* Get stats from the driver */ - struct iw_statistics *stats; - - stats = get_wireless_stats(dev); - if (stats) { - /* Copy statistics to extra */ - memcpy(extra, stats, sizeof(struct iw_statistics)); - wrqu->data.length = sizeof(struct iw_statistics); - - /* Check if we need to clear the updated flag */ - if (wrqu->data.flags != 0) - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; - return 0; - } else - return -EOPNOTSUPP; -} - -static iw_handler get_handler(struct net_device *dev, unsigned int cmd) -{ - /* Don't "optimise" the following variable, it will crash */ - unsigned int index; /* *MUST* be unsigned */ - const struct iw_handler_def *handlers = NULL; - -#ifdef CONFIG_CFG80211_WEXT - if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) - handlers = dev->ieee80211_ptr->wiphy->wext; -#endif -#ifdef CONFIG_WIRELESS_EXT - if (dev->wireless_handlers) - handlers = dev->wireless_handlers; -#endif - - if (!handlers) - return NULL; - - /* Try as a standard command */ - index = IW_IOCTL_IDX(cmd); - if (index < handlers->num_standard) - return handlers->standard[index]; - -#ifdef CONFIG_WEXT_PRIV - /* Try as a private command */ - index = cmd - SIOCIWFIRSTPRIV; - if (index < handlers->num_private) - return handlers->private[index]; -#endif - - /* Not found */ - return NULL; -} - -static int ioctl_standard_iw_point(struct iw_point *iwp, unsigned int cmd, - const struct iw_ioctl_description *descr, - iw_handler handler, struct net_device *dev, - struct iw_request_info *info) -{ - int err, extra_size, user_length = 0, essid_compat = 0; - char *extra; - - /* Calculate space needed by arguments. Always allocate - * for max space. - */ - extra_size = descr->max_tokens * descr->token_size; - - /* Check need for ESSID compatibility for WE < 21 */ - switch (cmd) { - case SIOCSIWESSID: - case SIOCGIWESSID: - case SIOCSIWNICKN: - case SIOCGIWNICKN: - if (iwp->length == descr->max_tokens + 1) - essid_compat = 1; - else if (IW_IS_SET(cmd) && (iwp->length != 0)) { - char essid[IW_ESSID_MAX_SIZE + 1]; - unsigned int len; - len = iwp->length * descr->token_size; - - if (len > IW_ESSID_MAX_SIZE) - return -EFAULT; - - err = copy_from_user(essid, iwp->pointer, len); - if (err) - return -EFAULT; - - if (essid[iwp->length - 1] == '\0') - essid_compat = 1; - } - break; - default: - break; - } - - iwp->length -= essid_compat; - - /* Check what user space is giving us */ - if (IW_IS_SET(cmd)) { - /* Check NULL pointer */ - if (!iwp->pointer && iwp->length != 0) - return -EFAULT; - /* Check if number of token fits within bounds */ - if (iwp->length > descr->max_tokens) - return -E2BIG; - if (iwp->length < descr->min_tokens) - return -EINVAL; - } else { - /* Check NULL pointer */ - if (!iwp->pointer) - return -EFAULT; - /* Save user space buffer size for checking */ - user_length = iwp->length; - - /* Don't check if user_length > max to allow forward - * compatibility. The test user_length < min is - * implied by the test at the end. - */ - - /* Support for very large requests */ - if ((descr->flags & IW_DESCR_FLAG_NOMAX) && - (user_length > descr->max_tokens)) { - /* Allow userspace to GET more than max so - * we can support any size GET requests. - * There is still a limit : -ENOMEM. - */ - extra_size = user_length * descr->token_size; - - /* Note : user_length is originally a __u16, - * and token_size is controlled by us, - * so extra_size won't get negative and - * won't overflow... - */ - } - } - - /* kzalloc() ensures NULL-termination for essid_compat. */ - extra = kzalloc(extra_size, GFP_KERNEL); - if (!extra) - return -ENOMEM; - - /* If it is a SET, get all the extra data in here */ - if (IW_IS_SET(cmd) && (iwp->length != 0)) { - if (copy_from_user(extra, iwp->pointer, - iwp->length * - descr->token_size)) { - err = -EFAULT; - goto out; - } - - if (cmd == SIOCSIWENCODEEXT) { - struct iw_encode_ext *ee = (void *) extra; - - if (iwp->length < sizeof(*ee) + ee->key_len) - return -EFAULT; - } - } - - if (IW_IS_GET(cmd) && !(descr->flags & IW_DESCR_FLAG_NOMAX)) { - /* - * If this is a GET, but not NOMAX, it means that the extra - * data is not bounded by userspace, but by max_tokens. Thus - * set the length to max_tokens. This matches the extra data - * allocation. - * The driver should fill it with the number of tokens it - * provided, and it may check iwp->length rather than having - * knowledge of max_tokens. If the driver doesn't change the - * iwp->length, this ioctl just copies back max_token tokens - * filled with zeroes. Hopefully the driver isn't claiming - * them to be valid data. - */ - iwp->length = descr->max_tokens; - } - - err = handler(dev, info, (union iwreq_data *) iwp, extra); - - iwp->length += essid_compat; - - /* If we have something to return to the user */ - if (!err && IW_IS_GET(cmd)) { - /* Check if there is enough buffer up there */ - if (user_length < iwp->length) { - err = -E2BIG; - goto out; - } - - if (copy_to_user(iwp->pointer, extra, - iwp->length * - descr->token_size)) { - err = -EFAULT; - goto out; - } - } - - /* Generate an event to notify listeners of the change */ - if ((descr->flags & IW_DESCR_FLAG_EVENT) && - ((err == 0) || (err == -EIWCOMMIT))) { - union iwreq_data *data = (union iwreq_data *) iwp; - - if (descr->flags & IW_DESCR_FLAG_RESTRICT) - /* If the event is restricted, don't - * export the payload. - */ - wireless_send_event(dev, cmd, data, NULL); - else - wireless_send_event(dev, cmd, data, extra); - } - -out: - kfree(extra); - return err; -} - -/* - * Call the commit handler in the driver - * (if exist and if conditions are right) - * - * Note : our current commit strategy is currently pretty dumb, - * but we will be able to improve on that... - * The goal is to try to agreagate as many changes as possible - * before doing the commit. Drivers that will define a commit handler - * are usually those that need a reset after changing parameters, so - * we want to minimise the number of reset. - * A cool idea is to use a timer : at each "set" command, we re-set the - * timer, when the timer eventually fires, we call the driver. - * Hopefully, more on that later. - * - * Also, I'm waiting to see how many people will complain about the - * netif_running(dev) test. I'm open on that one... - * Hopefully, the driver will remember to do a commit in "open()" ;-) - */ -int call_commit_handler(struct net_device *dev) -{ -#ifdef CONFIG_WIRELESS_EXT - if ((netif_running(dev)) && - (dev->wireless_handlers->standard[0] != NULL)) - /* Call the commit handler on the driver */ - return dev->wireless_handlers->standard[0](dev, NULL, - NULL, NULL); - else - return 0; /* Command completed successfully */ -#else - /* cfg80211 has no commit */ - return 0; -#endif -} - -/* - * Main IOCTl dispatcher. - * Check the type of IOCTL and call the appropriate wrapper... - */ -static int wireless_process_ioctl(struct net *net, struct ifreq *ifr, - unsigned int cmd, - struct iw_request_info *info, - wext_ioctl_func standard, - wext_ioctl_func private) -{ - struct iwreq *iwr = (struct iwreq *) ifr; - struct net_device *dev; - iw_handler handler; - - /* Permissions are already checked in dev_ioctl() before calling us. - * The copy_to/from_user() of ifr is also dealt with in there */ - - /* Make sure the device exist */ - if ((dev = __dev_get_by_name(net, ifr->ifr_name)) == NULL) - return -ENODEV; - - /* A bunch of special cases, then the generic case... - * Note that 'cmd' is already filtered in dev_ioctl() with - * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ - if (cmd == SIOCGIWSTATS) - return standard(dev, iwr, cmd, info, - &iw_handler_get_iwstats); - -#ifdef CONFIG_WEXT_PRIV - if (cmd == SIOCGIWPRIV && dev->wireless_handlers) - return standard(dev, iwr, cmd, info, - iw_handler_get_private); -#endif - - /* Basic check */ - if (!netif_device_present(dev)) - return -ENODEV; - - /* New driver API : try to find the handler */ - handler = get_handler(dev, cmd); - if (handler) { - /* Standard and private are not the same */ - if (cmd < SIOCIWFIRSTPRIV) - return standard(dev, iwr, cmd, info, handler); - else if (private) - return private(dev, iwr, cmd, info, handler); - } - /* Old driver API : call driver ioctl handler */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)) - if (dev->netdev_ops->ndo_do_ioctl) - return dev->netdev_ops->ndo_do_ioctl(dev, ifr, cmd); -#else - if (dev->do_ioctl) - return dev->do_ioctl(dev, ifr, cmd); -#endif - return -EOPNOTSUPP; -} - -/* If command is `set a parameter', or `get the encoding parameters', - * check if the user has the right to do it. - */ -static int wext_permission_check(unsigned int cmd) -{ - if ((IW_IS_SET(cmd) || cmd == SIOCGIWENCODE || - cmd == SIOCGIWENCODEEXT) && - !capable(CAP_NET_ADMIN)) - return -EPERM; - - return 0; -} - -/* entry point from dev ioctl */ -static int wext_ioctl_dispatch(struct net *net, struct ifreq *ifr, - unsigned int cmd, struct iw_request_info *info, - wext_ioctl_func standard, - wext_ioctl_func private) -{ - int ret = wext_permission_check(cmd); - - if (ret) - return ret; - - dev_load(net, ifr->ifr_name); - rtnl_lock(); - ret = wireless_process_ioctl(net, ifr, cmd, info, standard, private); - rtnl_unlock(); - - return ret; -} - -/* - * Wrapper to call a standard Wireless Extension handler. - * We do various checks and also take care of moving data between - * user space and kernel space. - */ -static int ioctl_standard_call(struct net_device * dev, - struct iwreq *iwr, - unsigned int cmd, - struct iw_request_info *info, - iw_handler handler) -{ - const struct iw_ioctl_description * descr; - int ret = -EINVAL; - - /* Get the description of the IOCTL */ - if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) - return -EOPNOTSUPP; - descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); - - /* Check if we have a pointer to user space data or not */ - if (descr->header_type != IW_HEADER_TYPE_POINT) { - - /* No extra arguments. Trivial to handle */ - ret = handler(dev, info, &(iwr->u), NULL); - - /* Generate an event to notify listeners of the change */ - if ((descr->flags & IW_DESCR_FLAG_EVENT) && - ((ret == 0) || (ret == -EIWCOMMIT))) - wireless_send_event(dev, cmd, &(iwr->u), NULL); - } else { - ret = ioctl_standard_iw_point(&iwr->u.data, cmd, descr, - handler, dev, info); - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - /* Here, we will generate the appropriate event if needed */ - - return ret; -} - - -int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, - void __user *arg) -{ - struct iw_request_info info = { .cmd = cmd, .flags = 0 }; - int ret; - - ret = wext_ioctl_dispatch(net, ifr, cmd, &info, - ioctl_standard_call, - ioctl_private_call); - if (ret >= 0 && - IW_IS_GET(cmd) && - copy_to_user(arg, ifr, sizeof(struct iwreq))) - return -EFAULT; - - return ret; -} - -#ifdef CONFIG_COMPAT -static int compat_standard_call(struct net_device *dev, - struct iwreq *iwr, - unsigned int cmd, - struct iw_request_info *info, - iw_handler handler) -{ - const struct iw_ioctl_description *descr; - struct compat_iw_point *iwp_compat; - struct iw_point iwp; - int err; - - descr = standard_ioctl + IW_IOCTL_IDX(cmd); - - if (descr->header_type != IW_HEADER_TYPE_POINT) - return ioctl_standard_call(dev, iwr, cmd, info, handler); - - iwp_compat = (struct compat_iw_point *) &iwr->u.data; - iwp.pointer = compat_ptr(iwp_compat->pointer); - iwp.length = iwp_compat->length; - iwp.flags = iwp_compat->flags; - - err = ioctl_standard_iw_point(&iwp, cmd, descr, handler, dev, info); - - iwp_compat->pointer = ptr_to_compat(iwp.pointer); - iwp_compat->length = iwp.length; - iwp_compat->flags = iwp.flags; - - return err; -} - -int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, - unsigned long arg) -{ - void __user *argp = (void __user *)arg; - struct iw_request_info info; - struct iwreq iwr; - char *colon; - int ret; - - if (copy_from_user(&iwr, argp, sizeof(struct iwreq))) - return -EFAULT; - - iwr.ifr_name[IFNAMSIZ-1] = 0; - colon = strchr(iwr.ifr_name, ':'); - if (colon) - *colon = 0; - - info.cmd = cmd; - info.flags = IW_REQUEST_FLAG_COMPAT; - - ret = wext_ioctl_dispatch(net, (struct ifreq *) &iwr, cmd, &info, - compat_standard_call, - compat_private_call); - - if (ret >= 0 && - IW_IS_GET(cmd) && - copy_to_user(argp, &iwr, sizeof(struct iwreq))) - return -EFAULT; - - return ret; -} -#endif diff --git a/net/wireless_ath/wext-priv.c b/net/wireless_ath/wext-priv.c deleted file mode 100755 index 674d426..0000000 --- a/net/wireless_ath/wext-priv.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * This file implement the Wireless Extensions priv API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * - * (As all part of the Linux kernel, this file is GPL) - */ -#include <linux/slab.h> -#include <linux/wireless.h> -#include <linux/netdevice.h> -#include <net/iw_handler.h> -#include <net/wext.h> - -int iw_handler_get_private(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - /* Check if the driver has something to export */ - if ((dev->wireless_handlers->num_private_args == 0) || - (dev->wireless_handlers->private_args == NULL)) - return -EOPNOTSUPP; - - /* Check if there is enough buffer up there */ - if (wrqu->data.length < dev->wireless_handlers->num_private_args) { - /* User space can't know in advance how large the buffer - * needs to be. Give it a hint, so that we can support - * any size buffer we want somewhat efficiently... */ - wrqu->data.length = dev->wireless_handlers->num_private_args; - return -E2BIG; - } - - /* Set the number of available ioctls. */ - wrqu->data.length = dev->wireless_handlers->num_private_args; - - /* Copy structure to the user buffer. */ - memcpy(extra, dev->wireless_handlers->private_args, - sizeof(struct iw_priv_args) * wrqu->data.length); - - return 0; -} - -/* Size (in bytes) of the various private data types */ -static const char iw_priv_type_size[] = { - 0, /* IW_PRIV_TYPE_NONE */ - 1, /* IW_PRIV_TYPE_BYTE */ - 1, /* IW_PRIV_TYPE_CHAR */ - 0, /* Not defined */ - sizeof(__u32), /* IW_PRIV_TYPE_INT */ - sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ - sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ - 0, /* Not defined */ -}; - -static int get_priv_size(__u16 args) -{ - int num = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - - return num * iw_priv_type_size[type]; -} - -static int adjust_priv_size(__u16 args, struct iw_point *iwp) -{ - int num = iwp->length; - int max = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - - /* Make sure the driver doesn't goof up */ - if (max < num) - num = max; - - return num * iw_priv_type_size[type]; -} - -/* - * Wrapper to call a private Wireless Extension handler. - * We do various checks and also take care of moving data between - * user space and kernel space. - * It's not as nice and slimline as the standard wrapper. The cause - * is struct iw_priv_args, which was not really designed for the - * job we are going here. - * - * IMPORTANT : This function prevent to set and get data on the same - * IOCTL and enforce the SET/GET convention. Not doing it would be - * far too hairy... - * If you need to set and get data at the same time, please don't use - * a iw_handler but process it in your ioctl handler (i.e. use the - * old driver API). - */ -static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd, - const struct iw_priv_args **descrp) -{ - const struct iw_priv_args *descr; - int i, extra_size; - - descr = NULL; - for (i = 0; i < dev->wireless_handlers->num_private_args; i++) { - if (cmd == dev->wireless_handlers->private_args[i].cmd) { - descr = &dev->wireless_handlers->private_args[i]; - break; - } - } - - extra_size = 0; - if (descr) { - if (IW_IS_SET(cmd)) { - int offset = 0; /* For sub-ioctls */ - /* Check for sub-ioctl handler */ - if (descr->name[0] == '\0') - /* Reserve one int for sub-ioctl index */ - offset = sizeof(__u32); - - /* Size of set arguments */ - extra_size = get_priv_size(descr->set_args); - - /* Does it fits in iwr ? */ - if ((descr->set_args & IW_PRIV_SIZE_FIXED) && - ((extra_size + offset) <= IFNAMSIZ)) - extra_size = 0; - } else { - /* Size of get arguments */ - extra_size = get_priv_size(descr->get_args); - - /* Does it fits in iwr ? */ - if ((descr->get_args & IW_PRIV_SIZE_FIXED) && - (extra_size <= IFNAMSIZ)) - extra_size = 0; - } - } - *descrp = descr; - return extra_size; -} - -static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd, - const struct iw_priv_args *descr, - iw_handler handler, struct net_device *dev, - struct iw_request_info *info, int extra_size) -{ - char *extra; - int err; - - /* Check what user space is giving us */ - if (IW_IS_SET(cmd)) { - if (!iwp->pointer && iwp->length != 0) - return -EFAULT; - - if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK)) - return -E2BIG; - } else if (!iwp->pointer) - return -EFAULT; - - extra = kzalloc(extra_size, GFP_KERNEL); - if (!extra) - return -ENOMEM; - - /* If it is a SET, get all the extra data in here */ - if (IW_IS_SET(cmd) && (iwp->length != 0)) { - if (copy_from_user(extra, iwp->pointer, extra_size)) { - err = -EFAULT; - goto out; - } - } - - /* Call the handler */ - err = handler(dev, info, (union iwreq_data *) iwp, extra); - - /* If we have something to return to the user */ - if (!err && IW_IS_GET(cmd)) { - /* Adjust for the actual length if it's variable, - * avoid leaking kernel bits outside. - */ - if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) - extra_size = adjust_priv_size(descr->get_args, iwp); - - if (copy_to_user(iwp->pointer, extra, extra_size)) - err = -EFAULT; - } - -out: - kfree(extra); - return err; -} - -int ioctl_private_call(struct net_device *dev, struct iwreq *iwr, - unsigned int cmd, struct iw_request_info *info, - iw_handler handler) -{ - int extra_size = 0, ret = -EINVAL; - const struct iw_priv_args *descr; - - extra_size = get_priv_descr_and_size(dev, cmd, &descr); - - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - /* No extra arguments. Trivial to handle */ - ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); - } else { - ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr, - handler, dev, info, extra_size); - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} - -#ifdef CONFIG_COMPAT -int compat_private_call(struct net_device *dev, struct iwreq *iwr, - unsigned int cmd, struct iw_request_info *info, - iw_handler handler) -{ - const struct iw_priv_args *descr; - int ret, extra_size; - - extra_size = get_priv_descr_and_size(dev, cmd, &descr); - - /* Check if we have a pointer to user space data or not. */ - if (extra_size == 0) { - /* No extra arguments. Trivial to handle */ - ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u)); - } else { - struct compat_iw_point *iwp_compat; - struct iw_point iwp; - - iwp_compat = (struct compat_iw_point *) &iwr->u.data; - iwp.pointer = compat_ptr(iwp_compat->pointer); - iwp.length = iwp_compat->length; - iwp.flags = iwp_compat->flags; - - ret = ioctl_private_iw_point(&iwp, cmd, descr, - handler, dev, info, extra_size); - - iwp_compat->pointer = ptr_to_compat(iwp.pointer); - iwp_compat->length = iwp.length; - iwp_compat->flags = iwp.flags; - } - - /* Call commit handler if needed and defined */ - if (ret == -EIWCOMMIT) - ret = call_commit_handler(dev); - - return ret; -} -#endif diff --git a/net/wireless_ath/wext-proc.c b/net/wireless_ath/wext-proc.c deleted file mode 100755 index f2fbfa6..0000000 --- a/net/wireless_ath/wext-proc.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * This file implement the Wireless Extensions proc API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ - -/* - * The /proc/net/wireless file is a human readable user-space interface - * exporting various wireless specific statistics from the wireless devices. - * This is the most popular part of the Wireless Extensions ;-) - * - * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). - * The content of the file is basically the content of "struct iw_statistics". - */ - -#include <linux/module.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/wireless.h> -#include <linux/netdevice.h> -#include <linux/rtnetlink.h> -#include <net/iw_handler.h> -#include <net/wext.h> - - -static void wireless_seq_printf_stats(struct seq_file *seq, - struct net_device *dev) -{ - /* Get stats from the driver */ - struct iw_statistics *stats = get_wireless_stats(dev); - static struct iw_statistics nullstats = {}; - - /* show device if it's wireless regardless of current stats */ - if (!stats) { -#ifdef CONFIG_WIRELESS_EXT - if (dev->wireless_handlers) - stats = &nullstats; -#endif -#ifdef CONFIG_CFG80211 - if (dev->ieee80211_ptr) - stats = &nullstats; -#endif - } - - if (stats) { - seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d " - "%6d %6d %6d\n", - dev->name, stats->status, stats->qual.qual, - stats->qual.updated & IW_QUAL_QUAL_UPDATED - ? '.' : ' ', - ((__s32) stats->qual.level) - - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), - stats->qual.updated & IW_QUAL_LEVEL_UPDATED - ? '.' : ' ', - ((__s32) stats->qual.noise) - - ((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0), - stats->qual.updated & IW_QUAL_NOISE_UPDATED - ? '.' : ' ', - stats->discard.nwid, stats->discard.code, - stats->discard.fragment, stats->discard.retries, - stats->discard.misc, stats->miss.beacon); - - if (stats != &nullstats) - stats->qual.updated &= ~IW_QUAL_ALL_UPDATED; - } -} - -/* ---------------------------------------------------------------- */ -/* - * Print info for /proc/net/wireless (print all entries) - */ -static int wireless_dev_seq_show(struct seq_file *seq, void *v) -{ - might_sleep(); - - if (v == SEQ_START_TOKEN) - seq_printf(seq, "Inter-| sta-| Quality | Discarded " - "packets | Missed | WE\n" - " face | tus | link level noise | nwid " - "crypt frag retry misc | beacon | %d\n", - WIRELESS_EXT); - else - wireless_seq_printf_stats(seq, v); - return 0; -} - -static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct net *net = seq_file_net(seq); - loff_t off; - struct net_device *dev; - - rtnl_lock(); - if (!*pos) - return SEQ_START_TOKEN; - - off = 1; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)) - for_each_netdev(net, dev) -#else - for_each_netdev(net) -#endif - if (off++ == *pos) - return dev; - return NULL; -} - -static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct net *net = seq_file_net(seq); - - ++*pos; - - return v == SEQ_START_TOKEN ? - first_net_device(net) : next_net_device(v); -} - -static void wireless_dev_seq_stop(struct seq_file *seq, void *v) -{ - rtnl_unlock(); -} - -static const struct seq_operations wireless_seq_ops = { - .start = wireless_dev_seq_start, - .next = wireless_dev_seq_next, - .stop = wireless_dev_seq_stop, - .show = wireless_dev_seq_show, -}; - -static int seq_open_wireless(struct inode *inode, struct file *file) -{ - return seq_open_net(inode, file, &wireless_seq_ops, - sizeof(struct seq_net_private)); -} - -static const struct file_operations wireless_seq_fops = { - .owner = THIS_MODULE, - .open = seq_open_wireless, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_net, -}; - -int __net_init wext_proc_init(struct net *net) -{ - /* Create /proc/net/wireless entry */ - if (!proc_net_fops_create(net, "wireless", S_IRUGO, &wireless_seq_fops)) - return -ENOMEM; - - return 0; -} - -void __net_exit wext_proc_exit(struct net *net) -{ - proc_net_remove(net, "wireless"); -} diff --git a/net/wireless_ath/wext-sme.c b/net/wireless_ath/wext-sme.c deleted file mode 100755 index be681b0..0000000 --- a/net/wireless_ath/wext-sme.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * cfg80211 wext compat for managed mode. - * - * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> - * Copyright (C) 2009 Intel Corporation. All rights reserved. - */ - -#include <linux/export.h> -#include <linux/etherdevice.h> -#include <linux/if_arp.h> -#include <linux/slab.h> -#include <net/cfg80211.h> -#include <net/cfg80211-wext.h> -#include "wext-compat.h" -#include "nl80211.h" - -int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev) -{ - struct cfg80211_cached_keys *ck = NULL; - const u8 *prev_bssid = NULL; - int err, i; - - ASSERT_RDEV_LOCK(rdev); - ASSERT_WDEV_LOCK(wdev); - -#ifdef CONFIG_MACH_PX -#else - if (!netif_running(wdev->netdev)) - return 0; -#endif - - wdev->wext.connect.ie = wdev->wext.ie; - wdev->wext.connect.ie_len = wdev->wext.ie_len; - - if (wdev->wext.keys) { - wdev->wext.keys->def = wdev->wext.default_key; - wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key; - if (wdev->wext.default_key != -1) - wdev->wext.connect.privacy = true; - } - - if (!wdev->wext.connect.ssid_len) - return 0; - - if (wdev->wext.keys) { - ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL); - if (!ck) - return -ENOMEM; - for (i = 0; i < 6; i++) - ck->params[i].key = ck->data[i]; - } - - if (wdev->wext.prev_bssid_valid) - prev_bssid = wdev->wext.prev_bssid; - - err = __cfg80211_connect(rdev, wdev->netdev, - &wdev->wext.connect, ck, prev_bssid); - if (err) - kfree(ck); - - return err; -} - -int cfg80211_mgd_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *wextfreq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan = NULL; - int err, freq; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - freq = cfg80211_wext_freq(wdev->wiphy, wextfreq); - if (freq < 0) - return freq; - - if (freq) { - chan = ieee80211_get_channel(wdev->wiphy, freq); - if (!chan) - return -EINVAL; - if (chan->flags & IEEE80211_CHAN_DISABLED) - return -EINVAL; - } - - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - - if (wdev->sme_state != CFG80211_SME_IDLE) { - bool event = true; - - if (wdev->wext.connect.channel == chan) { - err = 0; - goto out; - } - - /* if SSID set, we'll try right again, avoid event */ - if (wdev->wext.connect.ssid_len) - event = false; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, event); - if (err) - goto out; - } - - - wdev->wext.connect.channel = chan; - - /* SSID is not set, we just want to switch channel */ - if (chan && !wdev->wext.connect.ssid_len) { - err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); - goto out; - } - - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - return err; -} - -int cfg80211_mgd_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct ieee80211_channel *chan = NULL; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - wdev_lock(wdev); - if (wdev->current_bss) - chan = wdev->current_bss->pub.channel; - else if (wdev->wext.connect.channel) - chan = wdev->wext.connect.channel; - wdev_unlock(wdev); - - if (chan) { - freq->m = chan->center_freq; - freq->e = 6; - return 0; - } - - /* no channel if not joining */ - return -EINVAL; -} - -int cfg80211_mgd_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - size_t len = data->length; - int err; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - if (!data->flags) - len = 0; - - /* iwconfig uses nul termination in SSID.. */ - if (len > 0 && ssid[len - 1] == '\0') - len--; - - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - - err = 0; - - if (wdev->sme_state != CFG80211_SME_IDLE) { - bool event = true; - - if (wdev->wext.connect.ssid && len && - len == wdev->wext.connect.ssid_len && - memcmp(wdev->wext.connect.ssid, ssid, len) == 0) - goto out; - - /* if SSID set now, we'll try to connect, avoid event */ - if (len) - event = false; - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, event); - if (err) - goto out; - } - - wdev->wext.prev_bssid_valid = false; - wdev->wext.connect.ssid = wdev->wext.ssid; - memcpy(wdev->wext.ssid, ssid, len); - wdev->wext.connect.ssid_len = len; - - wdev->wext.connect.crypto.control_port = false; - wdev->wext.connect.crypto.control_port_ethertype = - cpu_to_be16(ETH_P_PAE); - - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - return err; -} - -int cfg80211_mgd_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - data->flags = 0; - - wdev_lock(wdev); - if (wdev->current_bss) { - const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub, - WLAN_EID_SSID); - if (ie) { - data->flags = 1; - data->length = ie[1]; - memcpy(ssid, ie + 2, data->length); - } - } else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) { - data->flags = 1; - data->length = wdev->wext.connect.ssid_len; - memcpy(ssid, wdev->wext.connect.ssid, data->length); - } - wdev_unlock(wdev); - - return 0; -} - -int cfg80211_mgd_wext_siwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 *bssid = ap_addr->sa_data; - int err; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - if (ap_addr->sa_family != ARPHRD_ETHER) - return -EINVAL; - - /* automatic mode */ - if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) - bssid = NULL; - - cfg80211_lock_rdev(rdev); - mutex_lock(&rdev->devlist_mtx); - wdev_lock(wdev); - - if (wdev->sme_state != CFG80211_SME_IDLE) { - err = 0; - /* both automatic */ - if (!bssid && !wdev->wext.connect.bssid) - goto out; - - /* fixed already - and no change */ - if (wdev->wext.connect.bssid && bssid && - compare_ether_addr(bssid, wdev->wext.connect.bssid) == 0) - goto out; - - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); - if (err) - goto out; - } - - if (bssid) { - memcpy(wdev->wext.bssid, bssid, ETH_ALEN); - wdev->wext.connect.bssid = wdev->wext.bssid; - } else - wdev->wext.connect.bssid = NULL; - - err = cfg80211_mgd_wext_connect(rdev, wdev); - out: - wdev_unlock(wdev); - mutex_unlock(&rdev->devlist_mtx); - cfg80211_unlock_rdev(rdev); - return err; -} - -int cfg80211_mgd_wext_giwap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - - /* call only for station! */ - if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) - return -EINVAL; - - ap_addr->sa_family = ARPHRD_ETHER; - - wdev_lock(wdev); - if (wdev->current_bss) - memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN); - else - memset(ap_addr->sa_data, 0, ETH_ALEN); - wdev_unlock(wdev); - - return 0; -} - -int cfg80211_wext_siwgenie(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - u8 *ie = extra; - int ie_len = data->length, err; - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - if (!ie_len) - ie = NULL; - - wdev_lock(wdev); - - /* no change */ - err = 0; - if (wdev->wext.ie_len == ie_len && - memcmp(wdev->wext.ie, ie, ie_len) == 0) - goto out; - - if (ie_len) { - ie = kmemdup(extra, ie_len, GFP_KERNEL); - if (!ie) { - err = -ENOMEM; - goto out; - } - } else - ie = NULL; - - kfree(wdev->wext.ie); - wdev->wext.ie = ie; - wdev->wext.ie_len = ie_len; - - if (wdev->sme_state != CFG80211_SME_IDLE) { - err = __cfg80211_disconnect(rdev, dev, - WLAN_REASON_DEAUTH_LEAVING, false); - if (err) - goto out; - } - - /* userspace better not think we'll reconnect */ - err = 0; - out: - wdev_unlock(wdev); - return err; -} - -int cfg80211_wext_siwmlme(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *extra) -{ - struct wireless_dev *wdev = dev->ieee80211_ptr; - struct iw_mlme *mlme = (struct iw_mlme *)extra; - struct cfg80211_registered_device *rdev; - int err; - - if (!wdev) - return -EOPNOTSUPP; - - rdev = wiphy_to_dev(wdev->wiphy); - - if (wdev->iftype != NL80211_IFTYPE_STATION) - return -EINVAL; - - if (mlme->addr.sa_family != ARPHRD_ETHER) - return -EINVAL; - - wdev_lock(wdev); - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - case IW_MLME_DISASSOC: - err = __cfg80211_disconnect(rdev, dev, mlme->reason_code, - true); - break; - default: - err = -EOPNOTSUPP; - break; - } - wdev_unlock(wdev); - - return err; -} diff --git a/net/wireless_ath/wext-spy.c b/net/wireless_ath/wext-spy.c deleted file mode 100755 index 9e0dc1b..0000000 --- a/net/wireless_ath/wext-spy.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * This file implement the Wireless Extensions spy API. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ - -#include <linux/wireless.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/export.h> -#include <net/iw_handler.h> -#include <net/arp.h> -#include <net/wext.h> - -static inline struct iw_spy_data *get_spydata(struct net_device *dev) -{ - /* This is the new way */ - if (dev->wireless_data) - return dev->wireless_data->spy_data; - return NULL; -} - -int iw_handler_set_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - /* Disable spy collection while we copy the addresses. - * While we copy addresses, any call to wireless_spy_update() - * will NOP. This is OK, as anyway the addresses are changing. */ - spydata->spy_number = 0; - - /* We want to operate without locking, because wireless_spy_update() - * most likely will happen in the interrupt handler, and therefore - * have its own locking constraints and needs performance. - * The rtnl_lock() make sure we don't race with the other iw_handlers. - * This make sure wireless_spy_update() "see" that the spy list - * is temporarily disabled. */ - smp_wmb(); - - /* Are there are addresses to copy? */ - if (wrqu->data.length > 0) { - int i; - - /* Copy addresses */ - for (i = 0; i < wrqu->data.length; i++) - memcpy(spydata->spy_address[i], address[i].sa_data, - ETH_ALEN); - /* Reset stats */ - memset(spydata->spy_stat, 0, - sizeof(struct iw_quality) * IW_MAX_SPY); - } - - /* Make sure above is updated before re-enabling */ - smp_wmb(); - - /* Enable addresses */ - spydata->spy_number = wrqu->data.length; - - return 0; -} -EXPORT_SYMBOL(iw_handler_set_spy); - -int iw_handler_get_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - int i; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - wrqu->data.length = spydata->spy_number; - - /* Copy addresses. */ - for (i = 0; i < spydata->spy_number; i++) { - memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); - address[i].sa_family = AF_UNIX; - } - /* Copy stats to the user buffer (just after). */ - if (spydata->spy_number > 0) - memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), - spydata->spy_stat, - sizeof(struct iw_quality) * spydata->spy_number); - /* Reset updated flags. */ - for (i = 0; i < spydata->spy_number; i++) - spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED; - return 0; -} -EXPORT_SYMBOL(iw_handler_get_spy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : set spy threshold - */ -int iw_handler_set_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - /* Just do it */ - memcpy(&(spydata->spy_thr_low), &(threshold->low), - 2 * sizeof(struct iw_quality)); - - /* Clear flag */ - memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); - - return 0; -} -EXPORT_SYMBOL(iw_handler_set_thrspy); - -/*------------------------------------------------------------------*/ -/* - * Standard Wireless Handler : get spy threshold - */ -int iw_handler_get_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra) -{ - struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return -EOPNOTSUPP; - - /* Just do it */ - memcpy(&(threshold->low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - - return 0; -} -EXPORT_SYMBOL(iw_handler_get_thrspy); - -/*------------------------------------------------------------------*/ -/* - * Prepare and send a Spy Threshold event - */ -static void iw_send_thrspy_event(struct net_device * dev, - struct iw_spy_data * spydata, - unsigned char * address, - struct iw_quality * wstats) -{ - union iwreq_data wrqu; - struct iw_thrspy threshold; - - /* Init */ - wrqu.data.length = 1; - wrqu.data.flags = 0; - /* Copy address */ - memcpy(threshold.addr.sa_data, address, ETH_ALEN); - threshold.addr.sa_family = ARPHRD_ETHER; - /* Copy stats */ - memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); - /* Copy also thresholds */ - memcpy(&(threshold.low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); -} - -/* ---------------------------------------------------------------- */ -/* - * Call for the driver to update the spy data. - * For now, the spy data is a simple array. As the size of the array is - * small, this is good enough. If we wanted to support larger number of - * spy addresses, we should use something more efficient... - */ -void wireless_spy_update(struct net_device * dev, - unsigned char * address, - struct iw_quality * wstats) -{ - struct iw_spy_data * spydata = get_spydata(dev); - int i; - int match = -1; - - /* Make sure driver is not buggy or using the old API */ - if (!spydata) - return; - - /* Update all records that match */ - for (i = 0; i < spydata->spy_number; i++) - if (!compare_ether_addr(address, spydata->spy_address[i])) { - memcpy(&(spydata->spy_stat[i]), wstats, - sizeof(struct iw_quality)); - match = i; - } - - /* Generate an event if we cross the spy threshold. - * To avoid event storms, we have a simple hysteresis : we generate - * event only when we go under the low threshold or above the - * high threshold. */ - if (match >= 0) { - if (spydata->spy_thr_under[match]) { - if (wstats->level > spydata->spy_thr_high.level) { - spydata->spy_thr_under[match] = 0; - iw_send_thrspy_event(dev, spydata, - address, wstats); - } - } else { - if (wstats->level < spydata->spy_thr_low.level) { - spydata->spy_thr_under[match] = 1; - iw_send_thrspy_event(dev, spydata, - address, wstats); - } - } - } -} -EXPORT_SYMBOL(wireless_spy_update); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 6b9e4e1..7c8e0cb 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1349,16 +1349,14 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) BUG(); } xdst = dst_alloc(dst_ops, NULL, 0, 0, 0); + memset(&xdst->u.rt6.rt6i_table, 0, sizeof(*xdst) - sizeof(struct dst_entry)); + xfrm_policy_put_afinfo(afinfo); - if (likely(xdst)) { - memset(&xdst->u.rt6.rt6i_table, 0, - sizeof(*xdst) - sizeof(struct dst_entry)); + if (likely(xdst)) xdst->flo.ops = &xfrm_bundle_fc_ops; - } else + else xdst = ERR_PTR(-ENOBUFS); - xfrm_policy_put_afinfo(afinfo); - return xdst; } |