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;
} }
......
This diff is collapsed.
...@@ -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