Commit fcde38d4 authored by Maxim Krasnyansky's avatar Maxim Krasnyansky Committed by Linus Torvalds

Sync up Bluetooth core with 2.4.x.

SMP locking fixes. 
Support for Hotplug.
Support for L2CAP connectionless channels (SOCK_DGRAM).
HCI filter handling fixes.
Other minor fixes and cleanups.
parent f5076217
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#define BTPROTO_HCI 1 #define BTPROTO_HCI 1
#define BTPROTO_SCO 2 #define BTPROTO_SCO 2
#define BTPROTO_RFCOMM 3 #define BTPROTO_RFCOMM 3
#define BTPROTO_BNEP 4
#define SOL_HCI 0 #define SOL_HCI 0
#define SOL_L2CAP 6 #define SOL_L2CAP 6
...@@ -199,14 +200,4 @@ int hci_sock_cleanup(void); ...@@ -199,14 +200,4 @@ int hci_sock_cleanup(void);
int bterr(__u16 code); int bterr(__u16 code);
#ifndef MODULE_LICENSE
#define MODULE_LICENSE(x)
#endif
#ifndef list_for_each_safe
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
#endif
#endif /* __BLUETOOTH_H */ #endif /* __BLUETOOTH_H */
...@@ -113,10 +113,10 @@ enum { ...@@ -113,10 +113,10 @@ enum {
#define ACL_PTYPE_MASK (~SCO_PTYPE_MASK) #define ACL_PTYPE_MASK (~SCO_PTYPE_MASK)
/* ACL flags */ /* ACL flags */
#define ACL_CONT 0x0001 #define ACL_CONT 0x01
#define ACL_START 0x0002 #define ACL_START 0x02
#define ACL_ACTIVE_BCAST 0x0010 #define ACL_ACTIVE_BCAST 0x04
#define ACL_PICO_BCAST 0x0020 #define ACL_PICO_BCAST 0x08
/* Baseband links */ /* Baseband links */
#define SCO_LINK 0x00 #define SCO_LINK 0x00
...@@ -542,7 +542,7 @@ typedef struct { ...@@ -542,7 +542,7 @@ typedef struct {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 role; __u8 role;
} __attribute__ ((packed)) evt_role_change; } __attribute__ ((packed)) evt_role_change;
#define EVT_ROLE_CHANGE_SIZE 1 #define EVT_ROLE_CHANGE_SIZE 8
#define EVT_PIN_CODE_REQ 0x16 #define EVT_PIN_CODE_REQ 0x16
typedef struct { typedef struct {
...@@ -658,6 +658,12 @@ struct sockaddr_hci { ...@@ -658,6 +658,12 @@ struct sockaddr_hci {
#define HCI_DEV_NONE 0xffff #define HCI_DEV_NONE 0xffff
struct hci_filter { struct hci_filter {
unsigned long type_mask;
unsigned long event_mask[2];
__u16 opcode;
};
struct hci_ufilter {
__u32 type_mask; __u32 type_mask;
__u32 event_mask[2]; __u32 event_mask[2];
__u16 opcode; __u16 opcode;
...@@ -668,20 +674,6 @@ struct hci_filter { ...@@ -668,20 +674,6 @@ struct hci_filter {
#define HCI_FLT_OGF_BITS 63 #define HCI_FLT_OGF_BITS 63
#define HCI_FLT_OCF_BITS 127 #define HCI_FLT_OCF_BITS 127
#if BITS_PER_LONG == 64
static inline void hci_set_bit(int nr, void *addr)
{
*((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
}
static inline int hci_test_bit(int nr, void *addr)
{
return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
}
#else
#define hci_set_bit set_bit
#define hci_test_bit test_bit
#endif
/* Ioctl requests structures */ /* Ioctl requests structures */
struct hci_dev_stats { struct hci_dev_stats {
__u32 err_rx; __u32 err_rx;
......
...@@ -149,7 +149,7 @@ struct hci_conn { ...@@ -149,7 +149,7 @@ struct hci_conn {
extern struct hci_proto *hci_proto[]; extern struct hci_proto *hci_proto[];
extern struct list_head hdev_list; extern struct list_head hdev_list;
extern spinlock_t hdev_list_lock; extern rwlock_t hdev_list_lock;
/* ----- Inquiry cache ----- */ /* ----- Inquiry cache ----- */
#define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds #define INQUIRY_CACHE_AGE_MAX (HZ*30) // 30 seconds
...@@ -339,8 +339,8 @@ static inline void hci_sched_tx(struct hci_dev *hdev) ...@@ -339,8 +339,8 @@ static inline void hci_sched_tx(struct hci_dev *hdev)
/* ----- HCI protocols ----- */ /* ----- HCI protocols ----- */
struct hci_proto { struct hci_proto {
char *name; char *name;
__u32 id; unsigned int id;
__u32 flags; unsigned long flags;
void *priv; void *priv;
...@@ -450,12 +450,11 @@ struct hci_pinfo { ...@@ -450,12 +450,11 @@ struct hci_pinfo {
#define HCI_SFLT_MAX_OGF 4 #define HCI_SFLT_MAX_OGF 4
struct hci_sec_filter { struct hci_sec_filter {
__u32 type_mask; unsigned long type_mask;
__u32 event_mask[2]; unsigned long event_mask[2];
__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; unsigned long ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
}; };
/* ----- HCI requests ----- */ /* ----- HCI requests ----- */
#define HCI_REQ_DONE 0 #define HCI_REQ_DONE 0
#define HCI_REQ_PEND 1 #define HCI_REQ_PEND 1
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $ * $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $
*/ */
#define VERSION "2.0" #define VERSION "2.2"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
#endif #endif
/* Bluetooth sockets */ /* Bluetooth sockets */
#define BLUEZ_MAX_PROTO 4 #define BLUEZ_MAX_PROTO 5
static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO]; static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
static kmem_cache_t *bluez_sock_cache; static kmem_cache_t *bluez_sock_cache;
...@@ -136,18 +136,18 @@ struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int p ...@@ -136,18 +136,18 @@ struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int p
void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk) void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
{ {
write_lock(&l->lock); write_lock_bh(&l->lock);
sk->next = l->head; sk->next = l->head;
l->head = sk; l->head = sk;
sock_hold(sk); sock_hold(sk);
write_unlock(&l->lock); write_unlock_bh(&l->lock);
} }
void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
{ {
struct sock **skp; struct sock **skp;
write_lock(&l->lock); write_lock_bh(&l->lock);
for (skp = &l->head; *skp; skp = &((*skp)->next)) { for (skp = &l->head; *skp; skp = &((*skp)->next)) {
if (*skp == sk) { if (*skp == sk) {
*skp = sk->next; *skp = sk->next;
...@@ -155,7 +155,7 @@ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk) ...@@ -155,7 +155,7 @@ void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
break; break;
} }
} }
write_unlock(&l->lock); write_unlock_bh(&l->lock);
} }
void bluez_accept_enqueue(struct sock *parent, struct sock *sk) void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
...@@ -265,6 +265,9 @@ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table ...@@ -265,6 +265,9 @@ unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table
if (sk->state == BT_CLOSED) if (sk->state == BT_CLOSED)
mask |= POLLHUP; mask |= POLLHUP;
if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
return mask;
if (sock_writeable(sk)) if (sock_writeable(sk))
mask |= POLLOUT | POLLWRNORM | POLLWRBAND; mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
else else
......
...@@ -73,7 +73,7 @@ void hci_acl_connect(struct hci_conn *conn) ...@@ -73,7 +73,7 @@ void hci_acl_connect(struct hci_conn *conn)
bacpy(&cp.bdaddr, &conn->dst); bacpy(&cp.bdaddr, &conn->dst);
if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) && if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
inquiry_entry_age(ie) > INQUIRY_ENTRY_AGE_MAX) { inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
cp.pscan_rep_mode = ie->info.pscan_rep_mode; cp.pscan_rep_mode = ie->info.pscan_rep_mode;
cp.pscan_mode = ie->info.pscan_mode; cp.pscan_mode = ie->info.pscan_mode;
cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000); cp.clock_offset = ie->info.clock_offset | __cpu_to_le16(0x8000);
...@@ -188,9 +188,6 @@ int hci_conn_del(struct hci_conn *conn) ...@@ -188,9 +188,6 @@ int hci_conn_del(struct hci_conn *conn)
acl->link = NULL; acl->link = NULL;
hci_conn_put(acl); hci_conn_put(acl);
} }
/* Unacked frames */
hdev->sco_cnt += conn->sent;
} else { } else {
struct hci_conn *sco = conn->link; struct hci_conn *sco = conn->link;
if (sco) if (sco)
...@@ -220,7 +217,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) ...@@ -220,7 +217,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
BT_DBG("%s -> %s", batostr(src), batostr(dst)); BT_DBG("%s -> %s", batostr(src), batostr(dst));
spin_lock_bh(&hdev_list_lock); read_lock_bh(&hdev_list_lock);
list_for_each(p, &hdev_list) { list_for_each(p, &hdev_list) {
struct hci_dev *d; struct hci_dev *d;
...@@ -248,7 +245,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) ...@@ -248,7 +245,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
if (hdev) if (hdev)
hci_dev_hold(hdev); hci_dev_hold(hdev);
spin_unlock_bh(&hdev_list_lock); read_unlock_bh(&hdev_list_lock);
return hdev; return hdev;
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kmod.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/errno.h> #include <linux/errno.h>
...@@ -66,7 +67,7 @@ rwlock_t hci_task_lock = RW_LOCK_UNLOCKED; ...@@ -66,7 +67,7 @@ rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
/* HCI device list */ /* HCI device list */
LIST_HEAD(hdev_list); LIST_HEAD(hdev_list);
spinlock_t hdev_list_lock; rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
/* HCI protocols */ /* HCI protocols */
#define HCI_MAX_PROTO 2 #define HCI_MAX_PROTO 2
...@@ -75,7 +76,6 @@ struct hci_proto *hci_proto[HCI_MAX_PROTO]; ...@@ -75,7 +76,6 @@ struct hci_proto *hci_proto[HCI_MAX_PROTO];
/* HCI notifiers list */ /* HCI notifiers list */
static struct notifier_block *hci_notifier; static struct notifier_block *hci_notifier;
/* ---- HCI notifications ---- */ /* ---- HCI notifications ---- */
int hci_register_notifier(struct notifier_block *nb) int hci_register_notifier(struct notifier_block *nb)
...@@ -93,6 +93,32 @@ void hci_notify(struct hci_dev *hdev, int event) ...@@ -93,6 +93,32 @@ void hci_notify(struct hci_dev *hdev, int event)
notifier_call_chain(&hci_notifier, event, hdev); notifier_call_chain(&hci_notifier, event, hdev);
} }
/* ---- HCI hotplug support ---- */
#ifdef CONFIG_HOTPLUG
static int hci_run_hotplug(char *dev, char *action)
{
char *argv[3], *envp[5], dstr[20], astr[32];
sprintf(dstr, "DEVICE=%s", dev);
sprintf(astr, "ACTION=%s", action);
argv[0] = hotplug_path;
argv[1] = "bluetooth";
argv[2] = NULL;
envp[0] = "HOME=/";
envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
envp[2] = dstr;
envp[3] = astr;
envp[4] = NULL;
return call_usermodehelper(argv[0], argv, envp);
}
#else
#define hci_run_hotplug(A...)
#endif
/* ---- HCI requests ---- */ /* ---- HCI requests ---- */
...@@ -270,7 +296,7 @@ struct hci_dev *hci_dev_get(int index) ...@@ -270,7 +296,7 @@ struct hci_dev *hci_dev_get(int index)
if (index < 0) if (index < 0)
return NULL; return NULL;
spin_lock(&hdev_list_lock); read_lock(&hdev_list_lock);
list_for_each(p, &hdev_list) { list_for_each(p, &hdev_list) {
hdev = list_entry(p, struct hci_dev, list); hdev = list_entry(p, struct hci_dev, list);
if (hdev->id == index) { if (hdev->id == index) {
...@@ -280,7 +306,7 @@ struct hci_dev *hci_dev_get(int index) ...@@ -280,7 +306,7 @@ struct hci_dev *hci_dev_get(int index)
} }
hdev = NULL; hdev = NULL;
done: done:
spin_unlock(&hdev_list_lock); read_unlock(&hdev_list_lock);
return hdev; return hdev;
} }
...@@ -699,7 +725,7 @@ int hci_get_dev_list(unsigned long arg) ...@@ -699,7 +725,7 @@ int hci_get_dev_list(unsigned long arg)
return -ENOMEM; return -ENOMEM;
dr = dl->dev_req; dr = dl->dev_req;
spin_lock_bh(&hdev_list_lock); read_lock_bh(&hdev_list_lock);
list_for_each(p, &hdev_list) { list_for_each(p, &hdev_list) {
struct hci_dev *hdev; struct hci_dev *hdev;
hdev = list_entry(p, struct hci_dev, list); hdev = list_entry(p, struct hci_dev, list);
...@@ -708,7 +734,7 @@ int hci_get_dev_list(unsigned long arg) ...@@ -708,7 +734,7 @@ int hci_get_dev_list(unsigned long arg)
if (++n >= dev_num) if (++n >= dev_num)
break; break;
} }
spin_unlock_bh(&hdev_list_lock); read_unlock_bh(&hdev_list_lock);
dl->dev_num = n; dl->dev_num = n;
size = n * sizeof(struct hci_dev_req) + sizeof(__u16); size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
...@@ -768,7 +794,7 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -768,7 +794,7 @@ int hci_register_dev(struct hci_dev *hdev)
if (!hdev->open || !hdev->close || !hdev->destruct) if (!hdev->open || !hdev->close || !hdev->destruct)
return -EINVAL; return -EINVAL;
spin_lock_bh(&hdev_list_lock); write_lock_bh(&hdev_list_lock);
/* Find first available device id */ /* Find first available device id */
list_for_each(p, &hdev_list) { list_for_each(p, &hdev_list) {
...@@ -807,11 +833,12 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -807,11 +833,12 @@ int hci_register_dev(struct hci_dev *hdev)
atomic_set(&hdev->promisc, 0); atomic_set(&hdev->promisc, 0);
hci_notify(hdev, HCI_DEV_REG);
MOD_INC_USE_COUNT; MOD_INC_USE_COUNT;
spin_unlock_bh(&hdev_list_lock); write_unlock_bh(&hdev_list_lock);
hci_notify(hdev, HCI_DEV_REG);
hci_run_hotplug(hdev->name, "register");
return id; return id;
} }
...@@ -821,13 +848,15 @@ int hci_unregister_dev(struct hci_dev *hdev) ...@@ -821,13 +848,15 @@ int hci_unregister_dev(struct hci_dev *hdev)
{ {
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
spin_lock_bh(&hdev_list_lock); write_lock_bh(&hdev_list_lock);
list_del(&hdev->list); list_del(&hdev->list);
spin_unlock_bh(&hdev_list_lock); write_unlock_bh(&hdev_list_lock);
hci_dev_do_close(hdev); hci_dev_do_close(hdev);
hci_notify(hdev, HCI_DEV_UNREG); hci_notify(hdev, HCI_DEV_UNREG);
hci_run_hotplug(hdev->name, "unregister");
hci_dev_put(hdev); hci_dev_put(hdev);
MOD_DEC_USE_COUNT; MOD_DEC_USE_COUNT;
...@@ -1103,14 +1132,13 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int ...@@ -1103,14 +1132,13 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
{ {
struct conn_hash *h = &hdev->conn_hash; struct conn_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL; struct hci_conn *conn = NULL;
int num = 0, min = 0xffff; int num = 0, min = ~0;
struct list_head *p; struct list_head *p;
/* We don't have to lock device here. Connections are always /* We don't have to lock device here. Connections are always
* added and removed with TX task disabled. */ * added and removed with TX task disabled. */
list_for_each(p, &h->list) { list_for_each(p, &h->list) {
struct hci_conn *c; struct hci_conn *c;
c = list_entry(p, struct hci_conn, list); c = list_entry(p, struct hci_conn, list);
if (c->type != type || c->state != BT_CONNECTED if (c->type != type || c->state != BT_CONNECTED
...@@ -1118,14 +1146,15 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int ...@@ -1118,14 +1146,15 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
continue; continue;
num++; num++;
if (c->sent < min || type == SCO_LINK) { if (c->sent < min) {
min = c->sent; min = c->sent;
conn = c; conn = c;
} }
} }
if (conn) { if (conn) {
int q = hdev->acl_cnt / num; int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
int q = cnt / num;
*quote = q ? q : 1; *quote = q ? q : 1;
} else } else
*quote = 0; *quote = 0;
...@@ -1134,6 +1163,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int ...@@ -1134,6 +1163,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
return conn; return conn;
} }
static inline void hci_acl_tx_to(struct hci_dev *hdev)
{
struct conn_hash *h = &hdev->conn_hash;
struct list_head *p;
struct hci_conn *c;
BT_ERR("%s ACL tx timeout", hdev->name);
/* Kill stalled connections */
list_for_each(p, &h->list) {
c = list_entry(p, struct hci_conn, list);
if (c->type == ACL_LINK && c->sent) {
BT_ERR("%s killing stalled ACL connection %s",
hdev->name, batostr(&c->dst));
hci_acl_disconn(c, 0x13);
}
}
}
static inline void hci_sched_acl(struct hci_dev *hdev) static inline void hci_sched_acl(struct hci_dev *hdev)
{ {
struct hci_conn *conn; struct hci_conn *conn;
...@@ -1142,21 +1190,19 @@ static inline void hci_sched_acl(struct hci_dev *hdev) ...@@ -1142,21 +1190,19 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > HZ*5) { /* ACL tx timeout must be longer than maximum
BT_ERR("%s ACL tx timeout", hdev->name); * link supervision timeout (40.9 seconds) */
hdev->acl_cnt++; if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
} hci_acl_tx_to(hdev);
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) { while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
while (quote && (skb = skb_dequeue(&conn->data_q))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len); BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(skb); hci_send_frame(skb);
hdev->acl_last_tx = jiffies; hdev->acl_last_tx = jiffies;
conn->sent++;
hdev->acl_cnt--; hdev->acl_cnt--;
quote--; conn->sent++;
} }
} }
} }
...@@ -1170,15 +1216,14 @@ static inline void hci_sched_sco(struct hci_dev *hdev) ...@@ -1170,15 +1216,14 @@ static inline void hci_sched_sco(struct hci_dev *hdev)
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
while ((conn = hci_low_sent(hdev, SCO_LINK, &quote))) { while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
while (quote && (skb = skb_dequeue(&conn->data_q))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len); BT_DBG("skb %p len %d", skb, skb->len);
hci_send_frame(skb); hci_send_frame(skb);
//conn->sent++; conn->sent++;
//hdev->sco_cnt--; if (conn->sent == ~0)
quote--; conn->sent = 0;
} }
} }
} }
...@@ -1205,7 +1250,6 @@ static void hci_tx_task(unsigned long arg) ...@@ -1205,7 +1250,6 @@ static void hci_tx_task(unsigned long arg)
read_unlock(&hci_task_lock); read_unlock(&hci_task_lock);
} }
/* ----- HCI RX task (incomming data proccessing) ----- */ /* ----- HCI RX task (incomming data proccessing) ----- */
/* ACL data packet */ /* ACL data packet */
...@@ -1367,9 +1411,6 @@ static void hci_cmd_task(unsigned long arg) ...@@ -1367,9 +1411,6 @@ static void hci_cmd_task(unsigned long arg)
int hci_core_init(void) int hci_core_init(void)
{ {
/* Init locks */
spin_lock_init(&hdev_list_lock);
return 0; return 0;
} }
......
...@@ -352,15 +352,12 @@ static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) ...@@ -352,15 +352,12 @@ static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
hci_dev_lock(hdev); hci_dev_lock(hdev);
acl = conn_hash_lookup_handle(hdev, handle); acl = conn_hash_lookup_handle(hdev, handle);
if (!acl || !(sco = acl->link)) { if (acl && (sco = acl->link)) {
hci_dev_unlock(hdev);
break;
}
sco->state = BT_CLOSED; sco->state = BT_CLOSED;
hci_proto_connect_cfm(sco, status); hci_proto_connect_cfm(sco, status);
hci_conn_del(sco); hci_conn_del(sco);
}
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
} }
......
...@@ -86,7 +86,7 @@ static struct bluez_sock_list hci_sk_list = { ...@@ -86,7 +86,7 @@ static struct bluez_sock_list hci_sk_list = {
/* Send frame to RAW socket */ /* Send frame to RAW socket */
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct sock * sk; struct sock *sk;
BT_DBG("hdev %p len %d", hdev, skb->len); BT_DBG("hdev %p len %d", hdev, skb->len);
...@@ -105,13 +105,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -105,13 +105,13 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
/* Apply filter */ /* Apply filter */
flt = &hci_pi(sk)->filter; flt = &hci_pi(sk)->filter;
if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask)) if (!test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
continue; continue;
if (skb->pkt_type == HCI_EVENT_PKT) { if (skb->pkt_type == HCI_EVENT_PKT) {
register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
if (!hci_test_bit(evt, &flt->event_mask)) if (!test_bit(evt, flt->event_mask))
continue; continue;
if (flt->opcode && ((evt == EVT_CMD_COMPLETE && if (flt->opcode && ((evt == EVT_CMD_COMPLETE &&
...@@ -249,7 +249,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long a ...@@ -249,7 +249,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long a
err = hci_sock_bound_ioctl(sk, cmd, arg); err = hci_sock_bound_ioctl(sk, cmd, arg);
release_sock(sk); release_sock(sk);
return err; return err;
}; }
} }
static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
...@@ -393,12 +393,12 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, ...@@ -393,12 +393,12 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
err = -EPERM; err = -EPERM;
if (skb->pkt_type == HCI_COMMAND_PKT) { if (skb->pkt_type == HCI_COMMAND_PKT) {
__u16 opcode = __le16_to_cpu(*(__u16 *)skb->data); u16 opcode = __le16_to_cpu(*(__u16 *)skb->data);
__u16 ogf = cmd_opcode_ogf(opcode) - 1; u16 ogf = cmd_opcode_ogf(opcode) - 1;
__u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS; u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS;
if (ogf > HCI_SFLT_MAX_OGF || if (ogf > HCI_SFLT_MAX_OGF ||
!hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf])) !test_bit(ocf, hci_sec_filter.ocf_mask[ogf]))
goto drop; goto drop;
} else } else
goto drop; goto drop;
...@@ -420,8 +420,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, ...@@ -420,8 +420,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len) int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
{ {
struct hci_ufilter uf = { .opcode = 0 };
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct hci_filter flt = { opcode: 0 };
int err = 0, opt = 0; int err = 0, opt = 0;
BT_DBG("sk %p, opt %d", sk, optname); BT_DBG("sk %p, opt %d", sk, optname);
...@@ -454,25 +454,32 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva ...@@ -454,25 +454,32 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva
break; break;
case HCI_FILTER: case HCI_FILTER:
len = MIN(len, sizeof(struct hci_filter)); len = MIN(len, sizeof(uf));
if (copy_from_user(&flt, optval, len)) { if (copy_from_user(&uf, optval, len)) {
err = -EFAULT; err = -EFAULT;
break; break;
} }
if (!capable(CAP_NET_RAW)) { if (!capable(CAP_NET_RAW)) {
flt.type_mask &= hci_sec_filter.type_mask; uf.type_mask &= hci_sec_filter.type_mask;
flt.event_mask[0] &= hci_sec_filter.event_mask[0]; uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0);
flt.event_mask[1] &= hci_sec_filter.event_mask[1]; uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1);
} }
memcpy(&hci_pi(sk)->filter, &flt, len); {
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[0];
}
break; break;
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
}; }
release_sock(sk); release_sock(sk);
return err; return err;
...@@ -480,6 +487,7 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva ...@@ -480,6 +487,7 @@ int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optva
int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{ {
struct hci_ufilter uf;
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
int len, opt; int len, opt;
...@@ -508,15 +516,24 @@ int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optva ...@@ -508,15 +516,24 @@ int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optva
break; break;
case HCI_FILTER: case HCI_FILTER:
len = MIN(len, sizeof(struct hci_filter)); {
if (copy_to_user(optval, &hci_pi(sk)->filter, len)) 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[0] = *((u32 *) f->event_mask + 1);
}
len = MIN(len, sizeof(uf));
if (copy_to_user(optval, &uf, len))
return -EFAULT; return -EFAULT;
break; break;
default: default:
return -ENOPROTOOPT; return -ENOPROTOOPT;
break; break;
}; }
return 0; return 0;
} }
......
...@@ -25,9 +25,9 @@ ...@@ -25,9 +25,9 @@
/* /*
* BlueZ L2CAP core and sockets. * BlueZ L2CAP core and sockets.
* *
* $Id: l2cap.c,v 1.8 2002/04/19 00:01:39 maxk Exp $ * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $
*/ */
#define VERSION "2.0" #define VERSION "2.1"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
...@@ -69,7 +70,7 @@ struct bluez_sock_list l2cap_sk_list = { ...@@ -69,7 +70,7 @@ struct bluez_sock_list l2cap_sk_list = {
static int l2cap_conn_del(struct hci_conn *conn, int err); static int l2cap_conn_del(struct hci_conn *conn, int err);
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
static void l2cap_chan_del(struct sock *sk, int err); static void l2cap_chan_del(struct sock *sk, int err);
static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len); static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
...@@ -177,6 +178,14 @@ static int l2cap_conn_del(struct hci_conn *hcon, int err) ...@@ -177,6 +178,14 @@ static int l2cap_conn_del(struct hci_conn *hcon, int err)
return 0; return 0;
} }
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
{
struct l2cap_chan_list *l = &conn->chan_list;
write_lock(&l->lock);
__l2cap_chan_add(conn, sk, parent);
write_unlock(&l->lock);
}
int l2cap_connect(struct sock *sk) int l2cap_connect(struct sock *sk)
{ {
bdaddr_t *src = &bluez_sk(sk)->src; bdaddr_t *src = &bluez_sk(sk)->src;
...@@ -234,32 +243,26 @@ int l2cap_connect(struct sock *sk) ...@@ -234,32 +243,26 @@ int l2cap_connect(struct sock *sk)
} }
/* -------- Socket interface ---------- */ /* -------- Socket interface ---------- */
static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr) static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src)
{ {
bdaddr_t *src = &addr->l2_bdaddr;
__u16 psm = addr->l2_psm;
struct sock *sk; struct sock *sk;
for (sk = l2cap_sk_list.head; sk; sk = sk->next) { for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
if (l2cap_pi(sk)->psm == psm && if (l2cap_pi(sk)->psm == psm &&
!bacmp(&bluez_sk(sk)->src, src)) !bacmp(&bluez_sk(sk)->src, src))
break; break;
} }
return sk; return sk;
} }
/* Find socket listening on psm and source bdaddr. /* Find socket with psm and source bdaddr.
* Returns closest match. * Returns closest match.
*/ */
static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm) static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
{ {
struct sock *sk, *sk1 = NULL; struct sock *sk, *sk1 = NULL;
read_lock(&l2cap_sk_list.lock);
for (sk = l2cap_sk_list.head; sk; sk = sk->next) { for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
if (sk->state != BT_LISTEN) if (state && sk->state != state)
continue; continue;
if (l2cap_pi(sk)->psm == psm) { if (l2cap_pi(sk)->psm == psm) {
...@@ -272,9 +275,19 @@ static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm) ...@@ -272,9 +275,19 @@ static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
sk1 = sk; sk1 = sk;
} }
} }
return sk ? sk : sk1;
}
/* Find socket with given address (psm, src).
* Returns locked socket */
static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
{
struct sock *s;
read_lock(&l2cap_sk_list.lock);
s = __l2cap_get_sock_by_psm(state, psm, src);
if (s) bh_lock_sock(s);
read_unlock(&l2cap_sk_list.lock); read_unlock(&l2cap_sk_list.lock);
return sk ? sk : sk1; return s;
} }
static void l2cap_sock_destruct(struct sock *sk) static void l2cap_sock_destruct(struct sock *sk)
...@@ -320,8 +333,6 @@ static void l2cap_sock_kill(struct sock *sk) ...@@ -320,8 +333,6 @@ static void l2cap_sock_kill(struct sock *sk)
sock_put(sk); sock_put(sk);
} }
/* Close socket.
*/
static void __l2cap_sock_close(struct sock *sk, int reason) static void __l2cap_sock_close(struct sock *sk, int reason)
{ {
BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket); BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
...@@ -333,6 +344,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason) ...@@ -333,6 +344,7 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
case BT_CONNECTED: case BT_CONNECTED:
case BT_CONFIG: case BT_CONFIG:
case BT_CONNECT2:
if (sk->type == SOCK_SEQPACKET) { if (sk->type == SOCK_SEQPACKET) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_conn *conn = l2cap_pi(sk)->conn;
l2cap_disconn_req req; l2cap_disconn_req req;
...@@ -349,7 +361,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason) ...@@ -349,7 +361,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
break; break;
case BT_CONNECT: case BT_CONNECT:
case BT_CONNECT2:
case BT_DISCONN: case BT_DISCONN:
l2cap_chan_del(sk, reason); l2cap_chan_del(sk, reason);
break; break;
...@@ -380,7 +391,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) ...@@ -380,7 +391,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
if (parent) { if (parent) {
sk->type = parent->type; sk->type = parent->type;
pi->imtu = l2cap_pi(parent)->imtu; pi->imtu = l2cap_pi(parent)->imtu;
pi->omtu = l2cap_pi(parent)->omtu; pi->omtu = l2cap_pi(parent)->omtu;
pi->link_mode = l2cap_pi(parent)->link_mode; pi->link_mode = l2cap_pi(parent)->link_mode;
...@@ -405,6 +415,8 @@ static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio) ...@@ -405,6 +415,8 @@ static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
sk->destruct = l2cap_sock_destruct; sk->destruct = l2cap_sock_destruct;
sk->sndtimeo = L2CAP_CONN_TIMEOUT; sk->sndtimeo = L2CAP_CONN_TIMEOUT;
sk->protocol = proto;
sk->state = BT_OPEN; sk->state = BT_OPEN;
l2cap_sock_init_timer(sk); l2cap_sock_init_timer(sk);
...@@ -421,10 +433,11 @@ static int l2cap_sock_create(struct socket *sock, int protocol) ...@@ -421,10 +433,11 @@ static int l2cap_sock_create(struct socket *sock, int protocol)
BT_DBG("sock %p", sock); BT_DBG("sock %p", sock);
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW) sock->state = SS_UNCONNECTED;
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT; return -ESOCKTNOSUPPORT;
sock->state = SS_UNCONNECTED;
sock->ops = &l2cap_sock_ops; sock->ops = &l2cap_sock_ops;
sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL); sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL);
...@@ -453,20 +466,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_ ...@@ -453,20 +466,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
goto done; goto done;
} }
write_lock(&l2cap_sk_list.lock); write_lock_bh(&l2cap_sk_list.lock);
if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
err = -EADDRINUSE; err = -EADDRINUSE;
goto unlock; } else {
}
/* Save source address */ /* Save source address */
bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr); bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr);
l2cap_pi(sk)->psm = la->l2_psm; l2cap_pi(sk)->psm = la->l2_psm;
sk->state = BT_BOUND; sk->state = BT_BOUND;
}
unlock: write_unlock_bh(&l2cap_sk_list.lock);
write_unlock(&l2cap_sk_list.lock);
done: done:
release_sock(sk); release_sock(sk);
...@@ -629,7 +638,6 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l ...@@ -629,7 +638,6 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src); bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src);
la->l2_psm = l2cap_pi(sk)->psm; la->l2_psm = l2cap_pi(sk)->psm;
return 0; return 0;
} }
...@@ -646,6 +654,10 @@ static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, ...@@ -646,6 +654,10 @@ static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
if (msg->msg_flags & MSG_OOB) if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* Check outgoing MTU */
if (len > l2cap_pi(sk)->omtu)
return -EINVAL;
lock_sock(sk); lock_sock(sk);
if (sk->state == BT_CONNECTED) if (sk->state == BT_CONNECTED)
...@@ -691,7 +703,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch ...@@ -691,7 +703,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
}; }
release_sock(sk); release_sock(sk);
return err; return err;
...@@ -743,20 +755,37 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch ...@@ -743,20 +755,37 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
}; }
release_sock(sk); release_sock(sk);
return err; return err;
} }
static int l2cap_sock_release(struct socket *sock) static int l2cap_sock_shutdown(struct socket *sock, int how)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
BT_DBG("sock %p, sk %p", sock, sk); BT_DBG("sock %p, sk %p", sock, sk);
if (!sk) if (!sk) return 0;
l2cap_sock_clear_timer(sk);
lock_sock(sk);
sk->shutdown = SHUTDOWN_MASK;
__l2cap_sock_close(sk, ECONNRESET);
release_sock(sk);
return 0; return 0;
}
static int l2cap_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_orphan(sk);
l2cap_sock_close(sk); l2cap_sock_close(sk);
...@@ -767,24 +796,20 @@ static int l2cap_sock_release(struct socket *sock) ...@@ -767,24 +796,20 @@ static int l2cap_sock_release(struct socket *sock)
static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid) static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
{ {
struct sock *s; struct sock *s;
for (s = l->head; s; s = l2cap_pi(s)->next_c) { for (s = l->head; s; s = l2cap_pi(s)->next_c) {
if (l2cap_pi(s)->dcid == cid) if (l2cap_pi(s)->dcid == cid)
break; break;
} }
return s; return s;
} }
static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid) static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
{ {
struct sock *s; struct sock *s;
for (s = l->head; s; s = l2cap_pi(s)->next_c) { for (s = l->head; s; s = l2cap_pi(s)->next_c) {
if (l2cap_pi(s)->scid == cid) if (l2cap_pi(s)->scid == cid)
break; break;
} }
return s; return s;
} }
...@@ -850,10 +875,15 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so ...@@ -850,10 +875,15 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
l2cap_pi(sk)->conn = conn; l2cap_pi(sk)->conn = conn;
if (sk->type == SOCK_SEQPACKET) { if (sk->type == SOCK_SEQPACKET) {
/* Alloc CID for normal socket */ /* Alloc CID for connection-oriented socket */
l2cap_pi(sk)->scid = l2cap_alloc_cid(l); l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
} else if (sk->type == SOCK_DGRAM) {
/* Connectionless socket */
l2cap_pi(sk)->scid = 0x0002;
l2cap_pi(sk)->dcid = 0x0002;
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
} else { } else {
/* Raw socket can send only signalling messages */ /* Raw socket can send/recv signalling messages only */
l2cap_pi(sk)->scid = 0x0001; l2cap_pi(sk)->scid = 0x0001;
l2cap_pi(sk)->dcid = 0x0001; l2cap_pi(sk)->dcid = 0x0001;
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
...@@ -865,14 +895,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so ...@@ -865,14 +895,6 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
bluez_accept_enqueue(parent, sk); bluez_accept_enqueue(parent, sk);
} }
static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
{
struct l2cap_chan_list *l = &conn->chan_list;
write_lock(&l->lock);
__l2cap_chan_add(conn, sk, parent);
write_unlock(&l->lock);
}
/* Delete channel. /* Delete channel.
* Must be called on the locked socket. */ * Must be called on the locked socket. */
static void l2cap_chan_del(struct sock *sk, int err) static void l2cap_chan_del(struct sock *sk, int err)
...@@ -984,25 +1006,31 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len) ...@@ -984,25 +1006,31 @@ static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
{ {
struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_conn *conn = l2cap_pi(sk)->conn;
struct sk_buff *skb, **frag; struct sk_buff *skb, **frag;
int err, size, count, sent=0; int err, hlen, count, sent=0;
l2cap_hdr *lh; l2cap_hdr *lh;
/* Check outgoing MTU */
if (len > l2cap_pi(sk)->omtu)
return -EINVAL;
BT_DBG("sk %p len %d", sk, len); BT_DBG("sk %p len %d", sk, len);
/* First fragment (with L2CAP header) */ /* First fragment (with L2CAP header) */
count = MIN(conn->mtu - L2CAP_HDR_SIZE, len); if (sk->type == SOCK_DGRAM)
size = L2CAP_HDR_SIZE + count; hlen = L2CAP_HDR_SIZE + 2;
if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err))) else
hlen = L2CAP_HDR_SIZE;
count = MIN(conn->mtu - hlen, len);
skb = bluez_skb_send_alloc(sk, hlen + count,
msg->msg_flags & MSG_DONTWAIT, &err);
if (!skb)
return err; return err;
/* Create L2CAP header */ /* Create L2CAP header */
lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = __cpu_to_le16(len);
lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
if (sk->type == SOCK_DGRAM)
put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2));
if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
err = -EFAULT; err = -EFAULT;
...@@ -1315,38 +1343,42 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, ...@@ -1315,38 +1343,42 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd,
BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid); BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
/* Check if we have socket listening on psm */ /* Check if we have socket listening on psm */
if (!(parent = l2cap_get_sock_listen(conn->src, psm))) { parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
if (!parent) {
result = L2CAP_CR_BAD_PSM; result = L2CAP_CR_BAD_PSM;
goto resp; goto sendresp;
} }
write_lock(&list->lock);
bh_lock_sock(parent);
result = L2CAP_CR_NO_MEM; result = L2CAP_CR_NO_MEM;
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(list, scid))
goto unlock;
/* Check for backlog size */ /* Check for backlog size */
if (parent->ack_backlog > parent->max_ack_backlog) { if (parent->ack_backlog > parent->max_ack_backlog) {
BT_DBG("backlog full %d", parent->ack_backlog); BT_DBG("backlog full %d", parent->ack_backlog);
goto unlock; goto response;
} }
if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC))) sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);
goto unlock; if (!sk)
goto response;
l2cap_sock_init(sk, parent); write_lock(&list->lock);
/* Check if we already have channel with that dcid */
if (__l2cap_get_chan_by_dcid(list, scid)) {
write_unlock(&list->lock);
sk->zapped = 1;
l2cap_sock_kill(sk);
goto response;
}
hci_conn_hold(conn->hcon);
l2cap_sock_init(sk, parent);
bacpy(&bluez_sk(sk)->src, conn->src); bacpy(&bluez_sk(sk)->src, conn->src);
bacpy(&bluez_sk(sk)->dst, conn->dst); bacpy(&bluez_sk(sk)->dst, conn->dst);
l2cap_pi(sk)->psm = psm; l2cap_pi(sk)->psm = psm;
l2cap_pi(sk)->dcid = scid; l2cap_pi(sk)->dcid = scid;
hci_conn_hold(conn->hcon);
__l2cap_chan_add(conn, sk, parent); __l2cap_chan_add(conn, sk, parent);
dcid = l2cap_pi(sk)->scid; dcid = l2cap_pi(sk)->scid;
...@@ -1360,20 +1392,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, ...@@ -1360,20 +1392,22 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd,
if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) { if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
if (!hci_conn_encrypt(conn->hcon)) if (!hci_conn_encrypt(conn->hcon))
goto unlock; goto done;
} else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) { } else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
if (!hci_conn_auth(conn->hcon)) if (!hci_conn_auth(conn->hcon))
goto unlock; goto done;
} }
sk->state = BT_CONFIG; sk->state = BT_CONFIG;
result = status = 0; result = status = 0;
unlock: done:
bh_unlock_sock(parent);
write_unlock(&list->lock); write_unlock(&list->lock);
resp: response:
bh_unlock_sock(parent);
sendresp:
rsp.scid = __cpu_to_le16(scid); rsp.scid = __cpu_to_le16(scid);
rsp.dcid = __cpu_to_le16(dcid); rsp.dcid = __cpu_to_le16(dcid);
rsp.result = __cpu_to_le16(result); rsp.result = __cpu_to_le16(result);
...@@ -1678,10 +1712,37 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct ...@@ -1678,10 +1712,37 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct
return 0; return 0;
} }
static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb)
{
struct sock *sk;
sk = l2cap_get_sock_by_psm(0, psm, conn->src);
if (!sk)
goto drop;
BT_DBG("sk %p, len %d", sk, skb->len);
if (sk->state != BT_BOUND && sk->state != BT_CONNECTED)
goto drop;
if (l2cap_pi(sk)->imtu < skb->len)
goto drop;
if (!sock_queue_rcv_skb(sk, skb))
goto done;
drop:
kfree_skb(skb);
done:
if (sk) bh_unlock_sock(sk);
return 0;
}
static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
{ {
l2cap_hdr *lh = (l2cap_hdr *) skb->data; l2cap_hdr *lh = (l2cap_hdr *) skb->data;
__u16 cid, len; __u16 cid, psm, len;
skb_pull(skb, L2CAP_HDR_SIZE); skb_pull(skb, L2CAP_HDR_SIZE);
cid = __le16_to_cpu(lh->cid); cid = __le16_to_cpu(lh->cid);
...@@ -1689,10 +1750,21 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) ...@@ -1689,10 +1750,21 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
BT_DBG("len %d, cid 0x%4.4x", len, cid); BT_DBG("len %d, cid 0x%4.4x", len, cid);
if (cid == 0x0001) switch (cid) {
case 0x0001:
l2cap_sig_channel(conn, skb); l2cap_sig_channel(conn, skb);
else break;
case 0x0002:
psm = get_unaligned((__u16 *) skb->data);
skb_pull(skb, 2);
l2cap_conless_channel(conn, psm, skb);
break;
default:
l2cap_data_channel(conn, cid, skb); l2cap_data_channel(conn, cid, skb);
break;
}
} }
/* ------------ L2CAP interface with lower layer (HCI) ------------- */ /* ------------ L2CAP interface with lower layer (HCI) ------------- */
...@@ -1859,8 +1931,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 ...@@ -1859,8 +1931,8 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16
BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags); BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
if (flags & ACL_START) { if (flags & ACL_START) {
int flen, tlen, size; l2cap_hdr *hdr;
l2cap_hdr *lh; int len;
if (conn->rx_len) { if (conn->rx_len) {
BT_ERR("Unexpected start frame (len %d)", skb->len); BT_ERR("Unexpected start frame (len %d)", skb->len);
...@@ -1869,30 +1941,28 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 ...@@ -1869,30 +1941,28 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16
conn->rx_len = 0; conn->rx_len = 0;
} }
if (skb->len < L2CAP_HDR_SIZE) { if (skb->len < 2) {
BT_ERR("Frame is too small (len %d)", skb->len); BT_ERR("Frame is too small (len %d)", skb->len);
goto drop; goto drop;
} }
lh = (l2cap_hdr *)skb->data; hdr = (l2cap_hdr *) skb->data;
tlen = __le16_to_cpu(lh->len); len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
flen = skb->len - L2CAP_HDR_SIZE;
BT_DBG("Start: total len %d, frag len %d", tlen, flen); BT_DBG("Start: total len %d, frag len %d", len, skb->len);
if (flen == tlen) { if (len == skb->len) {
/* Complete frame received */ /* Complete frame received */
l2cap_recv_frame(conn, skb); l2cap_recv_frame(conn, skb);
return 0; return 0;
} }
/* Allocate skb for the complete frame (with header) */ /* Allocate skb for the complete frame (with header) */
size = L2CAP_HDR_SIZE + tlen; if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
goto drop; goto drop;
memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len); memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
conn->rx_len = tlen - flen; conn->rx_len = len - skb->len;
} else { } else {
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len); BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
...@@ -1932,7 +2002,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) ...@@ -1932,7 +2002,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
struct sock *sk; struct sock *sk;
char *ptr = buf; char *ptr = buf;
write_lock(&list->lock); read_lock_bh(&list->lock);
for (sk = list->head; sk; sk = sk->next) { for (sk = list->head; sk; sk = sk->next) {
pi = l2cap_pi(sk); pi = l2cap_pi(sk);
...@@ -1942,7 +2012,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list) ...@@ -1942,7 +2012,7 @@ static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
pi->link_mode); pi->link_mode);
} }
write_unlock(&list->lock); read_unlock_bh(&list->lock);
ptr += sprintf(ptr, "\n"); ptr += sprintf(ptr, "\n");
return ptr - buf; return ptr - buf;
...@@ -1983,12 +2053,12 @@ static struct proto_ops l2cap_sock_ops = { ...@@ -1983,12 +2053,12 @@ static struct proto_ops l2cap_sock_ops = {
.sendmsg = l2cap_sock_sendmsg, .sendmsg = l2cap_sock_sendmsg,
.recvmsg = bluez_sock_recvmsg, .recvmsg = bluez_sock_recvmsg,
.poll = bluez_sock_poll, .poll = bluez_sock_poll,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair, .socketpair = sock_no_socketpair,
.ioctl = sock_no_ioctl, .ioctl = sock_no_ioctl,
.shutdown = sock_no_shutdown, .shutdown = l2cap_sock_shutdown,
.setsockopt = l2cap_sock_setsockopt, .setsockopt = l2cap_sock_setsockopt,
.getsockopt = l2cap_sock_getsockopt, .getsockopt = l2cap_sock_getsockopt
.mmap = sock_no_mmap
}; };
static struct net_proto_family l2cap_sock_family_ops = { static struct net_proto_family l2cap_sock_family_ops = {
...@@ -2002,9 +2072,9 @@ static struct hci_proto l2cap_hci_proto = { ...@@ -2002,9 +2072,9 @@ static struct hci_proto l2cap_hci_proto = {
.connect_ind = l2cap_connect_ind, .connect_ind = l2cap_connect_ind,
.connect_cfm = l2cap_connect_cfm, .connect_cfm = l2cap_connect_cfm,
.disconn_ind = l2cap_disconn_ind, .disconn_ind = l2cap_disconn_ind,
.recv_acldata = l2cap_recv_acldata,
.auth_cfm = l2cap_auth_cfm, .auth_cfm = l2cap_auth_cfm,
.encrypt_cfm = l2cap_encrypt_cfm .encrypt_cfm = l2cap_encrypt_cfm,
.recv_acldata = l2cap_recv_acldata
}; };
int __init l2cap_init(void) int __init l2cap_init(void)
......
...@@ -105,7 +105,7 @@ int bterr(__u16 code) ...@@ -105,7 +105,7 @@ int bterr(__u16 code)
return EACCES; return EACCES;
case 0x06: case 0x06:
return EINVAL; return EBADE;
case 0x07: case 0x07:
return ENOMEM; return ENOMEM;
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $ * $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $
*/ */
#define VERSION "0.2" #define VERSION "0.3"
#include <linux/config.h> #include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -67,16 +67,15 @@ static struct bluez_sock_list sco_sk_list = { ...@@ -67,16 +67,15 @@ static struct bluez_sock_list sco_sk_list = {
.lock = RW_LOCK_UNLOCKED .lock = RW_LOCK_UNLOCKED
}; };
static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); 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 void sco_chan_del(struct sock *sk, int err);
static inline struct sock * sco_chan_get(struct sco_conn *conn);
static int sco_conn_del(struct hci_conn *conn, 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_close(struct sock *sk);
static void sco_sock_kill(struct sock *sk); static void sco_sock_kill(struct sock *sk);
/* ----- SCO timers ------ */ /* ---- SCO timers ---- */
static void sco_sock_timeout(unsigned long arg) static void sco_sock_timeout(unsigned long arg)
{ {
struct sock *sk = (struct sock *) arg; struct sock *sk = (struct sock *) arg;
...@@ -115,7 +114,7 @@ static void sco_sock_init_timer(struct sock *sk) ...@@ -115,7 +114,7 @@ static void sco_sock_init_timer(struct sock *sk)
sk->timer.data = (unsigned long)sk; sk->timer.data = (unsigned long)sk;
} }
/* -------- SCO connections --------- */ /* ---- SCO connections ---- */
static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
{ {
struct hci_dev *hdev = hcon->hdev; struct hci_dev *hdev = hcon->hdev;
...@@ -150,6 +149,15 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status) ...@@ -150,6 +149,15 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
return 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) static int sco_conn_del(struct hci_conn *hcon, int err)
{ {
struct sco_conn *conn; struct sco_conn *conn;
...@@ -176,6 +184,20 @@ static int sco_conn_del(struct hci_conn *hcon, int err) ...@@ -176,6 +184,20 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
return 0; 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;
}
int sco_connect(struct sock *sk) int sco_connect(struct sock *sk)
{ {
bdaddr_t *src = &bluez_sk(sk)->src; bdaddr_t *src = &bluez_sk(sk)->src;
...@@ -462,23 +484,20 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le ...@@ -462,23 +484,20 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
goto done; goto done;
} }
write_lock(&sco_sk_list.lock); write_lock_bh(&sco_sk_list.lock);
if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) { if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
err = -EADDRINUSE; err = -EADDRINUSE;
goto unlock; } else {
}
/* Save source address */ /* Save source address */
bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr); bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr);
sk->state = BT_BOUND; sk->state = BT_BOUND;
}
unlock: write_unlock_bh(&sco_sk_list.lock);
write_unlock(&sco_sk_list.lock);
done: done:
release_sock(sk); release_sock(sk);
return err; return err;
} }
...@@ -649,7 +668,7 @@ int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optva ...@@ -649,7 +668,7 @@ int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optva
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
}; }
release_sock(sk); release_sock(sk);
return err; return err;
...@@ -703,7 +722,7 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva ...@@ -703,7 +722,7 @@ int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optva
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
}; }
release_sock(sk); release_sock(sk);
return err; return err;
...@@ -735,29 +754,6 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock * ...@@ -735,29 +754,6 @@ static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *
bluez_accept_enqueue(parent, sk); bluez_accept_enqueue(parent, sk);
} }
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 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;
}
/* Delete channel. /* Delete channel.
* Must be called on the locked socket. */ * Must be called on the locked socket. */
static void sco_chan_del(struct sock *sk, int err) static void sco_chan_del(struct sock *sk, int err)
...@@ -895,7 +891,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list) ...@@ -895,7 +891,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
struct sock *sk; struct sock *sk;
char *ptr = buf; char *ptr = buf;
write_lock(&list->lock); read_lock_bh(&list->lock);
for (sk = list->head; sk; sk = sk->next) { for (sk = list->head; sk; sk = sk->next) {
pi = sco_pi(sk); pi = sco_pi(sk);
...@@ -904,7 +900,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list) ...@@ -904,7 +900,7 @@ static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
sk->state); sk->state);
} }
write_unlock(&list->lock); read_unlock_bh(&list->lock);
ptr += sprintf(ptr, "\n"); ptr += sprintf(ptr, "\n");
...@@ -946,12 +942,12 @@ static struct proto_ops sco_sock_ops = { ...@@ -946,12 +942,12 @@ static struct proto_ops sco_sock_ops = {
.sendmsg = sco_sock_sendmsg, .sendmsg = sco_sock_sendmsg,
.recvmsg = bluez_sock_recvmsg, .recvmsg = bluez_sock_recvmsg,
.poll = bluez_sock_poll, .poll = bluez_sock_poll,
.socketpair = sock_no_socketpair,
.ioctl = sock_no_ioctl, .ioctl = sock_no_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
.shutdown = sock_no_shutdown, .shutdown = sock_no_shutdown,
.setsockopt = sco_sock_setsockopt, .setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt, .getsockopt = sco_sock_getsockopt
.mmap = sock_no_mmap
}; };
static struct net_proto_family sco_sock_family_ops = { static struct net_proto_family sco_sock_family_ops = {
...@@ -965,7 +961,7 @@ static struct hci_proto sco_hci_proto = { ...@@ -965,7 +961,7 @@ static struct hci_proto sco_hci_proto = {
.connect_ind = sco_connect_ind, .connect_ind = sco_connect_ind,
.connect_cfm = sco_connect_cfm, .connect_cfm = sco_connect_cfm,
.disconn_ind = sco_disconn_ind, .disconn_ind = sco_disconn_ind,
.recv_scodata = sco_recv_scodata, .recv_scodata = sco_recv_scodata
}; };
int __init sco_init(void) int __init sco_init(void)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment