Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-next

Conflicts:
	net/bluetooth/l2cap_sock.c
	net/bluetooth/mgmt.c
This commit is contained in:
John W. Linville 2011-11-09 14:54:33 -05:00
commit 312fef7d18
27 changed files with 2302 additions and 983 deletions

View File

@ -30,6 +30,7 @@
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#define VERSION "1.0" #define VERSION "1.0"
#define ATH3K_FIRMWARE "ath3k-1.fw"
#define ATH3K_DNLOAD 0x01 #define ATH3K_DNLOAD 0x01
#define ATH3K_GETSTATE 0x05 #define ATH3K_GETSTATE 0x05
@ -400,9 +401,15 @@ static int ath3k_probe(struct usb_interface *intf,
return 0; return 0;
} }
if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
BT_ERR("Error loading firmware"); if (ret < 0) {
return -EIO; if (ret == -ENOENT)
BT_ERR("Firmware file \"%s\" not found",
ATH3K_FIRMWARE);
else
BT_ERR("Firmware file \"%s\" request failed (err=%d)",
ATH3K_FIRMWARE, ret);
return ret;
} }
ret = ath3k_load_firmware(udev, firmware); ret = ath3k_load_firmware(udev, firmware);
@ -441,4 +448,4 @@ MODULE_AUTHOR("Atheros Communications");
MODULE_DESCRIPTION("Atheros AR30xx firmware driver"); MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
MODULE_VERSION(VERSION); MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_FIRMWARE("ath3k-1.fw"); MODULE_FIRMWARE(ATH3K_FIRMWARE);

View File

@ -751,9 +751,7 @@ static void bfusb_disconnect(struct usb_interface *intf)
bfusb_close(hdev); bfusb_close(hdev);
if (hci_unregister_dev(hdev) < 0) hci_unregister_dev(hdev);
BT_ERR("Can't unregister HCI device %s", hdev->name);
hci_free_dev(hdev); hci_free_dev(hdev);
} }

View File

@ -844,9 +844,7 @@ static int bluecard_close(bluecard_info_t *info)
/* Turn FPGA off */ /* Turn FPGA off */
outb(0x80, iobase + 0x30); outb(0x80, iobase + 0x30);
if (hci_unregister_dev(hdev) < 0) hci_unregister_dev(hdev);
BT_ERR("Can't unregister HCI device %s", hdev->name);
hci_free_dev(hdev); hci_free_dev(hdev);
return 0; return 0;

View File

@ -636,9 +636,7 @@ static int bt3c_close(bt3c_info_t *info)
bt3c_hci_close(hdev); bt3c_hci_close(hdev);
if (hci_unregister_dev(hdev) < 0) hci_unregister_dev(hdev);
BT_ERR("Can't unregister HCI device %s", hdev->name);
hci_free_dev(hdev); hci_free_dev(hdev);
return 0; return 0;

View File

@ -565,9 +565,7 @@ static int btuart_close(btuart_info_t *info)
spin_unlock_irqrestore(&(info->lock), flags); spin_unlock_irqrestore(&(info->lock), flags);
if (hci_unregister_dev(hdev) < 0) hci_unregister_dev(hdev);
BT_ERR("Can't unregister HCI device %s", hdev->name);
hci_free_dev(hdev); hci_free_dev(hdev);
return 0; return 0;

View File

@ -315,7 +315,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
err = usb_submit_urb(urb, mem_flags); err = usb_submit_urb(urb, mem_flags);
if (err < 0) { if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)", if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err); hdev->name, urb, -err);
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
} }
@ -400,7 +401,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
err = usb_submit_urb(urb, mem_flags); err = usb_submit_urb(urb, mem_flags);
if (err < 0) { if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)", if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err); hdev->name, urb, -err);
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
} }
@ -523,7 +525,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
err = usb_submit_urb(urb, mem_flags); err = usb_submit_urb(urb, mem_flags);
if (err < 0) { if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)", if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err); hdev->name, urb, -err);
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
} }
@ -727,6 +730,9 @@ static int btusb_send_frame(struct sk_buff *skb)
usb_fill_bulk_urb(urb, data->udev, pipe, usb_fill_bulk_urb(urb, data->udev, pipe,
skb->data, skb->len, btusb_tx_complete, skb); skb->data, skb->len, btusb_tx_complete, skb);
if (skb->priority >= HCI_PRIO_MAX - 1)
urb->transfer_flags = URB_ISO_ASAP;
hdev->stat.acl_tx++; hdev->stat.acl_tx++;
break; break;
@ -770,7 +776,9 @@ skip_waking:
err = usb_submit_urb(urb, GFP_ATOMIC); err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) { if (err < 0) {
BT_ERR("%s urb %p submission failed", hdev->name, urb); if (err != -EPERM && err != -ENODEV)
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
kfree(urb->setup_packet); kfree(urb->setup_packet);
usb_unanchor_urb(urb); usb_unanchor_urb(urb);
} else { } else {

View File

@ -551,9 +551,7 @@ static int dtl1_close(dtl1_info_t *info)
spin_unlock_irqrestore(&(info->lock), flags); spin_unlock_irqrestore(&(info->lock), flags);
if (hci_unregister_dev(hdev) < 0) hci_unregister_dev(hdev);
BT_ERR("Can't unregister HCI device %s", hdev->name);
hci_free_dev(hdev); hci_free_dev(hdev);
return 0; return 0;

View File

@ -264,10 +264,7 @@ static int vhci_release(struct inode *inode, struct file *file)
struct vhci_data *data = file->private_data; struct vhci_data *data = file->private_data;
struct hci_dev *hdev = data->hdev; struct hci_dev *hdev = data->hdev;
if (hci_unregister_dev(hdev) < 0) { hci_unregister_dev(hdev);
BT_ERR("Can't unregister HCI device %s", hdev->name);
}
hci_free_dev(hdev); hci_free_dev(hdev);
file->private_data = NULL; file->private_data = NULL;

View File

@ -77,6 +77,33 @@ struct bt_power {
#define BT_POWER_FORCE_ACTIVE_OFF 0 #define BT_POWER_FORCE_ACTIVE_OFF 0
#define BT_POWER_FORCE_ACTIVE_ON 1 #define BT_POWER_FORCE_ACTIVE_ON 1
#define BT_CHANNEL_POLICY 10
/* BR/EDR only (default policy)
* AMP controllers cannot be used.
* Channel move requests from the remote device are denied.
* If the L2CAP channel is currently using AMP, move the channel to BR/EDR.
*/
#define BT_CHANNEL_POLICY_BREDR_ONLY 0
/* BR/EDR Preferred
* Allow use of AMP controllers.
* If the L2CAP channel is currently on AMP, move it to BR/EDR.
* Channel move requests from the remote device are allowed.
*/
#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1
/* AMP Preferred
* Allow use of AMP controllers
* If the L2CAP channel is currently on BR/EDR and AMP controller
* resources are available, initiate a channel move to AMP.
* Channel move requests from the remote device are allowed.
* If the L2CAP socket has not been connected yet, try to create
* and configure the channel directly on an AMP controller rather
* than BR/EDR.
*/
#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
__attribute__((format (printf, 2, 3))) __attribute__((format (printf, 2, 3)))
int bt_printk(const char *level, const char *fmt, ...); int bt_printk(const char *level, const char *fmt, ...);
@ -158,7 +185,7 @@ struct bt_skb_cb {
__u8 pkt_type; __u8 pkt_type;
__u8 incoming; __u8 incoming;
__u16 expect; __u16 expect;
__u8 tx_seq; __u16 tx_seq;
__u8 retries; __u8 retries;
__u8 sar; __u8 sar;
unsigned short channel; unsigned short channel;

View File

@ -264,6 +264,13 @@ enum {
#define HCI_LK_SMP_IRK 0x82 #define HCI_LK_SMP_IRK 0x82
#define HCI_LK_SMP_CSRK 0x83 #define HCI_LK_SMP_CSRK 0x83
/* ---- HCI Error Codes ---- */
#define HCI_ERROR_AUTH_FAILURE 0x05
#define HCI_ERROR_REJ_BAD_ADDR 0x0f
#define HCI_ERROR_REMOTE_USER_TERM 0x13
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
/* ----- HCI Commands ---- */ /* ----- HCI Commands ---- */
#define HCI_OP_NOP 0x0000 #define HCI_OP_NOP 0x0000
@ -726,6 +733,21 @@ struct hci_cp_write_page_scan_activity {
#define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_STANDARD 0x00
#define PAGE_SCAN_TYPE_INTERLACED 0x01 #define PAGE_SCAN_TYPE_INTERLACED 0x01
#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409
struct hci_rp_read_local_amp_info {
__u8 status;
__u8 amp_status;
__le32 total_bw;
__le32 max_bw;
__le32 min_latency;
__le32 max_pdu;
__u8 amp_type;
__le16 pal_cap;
__le16 max_assoc_size;
__le32 max_flush_to;
__le32 be_flush_to;
} __packed;
#define HCI_OP_LE_SET_EVENT_MASK 0x2001 #define HCI_OP_LE_SET_EVENT_MASK 0x2001
struct hci_cp_le_set_event_mask { struct hci_cp_le_set_event_mask {
__u8 mask[8]; __u8 mask[8];

View File

@ -32,6 +32,9 @@
#define HCI_PROTO_L2CAP 0 #define HCI_PROTO_L2CAP 0
#define HCI_PROTO_SCO 1 #define HCI_PROTO_SCO 1
/* HCI priority */
#define HCI_PRIO_MAX 7
/* HCI Core structures */ /* HCI Core structures */
struct inquiry_data { struct inquiry_data {
bdaddr_t bdaddr; bdaddr_t bdaddr;
@ -64,6 +67,12 @@ struct hci_conn_hash {
unsigned int le_num; unsigned int le_num;
}; };
struct hci_chan_hash {
struct list_head list;
spinlock_t lock;
unsigned int num;
};
struct bdaddr_list { struct bdaddr_list {
struct list_head list; struct list_head list;
bdaddr_t bdaddr; bdaddr_t bdaddr;
@ -150,6 +159,17 @@ struct hci_dev {
__u16 sniff_min_interval; __u16 sniff_min_interval;
__u16 sniff_max_interval; __u16 sniff_max_interval;
__u8 amp_status;
__u32 amp_total_bw;
__u32 amp_max_bw;
__u32 amp_min_latency;
__u32 amp_max_pdu;
__u8 amp_type;
__u16 amp_pal_cap;
__u16 amp_assoc_size;
__u32 amp_max_flush_to;
__u32 amp_be_flush_to;
unsigned int auto_accept_delay; unsigned int auto_accept_delay;
unsigned long quirks; unsigned long quirks;
@ -173,8 +193,10 @@ struct hci_dev {
struct workqueue_struct *workqueue; struct workqueue_struct *workqueue;
struct work_struct power_on; struct work_struct power_on;
struct work_struct power_off; struct delayed_work power_off;
struct timer_list off_timer;
__u16 discov_timeout;
struct delayed_work discov_off;
struct timer_list cmd_timer; struct timer_list cmd_timer;
struct tasklet_struct cmd_task; struct tasklet_struct cmd_task;
@ -195,6 +217,8 @@ struct hci_dev {
__u16 init_last_cmd; __u16 init_last_cmd;
struct list_head mgmt_pending;
struct inquiry_cache inq_cache; struct inquiry_cache inq_cache;
struct hci_conn_hash conn_hash; struct hci_conn_hash conn_hash;
struct list_head blacklist; struct list_head blacklist;
@ -273,6 +297,7 @@ struct hci_conn {
unsigned int sent; unsigned int sent;
struct sk_buff_head data_q; struct sk_buff_head data_q;
struct hci_chan_hash chan_hash;
struct timer_list disc_timer; struct timer_list disc_timer;
struct timer_list idle_timer; struct timer_list idle_timer;
@ -295,6 +320,14 @@ struct hci_conn {
void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
}; };
struct hci_chan {
struct list_head list;
struct hci_conn *conn;
struct sk_buff_head data_q;
unsigned int sent;
};
extern struct hci_proto *hci_proto[]; extern struct hci_proto *hci_proto[];
extern struct list_head hci_dev_list; extern struct list_head hci_dev_list;
extern struct list_head hci_cb_list; extern struct list_head hci_cb_list;
@ -455,6 +488,28 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
return NULL; return NULL;
} }
static inline void hci_chan_hash_init(struct hci_conn *c)
{
struct hci_chan_hash *h = &c->chan_hash;
INIT_LIST_HEAD(&h->list);
spin_lock_init(&h->lock);
h->num = 0;
}
static inline void hci_chan_hash_add(struct hci_conn *c, struct hci_chan *chan)
{
struct hci_chan_hash *h = &c->chan_hash;
list_add(&chan->list, &h->list);
h->num++;
}
static inline void hci_chan_hash_del(struct hci_conn *c, struct hci_chan *chan)
{
struct hci_chan_hash *h = &c->chan_hash;
list_del(&chan->list);
h->num--;
}
void hci_acl_connect(struct hci_conn *conn); void hci_acl_connect(struct hci_conn *conn);
void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
void hci_add_sco(struct hci_conn *conn, __u16 handle); void hci_add_sco(struct hci_conn *conn, __u16 handle);
@ -466,6 +521,10 @@ int hci_conn_del(struct hci_conn *conn);
void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_hash_flush(struct hci_dev *hdev);
void hci_conn_check_pending(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev);
struct hci_chan *hci_chan_create(struct hci_conn *conn);
int hci_chan_del(struct hci_chan *chan);
void hci_chan_hash_flush(struct hci_conn *conn);
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u8 sec_level, __u8 auth_type); __u8 sec_level, __u8 auth_type);
int hci_conn_check_link_mode(struct hci_conn *conn); int hci_conn_check_link_mode(struct hci_conn *conn);
@ -543,7 +602,7 @@ struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
struct hci_dev *hci_alloc_dev(void); struct hci_dev *hci_alloc_dev(void);
void hci_free_dev(struct hci_dev *hdev); void hci_free_dev(struct hci_dev *hdev);
int hci_register_dev(struct hci_dev *hdev); int hci_register_dev(struct hci_dev *hdev);
int hci_unregister_dev(struct hci_dev *hdev); void hci_unregister_dev(struct hci_dev *hdev);
int hci_suspend_dev(struct hci_dev *hdev); int hci_suspend_dev(struct hci_dev *hdev);
int hci_resume_dev(struct hci_dev *hdev); int hci_resume_dev(struct hci_dev *hdev);
int hci_dev_open(__u16 dev); int hci_dev_open(__u16 dev);
@ -597,8 +656,9 @@ int hci_recv_frame(struct sk_buff *skb);
int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count);
int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count);
int hci_register_sysfs(struct hci_dev *hdev); void hci_init_sysfs(struct hci_dev *hdev);
void hci_unregister_sysfs(struct hci_dev *hdev); int hci_add_sysfs(struct hci_dev *hdev);
void hci_del_sysfs(struct hci_dev *hdev);
void hci_conn_init_sysfs(struct hci_conn *conn); void hci_conn_init_sysfs(struct hci_conn *conn);
void hci_conn_add_sysfs(struct hci_conn *conn); void hci_conn_add_sysfs(struct hci_conn *conn);
void hci_conn_del_sysfs(struct hci_conn *conn); void hci_conn_del_sysfs(struct hci_conn *conn);
@ -674,7 +734,7 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
static inline int hci_proto_disconn_ind(struct hci_conn *conn) static inline int hci_proto_disconn_ind(struct hci_conn *conn)
{ {
register struct hci_proto *hp; register struct hci_proto *hp;
int reason = 0x13; int reason = HCI_ERROR_REMOTE_USER_TERM;
hp = hci_proto[HCI_PROTO_L2CAP]; hp = hci_proto[HCI_PROTO_L2CAP];
if (hp && hp->disconn_ind) if (hp && hp->disconn_ind)
@ -834,7 +894,7 @@ int hci_register_notifier(struct notifier_block *nb);
int hci_unregister_notifier(struct notifier_block *nb); int hci_unregister_notifier(struct notifier_block *nb);
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param);
void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
@ -847,34 +907,41 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb,
/* Management interface */ /* Management interface */
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
int mgmt_index_added(u16 index); int mgmt_index_added(struct hci_dev *hdev);
int mgmt_index_removed(u16 index); int mgmt_index_removed(struct hci_dev *hdev);
int mgmt_powered(u16 index, u8 powered); int mgmt_powered(struct hci_dev *hdev, u8 powered);
int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable);
int mgmt_connectable(u16 index, u8 connectable); int mgmt_connectable(struct hci_dev *hdev, u8 connectable);
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent); int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status);
int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); u8 persistent);
int mgmt_disconnect_failed(u16 index); int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure); int mgmt_disconnect_failed(struct hci_dev *hdev);
int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value,
u8 confirm_hint);
int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status);
int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr,
u8 status); u8 status);
int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure);
int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status); int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer,
u8 status); u8 status);
int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 *eir); u8 status);
int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
int mgmt_discovering(u16 index, u8 discovering); __le32 value, u8 confirm_hint);
int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr); int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr); u8 status);
int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev,
bdaddr_t *bdaddr, u8 status);
int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status);
int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
u8 *randomizer, u8 status);
int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type,
u8 *dev_class, s8 rssi, u8 *eir);
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name);
int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status);
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr);
/* HCI info for socket */ /* HCI info for socket */
#define hci_pi(sk) ((struct hci_pinfo *) sk) #define hci_pi(sk) ((struct hci_pinfo *) sk)
@ -913,4 +980,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
void hci_le_ltk_neg_reply(struct hci_conn *conn); void hci_le_ltk_neg_reply(struct hci_conn *conn);
int hci_do_inquiry(struct hci_dev *hdev, u8 length);
int hci_cancel_inquiry(struct hci_dev *hdev);
#endif /* __HCI_CORE_H */ #endif /* __HCI_CORE_H */

View File

@ -27,17 +27,23 @@
#ifndef __L2CAP_H #ifndef __L2CAP_H
#define __L2CAP_H #define __L2CAP_H
#include <asm/unaligned.h>
/* L2CAP defaults */ /* L2CAP defaults */
#define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MTU 672
#define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_MIN_MTU 48
#define L2CAP_DEFAULT_FLUSH_TO 0xffff #define L2CAP_DEFAULT_FLUSH_TO 0xffff
#define L2CAP_DEFAULT_TX_WINDOW 63 #define L2CAP_DEFAULT_TX_WINDOW 63
#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF
#define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_MAX_TX 3
#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
#define L2CAP_DEFAULT_ACK_TO 200 #define L2CAP_DEFAULT_ACK_TO 200
#define L2CAP_LE_DEFAULT_MTU 23 #define L2CAP_LE_DEFAULT_MTU 23
#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF
#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF
#define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF
#define L2CAP_DISC_TIMEOUT (100) #define L2CAP_DISC_TIMEOUT (100)
#define L2CAP_DISC_REJ_TIMEOUT (5000) /* 5 seconds */ #define L2CAP_DISC_REJ_TIMEOUT (5000) /* 5 seconds */
@ -91,52 +97,82 @@ struct l2cap_conninfo {
#define L2CAP_ECHO_RSP 0x09 #define L2CAP_ECHO_RSP 0x09
#define L2CAP_INFO_REQ 0x0a #define L2CAP_INFO_REQ 0x0a
#define L2CAP_INFO_RSP 0x0b #define L2CAP_INFO_RSP 0x0b
#define L2CAP_CREATE_CHAN_REQ 0x0c
#define L2CAP_CREATE_CHAN_RSP 0x0d
#define L2CAP_MOVE_CHAN_REQ 0x0e
#define L2CAP_MOVE_CHAN_RSP 0x0f
#define L2CAP_MOVE_CHAN_CFM 0x10
#define L2CAP_MOVE_CHAN_CFM_RSP 0x11
#define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 #define L2CAP_CONN_PARAM_UPDATE_REQ 0x12
#define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 #define L2CAP_CONN_PARAM_UPDATE_RSP 0x13
/* L2CAP feature mask */ /* L2CAP extended feature mask */
#define L2CAP_FEAT_FLOWCTL 0x00000001 #define L2CAP_FEAT_FLOWCTL 0x00000001
#define L2CAP_FEAT_RETRANS 0x00000002 #define L2CAP_FEAT_RETRANS 0x00000002
#define L2CAP_FEAT_BIDIR_QOS 0x00000004
#define L2CAP_FEAT_ERTM 0x00000008 #define L2CAP_FEAT_ERTM 0x00000008
#define L2CAP_FEAT_STREAMING 0x00000010 #define L2CAP_FEAT_STREAMING 0x00000010
#define L2CAP_FEAT_FCS 0x00000020 #define L2CAP_FEAT_FCS 0x00000020
#define L2CAP_FEAT_EXT_FLOW 0x00000040
#define L2CAP_FEAT_FIXED_CHAN 0x00000080 #define L2CAP_FEAT_FIXED_CHAN 0x00000080
#define L2CAP_FEAT_EXT_WINDOW 0x00000100
#define L2CAP_FEAT_UCD 0x00000200
/* L2CAP checksum option */ /* L2CAP checksum option */
#define L2CAP_FCS_NONE 0x00 #define L2CAP_FCS_NONE 0x00
#define L2CAP_FCS_CRC16 0x01 #define L2CAP_FCS_CRC16 0x01
/* L2CAP Control Field bit masks */ /* L2CAP fixed channels */
#define L2CAP_CTRL_SAR 0xC000 #define L2CAP_FC_L2CAP 0x02
#define L2CAP_CTRL_REQSEQ 0x3F00 #define L2CAP_FC_A2MP 0x08
#define L2CAP_CTRL_TXSEQ 0x007E
#define L2CAP_CTRL_RETRANS 0x0080
#define L2CAP_CTRL_FINAL 0x0080
#define L2CAP_CTRL_POLL 0x0010
#define L2CAP_CTRL_SUPERVISE 0x000C
#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
#define L2CAP_CTRL_TXSEQ_SHIFT 1 /* L2CAP Control Field bit masks */
#define L2CAP_CTRL_REQSEQ_SHIFT 8 #define L2CAP_CTRL_SAR 0xC000
#define L2CAP_CTRL_SAR_SHIFT 14 #define L2CAP_CTRL_REQSEQ 0x3F00
#define L2CAP_CTRL_TXSEQ 0x007E
#define L2CAP_CTRL_SUPERVISE 0x000C
#define L2CAP_CTRL_RETRANS 0x0080
#define L2CAP_CTRL_FINAL 0x0080
#define L2CAP_CTRL_POLL 0x0010
#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
#define L2CAP_CTRL_TXSEQ_SHIFT 1
#define L2CAP_CTRL_SUPER_SHIFT 2
#define L2CAP_CTRL_REQSEQ_SHIFT 8
#define L2CAP_CTRL_SAR_SHIFT 14
/* L2CAP Extended Control Field bit mask */
#define L2CAP_EXT_CTRL_TXSEQ 0xFFFC0000
#define L2CAP_EXT_CTRL_SAR 0x00030000
#define L2CAP_EXT_CTRL_SUPERVISE 0x00030000
#define L2CAP_EXT_CTRL_REQSEQ 0x0000FFFC
#define L2CAP_EXT_CTRL_POLL 0x00040000
#define L2CAP_EXT_CTRL_FINAL 0x00000002
#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */
#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2
#define L2CAP_EXT_CTRL_SAR_SHIFT 16
#define L2CAP_EXT_CTRL_SUPER_SHIFT 16
#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18
/* L2CAP Supervisory Function */ /* L2CAP Supervisory Function */
#define L2CAP_SUPER_RCV_READY 0x0000 #define L2CAP_SUPER_RR 0x00
#define L2CAP_SUPER_REJECT 0x0004 #define L2CAP_SUPER_REJ 0x01
#define L2CAP_SUPER_RCV_NOT_READY 0x0008 #define L2CAP_SUPER_RNR 0x02
#define L2CAP_SUPER_SELECT_REJECT 0x000C #define L2CAP_SUPER_SREJ 0x03
/* L2CAP Segmentation and Reassembly */ /* L2CAP Segmentation and Reassembly */
#define L2CAP_SDU_UNSEGMENTED 0x0000 #define L2CAP_SAR_UNSEGMENTED 0x00
#define L2CAP_SDU_START 0x4000 #define L2CAP_SAR_START 0x01
#define L2CAP_SDU_END 0x8000 #define L2CAP_SAR_END 0x02
#define L2CAP_SDU_CONTINUE 0xC000 #define L2CAP_SAR_CONTINUE 0x03
/* L2CAP Command rej. reasons */ /* L2CAP Command rej. reasons */
#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 #define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
#define L2CAP_REJ_MTU_EXCEEDED 0x0001 #define L2CAP_REJ_MTU_EXCEEDED 0x0001
#define L2CAP_REJ_INVALID_CID 0x0002 #define L2CAP_REJ_INVALID_CID 0x0002
/* L2CAP structures */ /* L2CAP structures */
struct l2cap_hdr { struct l2cap_hdr {
@ -144,6 +180,12 @@ struct l2cap_hdr {
__le16 cid; __le16 cid;
} __packed; } __packed;
#define L2CAP_HDR_SIZE 4 #define L2CAP_HDR_SIZE 4
#define L2CAP_ENH_HDR_SIZE 6
#define L2CAP_EXT_HDR_SIZE 8
#define L2CAP_FCS_SIZE 2
#define L2CAP_SDULEN_SIZE 2
#define L2CAP_PSMLEN_SIZE 2
struct l2cap_cmd_hdr { struct l2cap_cmd_hdr {
__u8 code; __u8 code;
@ -188,14 +230,15 @@ struct l2cap_conn_rsp {
#define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_START 0x0040
#define L2CAP_CID_DYN_END 0xffff #define L2CAP_CID_DYN_END 0xffff
/* connect result */ /* connect/create channel results */
#define L2CAP_CR_SUCCESS 0x0000 #define L2CAP_CR_SUCCESS 0x0000
#define L2CAP_CR_PEND 0x0001 #define L2CAP_CR_PEND 0x0001
#define L2CAP_CR_BAD_PSM 0x0002 #define L2CAP_CR_BAD_PSM 0x0002
#define L2CAP_CR_SEC_BLOCK 0x0003 #define L2CAP_CR_SEC_BLOCK 0x0003
#define L2CAP_CR_NO_MEM 0x0004 #define L2CAP_CR_NO_MEM 0x0004
#define L2CAP_CR_BAD_AMP 0x0005
/* connect status */ /* connect/create channel status */
#define L2CAP_CS_NO_INFO 0x0000 #define L2CAP_CS_NO_INFO 0x0000
#define L2CAP_CS_AUTHEN_PEND 0x0001 #define L2CAP_CS_AUTHEN_PEND 0x0001
#define L2CAP_CS_AUTHOR_PEND 0x0002 #define L2CAP_CS_AUTHOR_PEND 0x0002
@ -217,6 +260,8 @@ struct l2cap_conf_rsp {
#define L2CAP_CONF_UNACCEPT 0x0001 #define L2CAP_CONF_UNACCEPT 0x0001
#define L2CAP_CONF_REJECT 0x0002 #define L2CAP_CONF_REJECT 0x0002
#define L2CAP_CONF_UNKNOWN 0x0003 #define L2CAP_CONF_UNKNOWN 0x0003
#define L2CAP_CONF_PENDING 0x0004
#define L2CAP_CONF_EFS_REJECT 0x0005
struct l2cap_conf_opt { struct l2cap_conf_opt {
__u8 type; __u8 type;
@ -233,6 +278,8 @@ struct l2cap_conf_opt {
#define L2CAP_CONF_QOS 0x03 #define L2CAP_CONF_QOS 0x03
#define L2CAP_CONF_RFC 0x04 #define L2CAP_CONF_RFC 0x04
#define L2CAP_CONF_FCS 0x05 #define L2CAP_CONF_FCS 0x05
#define L2CAP_CONF_EFS 0x06
#define L2CAP_CONF_EWS 0x07
#define L2CAP_CONF_MAX_SIZE 22 #define L2CAP_CONF_MAX_SIZE 22
@ -251,6 +298,21 @@ struct l2cap_conf_rfc {
#define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_ERTM 0x03
#define L2CAP_MODE_STREAMING 0x04 #define L2CAP_MODE_STREAMING 0x04
struct l2cap_conf_efs {
__u8 id;
__u8 stype;
__le16 msdu;
__le32 sdu_itime;
__le32 acc_lat;
__le32 flush_to;
} __packed;
#define L2CAP_SERV_NOTRAFIC 0x00
#define L2CAP_SERV_BESTEFFORT 0x01
#define L2CAP_SERV_GUARANTEED 0x02
#define L2CAP_BESTEFFORT_ID 0x01
struct l2cap_disconn_req { struct l2cap_disconn_req {
__le16 dcid; __le16 dcid;
__le16 scid; __le16 scid;
@ -271,14 +333,57 @@ struct l2cap_info_rsp {
__u8 data[0]; __u8 data[0];
} __packed; } __packed;
struct l2cap_create_chan_req {
__le16 psm;
__le16 scid;
__u8 amp_id;
} __packed;
struct l2cap_create_chan_rsp {
__le16 dcid;
__le16 scid;
__le16 result;
__le16 status;
} __packed;
struct l2cap_move_chan_req {
__le16 icid;
__u8 dest_amp_id;
} __packed;
struct l2cap_move_chan_rsp {
__le16 icid;
__le16 result;
} __packed;
#define L2CAP_MR_SUCCESS 0x0000
#define L2CAP_MR_PEND 0x0001
#define L2CAP_MR_BAD_ID 0x0002
#define L2CAP_MR_SAME_ID 0x0003
#define L2CAP_MR_NOT_SUPP 0x0004
#define L2CAP_MR_COLLISION 0x0005
#define L2CAP_MR_NOT_ALLOWED 0x0006
struct l2cap_move_chan_cfm {
__le16 icid;
__le16 result;
} __packed;
#define L2CAP_MC_CONFIRMED 0x0000
#define L2CAP_MC_UNCONFIRMED 0x0001
struct l2cap_move_chan_cfm_rsp {
__le16 icid;
} __packed;
/* info type */ /* info type */
#define L2CAP_IT_CL_MTU 0x0001 #define L2CAP_IT_CL_MTU 0x0001
#define L2CAP_IT_FEAT_MASK 0x0002 #define L2CAP_IT_FEAT_MASK 0x0002
#define L2CAP_IT_FIXED_CHAN 0x0003 #define L2CAP_IT_FIXED_CHAN 0x0003
/* info result */ /* info result */
#define L2CAP_IR_SUCCESS 0x0000 #define L2CAP_IR_SUCCESS 0x0000
#define L2CAP_IR_NOTSUPP 0x0001 #define L2CAP_IR_NOTSUPP 0x0001
struct l2cap_conn_param_update_req { struct l2cap_conn_param_update_req {
__le16 min; __le16 min;
@ -297,7 +402,7 @@ struct l2cap_conn_param_update_rsp {
/* ----- L2CAP channels and connections ----- */ /* ----- L2CAP channels and connections ----- */
struct srej_list { struct srej_list {
__u8 tx_seq; __u16 tx_seq;
struct list_head list; struct list_head list;
}; };
@ -319,14 +424,11 @@ struct l2cap_chan {
__u16 flush_to; __u16 flush_to;
__u8 mode; __u8 mode;
__u8 chan_type; __u8 chan_type;
__u8 chan_policy;
__le16 sport; __le16 sport;
__u8 sec_level; __u8 sec_level;
__u8 role_switch;
__u8 force_reliable;
__u8 flushable;
__u8 force_active;
__u8 ident; __u8 ident;
@ -337,7 +439,8 @@ struct l2cap_chan {
__u8 fcs; __u8 fcs;
__u8 tx_win; __u16 tx_win;
__u16 tx_win_max;
__u8 max_tx; __u8 max_tx;
__u16 retrans_timeout; __u16 retrans_timeout;
__u16 monitor_timeout; __u16 monitor_timeout;
@ -345,25 +448,40 @@ struct l2cap_chan {
unsigned long conf_state; unsigned long conf_state;
unsigned long conn_state; unsigned long conn_state;
unsigned long flags;
__u8 next_tx_seq; __u16 next_tx_seq;
__u8 expected_ack_seq; __u16 expected_ack_seq;
__u8 expected_tx_seq; __u16 expected_tx_seq;
__u8 buffer_seq; __u16 buffer_seq;
__u8 buffer_seq_srej; __u16 buffer_seq_srej;
__u8 srej_save_reqseq; __u16 srej_save_reqseq;
__u8 frames_sent; __u16 frames_sent;
__u8 unacked_frames; __u16 unacked_frames;
__u8 retry_count; __u8 retry_count;
__u8 num_acked; __u8 num_acked;
__u16 sdu_len; __u16 sdu_len;
struct sk_buff *sdu; struct sk_buff *sdu;
struct sk_buff *sdu_last_frag; struct sk_buff *sdu_last_frag;
__u8 remote_tx_win; __u16 remote_tx_win;
__u8 remote_max_tx; __u8 remote_max_tx;
__u16 remote_mps; __u16 remote_mps;
__u8 local_id;
__u8 local_stype;
__u16 local_msdu;
__u32 local_sdu_itime;
__u32 local_acc_lat;
__u32 local_flush_to;
__u8 remote_id;
__u8 remote_stype;
__u16 remote_msdu;
__u32 remote_sdu_itime;
__u32 remote_acc_lat;
__u32 remote_flush_to;
struct timer_list chan_timer; struct timer_list chan_timer;
struct timer_list retrans_timer; struct timer_list retrans_timer;
struct timer_list monitor_timer; struct timer_list monitor_timer;
@ -391,6 +509,7 @@ struct l2cap_ops {
struct l2cap_conn { struct l2cap_conn {
struct hci_conn *hcon; struct hci_conn *hcon;
struct hci_chan *hchan;
bdaddr_t *dst; bdaddr_t *dst;
bdaddr_t *src; bdaddr_t *src;
@ -445,6 +564,9 @@ enum {
CONF_CONNECT_PEND, CONF_CONNECT_PEND,
CONF_NO_FCS_RECV, CONF_NO_FCS_RECV,
CONF_STATE2_DEVICE, CONF_STATE2_DEVICE,
CONF_EWS_RECV,
CONF_LOC_CONF_PEND,
CONF_REM_CONF_PEND,
}; };
#define L2CAP_CONF_MAX_CONF_REQ 2 #define L2CAP_CONF_MAX_CONF_REQ 2
@ -462,6 +584,16 @@ enum {
CONN_RNR_SENT, CONN_RNR_SENT,
}; };
/* Definitions for flags in l2cap_chan */
enum {
FLAG_ROLE_SWITCH,
FLAG_FORCE_ACTIVE,
FLAG_FORCE_RELIABLE,
FLAG_FLUSHABLE,
FLAG_EXT_CTRL,
FLAG_EFS_ENABLE,
};
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
@ -474,6 +606,22 @@ enum {
L2CAP_DEFAULT_ACK_TO); L2CAP_DEFAULT_ACK_TO);
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) #define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2)
{
int offset;
offset = (seq1 - seq2) % (chan->tx_win_max + 1);
if (offset < 0)
offset += (chan->tx_win_max + 1);
return offset;
}
static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq)
{
return (seq + 1) % (chan->tx_win_max + 1);
}
static inline int l2cap_tx_window_full(struct l2cap_chan *ch) static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
{ {
int sub; int sub;
@ -486,13 +634,165 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch)
return sub == ch->remote_tx_win; return sub == ch->remote_tx_win;
} }
#define __get_txseq(ctrl) (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1) static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl)
#define __get_reqseq(ctrl) (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8) {
#define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE)) if (test_bit(FLAG_EXT_CTRL, &chan->flags))
#define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >>
#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START) L2CAP_EXT_CTRL_REQSEQ_SHIFT;
else
return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT;
}
static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) &
L2CAP_EXT_CTRL_REQSEQ;
else
return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ;
}
static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >>
L2CAP_EXT_CTRL_TXSEQ_SHIFT;
else
return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT;
}
static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) &
L2CAP_EXT_CTRL_TXSEQ;
else
return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ;
}
static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE;
else
return ctrl & L2CAP_CTRL_FRAME_TYPE;
}
static inline __u32 __set_sframe(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_CTRL_FRAME_TYPE;
else
return L2CAP_CTRL_FRAME_TYPE;
}
static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT;
else
return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT;
}
static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR;
else
return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR;
}
static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl)
{
return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START;
}
static inline __u32 __get_sar_mask(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_CTRL_SAR;
else
return L2CAP_CTRL_SAR;
}
static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >>
L2CAP_EXT_CTRL_SUPER_SHIFT;
else
return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT;
}
static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) &
L2CAP_EXT_CTRL_SUPERVISE;
else
return (super << L2CAP_CTRL_SUPER_SHIFT) &
L2CAP_CTRL_SUPERVISE;
}
static inline __u32 __set_ctrl_final(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_CTRL_FINAL;
else
return L2CAP_CTRL_FINAL;
}
static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return ctrl & L2CAP_EXT_CTRL_FINAL;
else
return ctrl & L2CAP_CTRL_FINAL;
}
static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_CTRL_POLL;
else
return L2CAP_CTRL_POLL;
}
static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return ctrl & L2CAP_EXT_CTRL_POLL;
else
return ctrl & L2CAP_CTRL_POLL;
}
static inline __u32 __get_control(struct l2cap_chan *chan, void *p)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return get_unaligned_le32(p);
else
return get_unaligned_le16(p);
}
static inline void __put_control(struct l2cap_chan *chan, __u32 control,
void *p)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return put_unaligned_le32(control, p);
else
return put_unaligned_le16(control, p);
}
static inline __u8 __ctrl_size(struct l2cap_chan *chan)
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE;
else
return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE;
}
extern int disable_ertm; extern int disable_ertm;
extern int enable_hs;
int l2cap_init_sockets(void); int l2cap_init_sockets(void);
void l2cap_cleanup_sockets(void); void l2cap_cleanup_sockets(void);
@ -507,7 +807,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk);
void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_close(struct l2cap_chan *chan, int reason);
void l2cap_chan_destroy(struct l2cap_chan *chan); void l2cap_chan_destroy(struct l2cap_chan *chan);
int l2cap_chan_connect(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan);
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority);
void l2cap_chan_busy(struct l2cap_chan *chan, int busy); void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
#endif /* __L2CAP_H */ #endif /* __L2CAP_H */

View File

@ -69,6 +69,10 @@ struct mgmt_mode {
#define MGMT_OP_SET_POWERED 0x0005 #define MGMT_OP_SET_POWERED 0x0005
#define MGMT_OP_SET_DISCOVERABLE 0x0006 #define MGMT_OP_SET_DISCOVERABLE 0x0006
struct mgmt_cp_set_discoverable {
__u8 val;
__u16 timeout;
} __packed;
#define MGMT_OP_SET_CONNECTABLE 0x0007 #define MGMT_OP_SET_CONNECTABLE 0x0007
@ -96,24 +100,22 @@ struct mgmt_cp_set_service_cache {
__u8 enable; __u8 enable;
} __packed; } __packed;
struct mgmt_key_info { struct mgmt_link_key_info {
bdaddr_t bdaddr; bdaddr_t bdaddr;
u8 type; u8 type;
u8 val[16]; u8 val[16];
u8 pin_len; u8 pin_len;
u8 dlen;
u8 data[0];
} __packed; } __packed;
#define MGMT_OP_LOAD_KEYS 0x000D #define MGMT_OP_LOAD_LINK_KEYS 0x000D
struct mgmt_cp_load_keys { struct mgmt_cp_load_link_keys {
__u8 debug_keys; __u8 debug_keys;
__le16 key_count; __le16 key_count;
struct mgmt_key_info keys[0]; struct mgmt_link_key_info keys[0];
} __packed; } __packed;
#define MGMT_OP_REMOVE_KEY 0x000E #define MGMT_OP_REMOVE_KEYS 0x000E
struct mgmt_cp_remove_key { struct mgmt_cp_remove_keys {
bdaddr_t bdaddr; bdaddr_t bdaddr;
__u8 disconnect; __u8 disconnect;
} __packed; } __packed;
@ -126,10 +128,20 @@ struct mgmt_rp_disconnect {
bdaddr_t bdaddr; bdaddr_t bdaddr;
} __packed; } __packed;
#define MGMT_ADDR_BREDR 0x00
#define MGMT_ADDR_LE 0x01
#define MGMT_ADDR_BREDR_LE 0x02
#define MGMT_ADDR_INVALID 0xff
struct mgmt_addr_info {
bdaddr_t bdaddr;
__u8 type;
} __packed;
#define MGMT_OP_GET_CONNECTIONS 0x0010 #define MGMT_OP_GET_CONNECTIONS 0x0010
struct mgmt_rp_get_connections { struct mgmt_rp_get_connections {
__le16 conn_count; __le16 conn_count;
bdaddr_t conn[0]; struct mgmt_addr_info addr[0];
} __packed; } __packed;
#define MGMT_OP_PIN_CODE_REPLY 0x0011 #define MGMT_OP_PIN_CODE_REPLY 0x0011
@ -245,26 +257,19 @@ struct mgmt_ev_controller_error {
#define MGMT_EV_PAIRABLE 0x0009 #define MGMT_EV_PAIRABLE 0x0009
#define MGMT_EV_NEW_KEY 0x000A #define MGMT_EV_NEW_LINK_KEY 0x000A
struct mgmt_ev_new_key { struct mgmt_ev_new_link_key {
__u8 store_hint; __u8 store_hint;
struct mgmt_key_info key; struct mgmt_link_key_info key;
} __packed; } __packed;
#define MGMT_EV_CONNECTED 0x000B #define MGMT_EV_CONNECTED 0x000B
struct mgmt_ev_connected {
bdaddr_t bdaddr;
__u8 link_type;
} __packed;
#define MGMT_EV_DISCONNECTED 0x000C #define MGMT_EV_DISCONNECTED 0x000C
struct mgmt_ev_disconnected {
bdaddr_t bdaddr;
} __packed;
#define MGMT_EV_CONNECT_FAILED 0x000D #define MGMT_EV_CONNECT_FAILED 0x000D
struct mgmt_ev_connect_failed { struct mgmt_ev_connect_failed {
bdaddr_t bdaddr; struct mgmt_addr_info addr;
__u8 status; __u8 status;
} __packed; } __packed;
@ -294,7 +299,7 @@ struct mgmt_ev_local_name_changed {
#define MGMT_EV_DEVICE_FOUND 0x0012 #define MGMT_EV_DEVICE_FOUND 0x0012
struct mgmt_ev_device_found { struct mgmt_ev_device_found {
bdaddr_t bdaddr; struct mgmt_addr_info addr;
__u8 dev_class[3]; __u8 dev_class[3];
__s8 rssi; __s8 rssi;
__u8 eir[HCI_MAX_EIR_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH];

View File

@ -65,15 +65,13 @@ static DECLARE_RWSEM(bnep_session_sem);
static struct bnep_session *__bnep_get_session(u8 *dst) static struct bnep_session *__bnep_get_session(u8 *dst)
{ {
struct bnep_session *s; struct bnep_session *s;
struct list_head *p;
BT_DBG(""); BT_DBG("");
list_for_each(p, &bnep_session_list) { list_for_each_entry(s, &bnep_session_list, list)
s = list_entry(p, struct bnep_session, list);
if (!compare_ether_addr(dst, s->eh.h_source)) if (!compare_ether_addr(dst, s->eh.h_source))
return s; return s;
}
return NULL; return NULL;
} }
@ -667,17 +665,14 @@ static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
int bnep_get_connlist(struct bnep_connlist_req *req) int bnep_get_connlist(struct bnep_connlist_req *req)
{ {
struct list_head *p; struct bnep_session *s;
int err = 0, n = 0; int err = 0, n = 0;
down_read(&bnep_session_sem); down_read(&bnep_session_sem);
list_for_each(p, &bnep_session_list) { list_for_each_entry(s, &bnep_session_list, list) {
struct bnep_session *s;
struct bnep_conninfo ci; struct bnep_conninfo ci;
s = list_entry(p, struct bnep_session, list);
__bnep_copy_ci(&ci, s); __bnep_copy_ci(&ci, s);
if (copy_to_user(req->ci, &ci, sizeof(ci))) { if (copy_to_user(req->ci, &ci, sizeof(ci))) {

View File

@ -53,15 +53,13 @@ static LIST_HEAD(cmtp_session_list);
static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr)
{ {
struct cmtp_session *session; struct cmtp_session *session;
struct list_head *p;
BT_DBG(""); BT_DBG("");
list_for_each(p, &cmtp_session_list) { list_for_each_entry(session, &cmtp_session_list, list)
session = list_entry(p, struct cmtp_session, list);
if (!bacmp(bdaddr, &session->bdaddr)) if (!bacmp(bdaddr, &session->bdaddr))
return session; return session;
}
return NULL; return NULL;
} }
@ -431,19 +429,16 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
int cmtp_get_connlist(struct cmtp_connlist_req *req) int cmtp_get_connlist(struct cmtp_connlist_req *req)
{ {
struct list_head *p; struct cmtp_session *session;
int err = 0, n = 0; int err = 0, n = 0;
BT_DBG(""); BT_DBG("");
down_read(&cmtp_session_sem); down_read(&cmtp_session_sem);
list_for_each(p, &cmtp_session_list) { list_for_each_entry(session, &cmtp_session_list, list) {
struct cmtp_session *session;
struct cmtp_conninfo ci; struct cmtp_conninfo ci;
session = list_entry(p, struct cmtp_session, list);
__cmtp_copy_session(session, &ci); __cmtp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) { if (copy_to_user(req->ci, &ci, sizeof(ci))) {

View File

@ -374,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
skb_queue_head_init(&conn->data_q); skb_queue_head_init(&conn->data_q);
hci_chan_hash_init(conn);
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn); setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
@ -432,6 +434,8 @@ int hci_conn_del(struct hci_conn *conn)
tasklet_disable(&hdev->tx_task); tasklet_disable(&hdev->tx_task);
hci_chan_hash_flush(conn);
hci_conn_hash_del(hdev, conn); hci_conn_hash_del(hdev, conn);
if (hdev->notify) if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
@ -453,16 +457,13 @@ int hci_conn_del(struct hci_conn *conn)
struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
{ {
int use_src = bacmp(src, BDADDR_ANY); int use_src = bacmp(src, BDADDR_ANY);
struct hci_dev *hdev = NULL; struct hci_dev *hdev = NULL, *d;
struct list_head *p;
BT_DBG("%s -> %s", batostr(src), batostr(dst)); BT_DBG("%s -> %s", batostr(src), batostr(dst));
read_lock_bh(&hci_dev_list_lock); read_lock_bh(&hci_dev_list_lock);
list_for_each(p, &hci_dev_list) { list_for_each_entry(d, &hci_dev_list, list) {
struct hci_dev *d = list_entry(p, struct hci_dev, list);
if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags)) if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
continue; continue;
@ -819,7 +820,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
c->state = BT_CLOSED; c->state = BT_CLOSED;
hci_proto_disconn_cfm(c, 0x16); hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
hci_conn_del(c); hci_conn_del(c);
} }
} }
@ -855,10 +856,10 @@ EXPORT_SYMBOL(hci_conn_put_device);
int hci_get_conn_list(void __user *arg) int hci_get_conn_list(void __user *arg)
{ {
register struct hci_conn *c;
struct hci_conn_list_req req, *cl; struct hci_conn_list_req req, *cl;
struct hci_conn_info *ci; struct hci_conn_info *ci;
struct hci_dev *hdev; struct hci_dev *hdev;
struct list_head *p;
int n = 0, size, err; int n = 0, size, err;
if (copy_from_user(&req, arg, sizeof(req))) if (copy_from_user(&req, arg, sizeof(req)))
@ -882,10 +883,7 @@ int hci_get_conn_list(void __user *arg)
ci = cl->conn_info; ci = cl->conn_info;
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
list_for_each(p, &hdev->conn_hash.list) { list_for_each_entry(c, &hdev->conn_hash.list, list) {
register struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
bacpy(&(ci + n)->bdaddr, &c->dst); bacpy(&(ci + n)->bdaddr, &c->dst);
(ci + n)->handle = c->handle; (ci + n)->handle = c->handle;
(ci + n)->type = c->type; (ci + n)->type = c->type;
@ -956,3 +954,52 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg)
return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; 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_ATOMIC);
if (!chan)
return NULL;
chan->conn = conn;
skb_queue_head_init(&chan->data_q);
tasklet_disable(&hdev->tx_task);
hci_chan_hash_add(conn, chan);
tasklet_enable(&hdev->tx_task);
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);
tasklet_disable(&hdev->tx_task);
hci_chan_hash_del(conn, chan);
tasklet_enable(&hdev->tx_task);
skb_queue_purge(&chan->data_q);
kfree(chan);
return 0;
}
void hci_chan_hash_flush(struct hci_conn *conn)
{
struct hci_chan_hash *h = &conn->chan_hash;
struct hci_chan *chan, *tmp;
BT_DBG("conn %p", conn);
list_for_each_entry_safe(chan, tmp, &h->list, list)
hci_chan_del(chan);
}

View File

@ -319,8 +319,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt)
* Device is held on return. */ * Device is held on return. */
struct hci_dev *hci_dev_get(int index) struct hci_dev *hci_dev_get(int index)
{ {
struct hci_dev *hdev = NULL; struct hci_dev *hdev = NULL, *d;
struct list_head *p;
BT_DBG("%d", index); BT_DBG("%d", index);
@ -328,8 +327,7 @@ struct hci_dev *hci_dev_get(int index)
return NULL; return NULL;
read_lock(&hci_dev_list_lock); read_lock(&hci_dev_list_lock);
list_for_each(p, &hci_dev_list) { list_for_each_entry(d, &hci_dev_list, list) {
struct hci_dev *d = list_entry(p, struct hci_dev, list);
if (d->id == index) { if (d->id == index) {
hdev = hci_dev_hold(d); hdev = hci_dev_hold(d);
break; break;
@ -551,8 +549,11 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev); hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags); set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP); hci_notify(hdev, HCI_DEV_UP);
if (!test_bit(HCI_SETUP, &hdev->flags)) if (!test_bit(HCI_SETUP, &hdev->flags)) {
mgmt_powered(hdev->id, 1); hci_dev_lock_bh(hdev);
mgmt_powered(hdev, 1);
hci_dev_unlock_bh(hdev);
}
} else { } else {
/* Init failed, cleanup */ /* Init failed, cleanup */
tasklet_kill(&hdev->rx_task); tasklet_kill(&hdev->rx_task);
@ -597,6 +598,14 @@ static int hci_dev_do_close(struct hci_dev *hdev)
tasklet_kill(&hdev->rx_task); tasklet_kill(&hdev->rx_task);
tasklet_kill(&hdev->tx_task); tasklet_kill(&hdev->tx_task);
if (hdev->discov_timeout > 0) {
cancel_delayed_work(&hdev->discov_off);
hdev->discov_timeout = 0;
}
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work(&hdev->power_off);
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
inquiry_cache_flush(hdev); inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev); hci_conn_hash_flush(hdev);
@ -636,7 +645,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
* and no tasks are scheduled. */ * and no tasks are scheduled. */
hdev->close(hdev); hdev->close(hdev);
mgmt_powered(hdev->id, 0); hci_dev_lock_bh(hdev);
mgmt_powered(hdev, 0);
hci_dev_unlock_bh(hdev);
/* Clear flags */ /* Clear flags */
hdev->flags = 0; hdev->flags = 0;
@ -794,9 +805,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg)
int hci_get_dev_list(void __user *arg) int hci_get_dev_list(void __user *arg)
{ {
struct hci_dev *hdev;
struct hci_dev_list_req *dl; struct hci_dev_list_req *dl;
struct hci_dev_req *dr; struct hci_dev_req *dr;
struct list_head *p;
int n = 0, size, err; int n = 0, size, err;
__u16 dev_num; __u16 dev_num;
@ -815,12 +826,9 @@ int hci_get_dev_list(void __user *arg)
dr = dl->dev_req; dr = dl->dev_req;
read_lock_bh(&hci_dev_list_lock); read_lock_bh(&hci_dev_list_lock);
list_for_each(p, &hci_dev_list) { list_for_each_entry(hdev, &hci_dev_list, list) {
struct hci_dev *hdev; if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work(&hdev->power_off);
hdev = list_entry(p, struct hci_dev, list);
hci_del_off_timer(hdev);
if (!test_bit(HCI_MGMT, &hdev->flags)) if (!test_bit(HCI_MGMT, &hdev->flags))
set_bit(HCI_PAIRABLE, &hdev->flags); set_bit(HCI_PAIRABLE, &hdev->flags);
@ -855,7 +863,8 @@ int hci_get_dev_info(void __user *arg)
if (!hdev) if (!hdev)
return -ENODEV; return -ENODEV;
hci_del_off_timer(hdev); if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
cancel_delayed_work_sync(&hdev->power_off);
if (!test_bit(HCI_MGMT, &hdev->flags)) if (!test_bit(HCI_MGMT, &hdev->flags))
set_bit(HCI_PAIRABLE, &hdev->flags); set_bit(HCI_PAIRABLE, &hdev->flags);
@ -912,6 +921,7 @@ struct hci_dev *hci_alloc_dev(void)
if (!hdev) if (!hdev)
return NULL; return NULL;
hci_init_sysfs(hdev);
skb_queue_head_init(&hdev->driver_init); skb_queue_head_init(&hdev->driver_init);
return hdev; return hdev;
@ -938,39 +948,41 @@ static void hci_power_on(struct work_struct *work)
return; return;
if (test_bit(HCI_AUTO_OFF, &hdev->flags)) if (test_bit(HCI_AUTO_OFF, &hdev->flags))
mod_timer(&hdev->off_timer, queue_delayed_work(hdev->workqueue, &hdev->power_off,
jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); msecs_to_jiffies(AUTO_OFF_TIMEOUT));
if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) if (test_and_clear_bit(HCI_SETUP, &hdev->flags))
mgmt_index_added(hdev->id); mgmt_index_added(hdev);
} }
static void hci_power_off(struct work_struct *work) static void hci_power_off(struct work_struct *work)
{ {
struct hci_dev *hdev = container_of(work, struct hci_dev, power_off); struct hci_dev *hdev = container_of(work, struct hci_dev,
power_off.work);
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
clear_bit(HCI_AUTO_OFF, &hdev->flags);
hci_dev_close(hdev->id); hci_dev_close(hdev->id);
} }
static void hci_auto_off(unsigned long data) static void hci_discov_off(struct work_struct *work)
{ {
struct hci_dev *hdev = (struct hci_dev *) data; struct hci_dev *hdev;
u8 scan = SCAN_PAGE;
hdev = container_of(work, struct hci_dev, discov_off.work);
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
clear_bit(HCI_AUTO_OFF, &hdev->flags); hci_dev_lock_bh(hdev);
queue_work(hdev->workqueue, &hdev->power_off); hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan);
}
void hci_del_off_timer(struct hci_dev *hdev) hdev->discov_timeout = 0;
{
BT_DBG("%s", hdev->name);
clear_bit(HCI_AUTO_OFF, &hdev->flags); hci_dev_unlock_bh(hdev);
del_timer(&hdev->off_timer);
} }
int hci_uuids_clear(struct hci_dev *hdev) int hci_uuids_clear(struct hci_dev *hdev)
@ -1007,16 +1019,11 @@ int hci_link_keys_clear(struct hci_dev *hdev)
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
{ {
struct list_head *p; struct link_key *k;
list_for_each(p, &hdev->link_keys) {
struct link_key *k;
k = list_entry(p, struct link_key, list);
list_for_each_entry(k, &hdev->link_keys, list)
if (bacmp(bdaddr, &k->bdaddr) == 0) if (bacmp(bdaddr, &k->bdaddr) == 0)
return k; return k;
}
return NULL; return NULL;
} }
@ -1138,7 +1145,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
persistent = hci_persistent_key(hdev, conn, type, old_key_type); persistent = hci_persistent_key(hdev, conn, type, old_key_type);
mgmt_new_key(hdev->id, key, persistent); mgmt_new_link_key(hdev, key, persistent);
if (!persistent) { if (!persistent) {
list_del(&key->list); list_del(&key->list);
@ -1181,7 +1188,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
memcpy(id->rand, rand, sizeof(id->rand)); memcpy(id->rand, rand, sizeof(id->rand));
if (new_key) if (new_key)
mgmt_new_key(hdev->id, key, old_key_type); mgmt_new_link_key(hdev, key, old_key_type);
return 0; return 0;
} }
@ -1279,16 +1286,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
bdaddr_t *bdaddr) bdaddr_t *bdaddr)
{ {
struct list_head *p; struct bdaddr_list *b;
list_for_each(p, &hdev->blacklist) {
struct bdaddr_list *b;
b = list_entry(p, struct bdaddr_list, list);
list_for_each_entry(b, &hdev->blacklist, list)
if (bacmp(bdaddr, &b->bdaddr) == 0) if (bacmp(bdaddr, &b->bdaddr) == 0)
return b; return b;
}
return NULL; return NULL;
} }
@ -1327,7 +1329,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_add(&entry->list, &hdev->blacklist); list_add(&entry->list, &hdev->blacklist);
return mgmt_device_blocked(hdev->id, bdaddr); return mgmt_device_blocked(hdev, bdaddr);
} }
int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
@ -1346,7 +1348,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
list_del(&entry->list); list_del(&entry->list);
kfree(entry); kfree(entry);
return mgmt_device_unblocked(hdev->id, bdaddr); return mgmt_device_unblocked(hdev, bdaddr);
} }
static void hci_clear_adv_cache(unsigned long arg) static void hci_clear_adv_cache(unsigned long arg)
@ -1425,7 +1427,7 @@ int hci_add_adv_entry(struct hci_dev *hdev,
int hci_register_dev(struct hci_dev *hdev) int hci_register_dev(struct hci_dev *hdev)
{ {
struct list_head *head = &hci_dev_list, *p; struct list_head *head = &hci_dev_list, *p;
int i, id = 0; int i, id, error;
BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name, BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name,
hdev->bus, hdev->owner); hdev->bus, hdev->owner);
@ -1433,6 +1435,11 @@ 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;
/* 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_bh(&hci_dev_list_lock); write_lock_bh(&hci_dev_list_lock);
/* Find first available device id */ /* Find first available device id */
@ -1479,6 +1486,8 @@ int hci_register_dev(struct hci_dev *hdev)
hci_conn_hash_init(hdev); hci_conn_hash_init(hdev);
INIT_LIST_HEAD(&hdev->mgmt_pending);
INIT_LIST_HEAD(&hdev->blacklist); INIT_LIST_HEAD(&hdev->blacklist);
INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->uuids);
@ -1492,8 +1501,9 @@ int hci_register_dev(struct hci_dev *hdev)
(unsigned long) hdev); (unsigned long) hdev);
INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->power_on, hci_power_on);
INIT_WORK(&hdev->power_off, hci_power_off); INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
@ -1502,10 +1512,14 @@ int hci_register_dev(struct hci_dev *hdev)
write_unlock_bh(&hci_dev_list_lock); write_unlock_bh(&hci_dev_list_lock);
hdev->workqueue = create_singlethread_workqueue(hdev->name); hdev->workqueue = create_singlethread_workqueue(hdev->name);
if (!hdev->workqueue) if (!hdev->workqueue) {
goto nomem; error = -ENOMEM;
goto err;
}
hci_register_sysfs(hdev); error = hci_add_sysfs(hdev);
if (error < 0)
goto err_wqueue;
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev);
@ -1524,17 +1538,19 @@ int hci_register_dev(struct hci_dev *hdev)
return id; return id;
nomem: err_wqueue:
destroy_workqueue(hdev->workqueue);
err:
write_lock_bh(&hci_dev_list_lock); write_lock_bh(&hci_dev_list_lock);
list_del(&hdev->list); list_del(&hdev->list);
write_unlock_bh(&hci_dev_list_lock); write_unlock_bh(&hci_dev_list_lock);
return -ENOMEM; return error;
} }
EXPORT_SYMBOL(hci_register_dev); EXPORT_SYMBOL(hci_register_dev);
/* Unregister HCI device */ /* Unregister HCI device */
int hci_unregister_dev(struct hci_dev *hdev) void hci_unregister_dev(struct hci_dev *hdev)
{ {
int i; int i;
@ -1550,8 +1566,15 @@ int hci_unregister_dev(struct hci_dev *hdev)
kfree_skb(hdev->reassembly[i]); kfree_skb(hdev->reassembly[i]);
if (!test_bit(HCI_INIT, &hdev->flags) && if (!test_bit(HCI_INIT, &hdev->flags) &&
!test_bit(HCI_SETUP, &hdev->flags)) !test_bit(HCI_SETUP, &hdev->flags)) {
mgmt_index_removed(hdev->id); hci_dev_lock_bh(hdev);
mgmt_index_removed(hdev);
hci_dev_unlock_bh(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); hci_notify(hdev, HCI_DEV_UNREG);
@ -1560,9 +1583,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
rfkill_destroy(hdev->rfkill); rfkill_destroy(hdev->rfkill);
} }
hci_unregister_sysfs(hdev); hci_del_sysfs(hdev);
hci_del_off_timer(hdev);
del_timer(&hdev->adv_timer); del_timer(&hdev->adv_timer);
destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->workqueue);
@ -1576,8 +1598,6 @@ int hci_unregister_dev(struct hci_dev *hdev)
hci_dev_unlock_bh(hdev); hci_dev_unlock_bh(hdev);
__hci_dev_put(hdev); __hci_dev_put(hdev);
return 0;
} }
EXPORT_SYMBOL(hci_unregister_dev); EXPORT_SYMBOL(hci_unregister_dev);
@ -1948,23 +1968,18 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
hdr->dlen = cpu_to_le16(len); hdr->dlen = cpu_to_le16(len);
} }
void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) 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 hci_dev *hdev = conn->hdev;
struct sk_buff *list; struct sk_buff *list;
BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
skb->dev = (void *) hdev;
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
hci_add_acl_hdr(skb, conn->handle, flags);
list = skb_shinfo(skb)->frag_list; list = skb_shinfo(skb)->frag_list;
if (!list) { if (!list) {
/* Non fragmented */ /* Non fragmented */
BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
skb_queue_tail(&conn->data_q, skb); skb_queue_tail(queue, skb);
} else { } else {
/* Fragmented */ /* Fragmented */
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
@ -1972,9 +1987,9 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
skb_shinfo(skb)->frag_list = NULL; skb_shinfo(skb)->frag_list = NULL;
/* Queue all fragments atomically */ /* Queue all fragments atomically */
spin_lock_bh(&conn->data_q.lock); spin_lock_bh(&queue->lock);
__skb_queue_tail(&conn->data_q, skb); __skb_queue_tail(queue, skb);
flags &= ~ACL_START; flags &= ~ACL_START;
flags |= ACL_CONT; flags |= ACL_CONT;
@ -1987,11 +2002,25 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags)
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
__skb_queue_tail(&conn->data_q, skb); __skb_queue_tail(queue, skb);
} while (list); } while (list);
spin_unlock_bh(&conn->data_q.lock); spin_unlock_bh(&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);
tasklet_schedule(&hdev->tx_task); tasklet_schedule(&hdev->tx_task);
} }
@ -2026,16 +2055,12 @@ EXPORT_SYMBOL(hci_send_sco);
static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) 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_hash *h = &hdev->conn_hash;
struct hci_conn *conn = NULL; struct hci_conn *conn = NULL, *c;
int num = 0, min = ~0; int num = 0, min = ~0;
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_entry(c, &h->list, list) {
struct hci_conn *c;
c = list_entry(p, struct hci_conn, list);
if (c->type != type || skb_queue_empty(&c->data_q)) if (c->type != type || skb_queue_empty(&c->data_q))
continue; continue;
@ -2084,14 +2109,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
{ {
struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn_hash *h = &hdev->conn_hash;
struct list_head *p; struct hci_conn *c;
struct hci_conn *c;
BT_ERR("%s link tx timeout", hdev->name); BT_ERR("%s link tx timeout", hdev->name);
/* Kill stalled connections */ /* Kill stalled connections */
list_for_each(p, &h->list) { list_for_each_entry(c, &h->list, list) {
c = list_entry(p, struct hci_conn, list);
if (c->type == type && c->sent) { if (c->type == type && c->sent) {
BT_ERR("%s killing stalled connection %s", BT_ERR("%s killing stalled connection %s",
hdev->name, batostr(&c->dst)); hdev->name, batostr(&c->dst));
@ -2100,11 +2123,137 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
} }
} }
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);
list_for_each_entry(conn, &h->list, list) {
struct hci_chan_hash *ch;
struct hci_chan *tmp;
if (conn->type != type)
continue;
if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
continue;
conn_num++;
ch = &conn->chan_hash;
list_for_each_entry(tmp, &ch->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;
}
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);
list_for_each_entry(conn, &h->list, list) {
struct hci_chan_hash *ch;
struct hci_chan *chan;
if (conn->type != type)
continue;
if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
continue;
num++;
ch = &conn->chan_hash;
list_for_each_entry(chan, &ch->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;
}
}
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_chan *chan;
struct sk_buff *skb; struct sk_buff *skb;
int quote; int quote;
unsigned int cnt;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
@ -2118,19 +2267,35 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
hci_link_tx_to(hdev, ACL_LINK); hci_link_tx_to(hdev, ACL_LINK);
} }
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) { cnt = hdev->acl_cnt;
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
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); hci_send_frame(skb);
hdev->acl_last_tx = jiffies; hdev->acl_last_tx = jiffies;
hdev->acl_cnt--; hdev->acl_cnt--;
conn->sent++; chan->sent++;
chan->conn->sent++;
} }
} }
if (cnt != hdev->acl_cnt)
hci_prio_recalculate(hdev, ACL_LINK);
} }
/* Schedule SCO */ /* Schedule SCO */
@ -2182,9 +2347,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
static inline void hci_sched_le(struct hci_dev *hdev) static inline void hci_sched_le(struct hci_dev *hdev)
{ {
struct hci_conn *conn; struct hci_chan *chan;
struct sk_buff *skb; struct sk_buff *skb;
int quote, cnt; int quote, cnt, tmp;
BT_DBG("%s", hdev->name); BT_DBG("%s", hdev->name);
@ -2200,21 +2365,35 @@ static inline void hci_sched_le(struct hci_dev *hdev)
} }
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
while (cnt && (conn = hci_low_sent(hdev, LE_LINK, &quote))) { tmp = cnt;
while (quote-- && (skb = skb_dequeue(&conn->data_q))) { while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, &quote))) {
BT_DBG("skb %p len %d", skb, skb->len); 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); hci_send_frame(skb);
hdev->le_last_tx = jiffies; hdev->le_last_tx = jiffies;
cnt--; cnt--;
conn->sent++; chan->sent++;
chan->conn->sent++;
} }
} }
if (hdev->le_pkts) if (hdev->le_pkts)
hdev->le_cnt = cnt; hdev->le_cnt = cnt;
else else
hdev->acl_cnt = cnt; hdev->acl_cnt = cnt;
if (cnt != tmp)
hci_prio_recalculate(hdev, LE_LINK);
} }
static void hci_tx_task(unsigned long arg) static void hci_tx_task(unsigned long arg)
@ -2407,3 +2586,31 @@ static void hci_cmd_task(unsigned long arg)
} }
} }
} }
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;
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);
}

View File

@ -58,9 +58,11 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
if (status) if (status)
return; return;
if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && clear_bit(HCI_INQUIRY, &hdev->flags);
test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 0); hci_dev_lock(hdev);
mgmt_discovering(hdev, 0);
hci_dev_unlock(hdev);
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
@ -76,10 +78,6 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
if (status) if (status)
return; return;
if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 0);
hci_conn_check_pending(hdev); hci_conn_check_pending(hdev);
} }
@ -205,13 +203,15 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent) if (!sent)
return; return;
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags)) if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_set_local_name_complete(hdev->id, sent, status); mgmt_set_local_name_complete(hdev, sent, status);
if (status) if (status == 0)
return; memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); hci_dev_unlock(hdev);
} }
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@ -274,7 +274,8 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
{ {
__u8 status = *((__u8 *) skb->data); __u8 param, status = *((__u8 *) skb->data);
int old_pscan, old_iscan;
void *sent; void *sent;
BT_DBG("%s status 0x%x", hdev->name, status); BT_DBG("%s status 0x%x", hdev->name, status);
@ -283,28 +284,40 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent) if (!sent)
return; return;
if (!status) { param = *((__u8 *) sent);
__u8 param = *((__u8 *) sent);
int old_pscan, old_iscan;
old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); hci_dev_lock(hdev);
old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags);
if (param & SCAN_INQUIRY) { if (status != 0) {
set_bit(HCI_ISCAN, &hdev->flags); mgmt_write_scan_failed(hdev, param, status);
if (!old_iscan) hdev->discov_timeout = 0;
mgmt_discoverable(hdev->id, 1); goto done;
} else if (old_iscan)
mgmt_discoverable(hdev->id, 0);
if (param & SCAN_PAGE) {
set_bit(HCI_PSCAN, &hdev->flags);
if (!old_pscan)
mgmt_connectable(hdev->id, 1);
} else if (old_pscan)
mgmt_connectable(hdev->id, 0);
} }
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); hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
} }
@ -748,6 +761,30 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, 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, static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@ -804,19 +841,24 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status); BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags)) if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status); mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
if (rp->status != 0) if (rp->status != 0)
return; goto unlock;
cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
if (!cp) if (!cp)
return; goto unlock;
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
if (conn) if (conn)
conn->pin_length = cp->pin_len; 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) static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
@ -825,10 +867,15 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status); BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags)) if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr, mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr,
rp->status); rp->status);
hci_dev_unlock(hdev);
} }
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
struct sk_buff *skb) struct sk_buff *skb)
{ {
@ -855,9 +902,13 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, rp->status); BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags)) if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr, mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
rp->status); rp->status);
hci_dev_unlock(hdev);
} }
static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
@ -867,9 +918,13 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, rp->status); BT_DBG("%s status 0x%x", hdev->name, rp->status);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags)) if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr, mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
rp->status); rp->status);
hci_dev_unlock(hdev);
} }
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
@ -879,8 +934,10 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, rp->status); BT_DBG("%s status 0x%x", hdev->name, rp->status);
mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash, hci_dev_lock(hdev);
mgmt_read_local_oob_data_reply_complete(hdev, rp->hash,
rp->randomizer, rp->status); rp->randomizer, rp->status);
hci_dev_unlock(hdev);
} }
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
@ -955,12 +1012,18 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
if (status) { if (status) {
hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_req_complete(hdev, HCI_OP_INQUIRY, status);
hci_conn_check_pending(hdev); hci_conn_check_pending(hdev);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_inquiry_failed(hdev, status);
hci_dev_unlock(hdev);
return; return;
} }
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags) && set_bit(HCI_INQUIRY, &hdev->flags);
test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 1); hci_dev_lock(hdev);
mgmt_discovering(hdev, 1);
hci_dev_unlock(hdev);
} }
static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
@ -1339,13 +1402,16 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, status); BT_DBG("%s status %d", hdev->name, status);
if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) &&
test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 0);
hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_req_complete(hdev, HCI_OP_INQUIRY, status);
hci_conn_check_pending(hdev); hci_conn_check_pending(hdev);
if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
return;
hci_dev_lock(hdev);
mgmt_discovering(hdev, 0);
hci_dev_unlock(hdev);
} }
static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
@ -1361,12 +1427,6 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 1);
}
for (; num_rsp; num_rsp--, info++) { for (; num_rsp; num_rsp--, info++) {
bacpy(&data.bdaddr, &info->bdaddr); bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_rep_mode = info->pscan_rep_mode;
@ -1377,8 +1437,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *
data.rssi = 0x00; data.rssi = 0x00;
data.ssp_mode = 0x00; data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data); hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0, mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
NULL); info->dev_class, 0, NULL);
} }
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
@ -1412,7 +1472,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->state = BT_CONFIG; conn->state = BT_CONFIG;
hci_conn_hold(conn); hci_conn_hold(conn);
conn->disc_timeout = HCI_DISCONN_TIMEOUT; conn->disc_timeout = HCI_DISCONN_TIMEOUT;
mgmt_connected(hdev->id, &ev->bdaddr, conn->type); mgmt_connected(hdev, &ev->bdaddr, conn->type);
} else } else
conn->state = BT_CONNECTED; conn->state = BT_CONNECTED;
@ -1444,7 +1504,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
} else { } else {
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
if (conn->type == ACL_LINK) if (conn->type == ACL_LINK)
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
ev->status);
} }
if (conn->type == ACL_LINK) if (conn->type == ACL_LINK)
@ -1531,7 +1592,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
struct hci_cp_reject_conn_req cp; struct hci_cp_reject_conn_req cp;
bacpy(&cp.bdaddr, &ev->bdaddr); bacpy(&cp.bdaddr, &ev->bdaddr);
cp.reason = 0x0f; cp.reason = HCI_ERROR_REJ_BAD_ADDR;
hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
} }
} }
@ -1544,7 +1605,9 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
BT_DBG("%s status %d", hdev->name, ev->status); BT_DBG("%s status %d", hdev->name, ev->status);
if (ev->status) { if (ev->status) {
mgmt_disconnect_failed(hdev->id); hci_dev_lock(hdev);
mgmt_disconnect_failed(hdev);
hci_dev_unlock(hdev);
return; return;
} }
@ -1557,7 +1620,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
if (conn->type == ACL_LINK || conn->type == LE_LINK) if (conn->type == ACL_LINK || conn->type == LE_LINK)
mgmt_disconnected(hdev->id, &conn->dst); mgmt_disconnected(hdev, &conn->dst, conn->type);
hci_proto_disconn_cfm(conn, ev->reason); hci_proto_disconn_cfm(conn, ev->reason);
hci_conn_del(conn); hci_conn_del(conn);
@ -1588,7 +1651,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s
conn->sec_level = conn->pending_sec_level; conn->sec_level = conn->pending_sec_level;
} }
} else { } else {
mgmt_auth_failed(hdev->id, &conn->dst, ev->status); mgmt_auth_failed(hdev, &conn->dst, ev->status);
} }
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
@ -1643,7 +1706,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags)) if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags))
mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name); mgmt_remote_name(hdev, &ev->bdaddr, ev->name);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (!conn) if (!conn)
@ -1898,6 +1961,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
hci_cc_write_ca_timeout(hdev, skb); hci_cc_write_ca_timeout(hdev, skb);
break; break;
case HCI_OP_READ_LOCAL_AMP_INFO:
hci_cc_read_local_amp_info(hdev, skb);
break;
case HCI_OP_DELETE_STORED_LINK_KEY: case HCI_OP_DELETE_STORED_LINK_KEY:
hci_cc_delete_stored_link_key(hdev, skb); hci_cc_delete_stored_link_key(hdev, skb);
break; break;
@ -2029,7 +2096,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
case HCI_OP_DISCONNECT: case HCI_OP_DISCONNECT:
if (ev->status != 0) if (ev->status != 0)
mgmt_disconnect_failed(hdev->id); mgmt_disconnect_failed(hdev);
break; break;
case HCI_OP_LE_CREATE_CONN: case HCI_OP_LE_CREATE_CONN:
@ -2194,7 +2261,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff
else else
secure = 0; secure = 0;
mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure); mgmt_pin_code_request(hdev, &ev->bdaddr, secure);
} }
unlock: unlock:
@ -2363,12 +2430,6 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
hci_dev_lock(hdev); hci_dev_lock(hdev);
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 1);
}
if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
struct inquiry_info_with_rssi_and_pscan_mode *info; struct inquiry_info_with_rssi_and_pscan_mode *info;
info = (void *) (skb->data + 1); info = (void *) (skb->data + 1);
@ -2383,7 +2444,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi; data.rssi = info->rssi;
data.ssp_mode = 0x00; data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data); hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev->id, &info->bdaddr, mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
info->dev_class, info->rssi, info->dev_class, info->rssi,
NULL); NULL);
} }
@ -2400,7 +2461,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi; data.rssi = info->rssi;
data.ssp_mode = 0x00; data.ssp_mode = 0x00;
hci_inquiry_cache_update(hdev, &data); hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev->id, &info->bdaddr, mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
info->dev_class, info->rssi, info->dev_class, info->rssi,
NULL); NULL);
} }
@ -2531,12 +2592,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
if (!num_rsp) if (!num_rsp)
return; return;
if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) {
if (test_bit(HCI_MGMT, &hdev->flags))
mgmt_discovering(hdev->id, 1);
}
hci_dev_lock(hdev); hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) { for (; num_rsp; num_rsp--, info++) {
@ -2549,8 +2604,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
data.rssi = info->rssi; data.rssi = info->rssi;
data.ssp_mode = 0x01; data.ssp_mode = 0x01;
hci_inquiry_cache_update(hdev, &data); hci_inquiry_cache_update(hdev, &data);
mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, mgmt_device_found(hdev, &info->bdaddr, ACL_LINK,
info->rssi, info->data); info->dev_class, info->rssi, info->data);
} }
hci_dev_unlock(hdev); hci_dev_unlock(hdev);
@ -2614,7 +2669,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff
struct hci_cp_io_capability_neg_reply cp; struct hci_cp_io_capability_neg_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr); bacpy(&cp.bdaddr, &ev->bdaddr);
cp.reason = 0x18; /* Pairing not allowed */ cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED;
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
sizeof(cp), &cp); sizeof(cp), &cp);
@ -2706,7 +2761,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev,
} }
confirm: confirm:
mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey, mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
confirm_hint); confirm_hint);
unlock: unlock:
@ -2732,7 +2787,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_
* event gets always produced as initiator and is also mapped to * event gets always produced as initiator and is also mapped to
* the mgmt_auth_failed event */ * the mgmt_auth_failed event */
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0) if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
mgmt_auth_failed(hdev->id, &conn->dst, ev->status); mgmt_auth_failed(hdev, &conn->dst, ev->status);
hci_conn_put(conn); hci_conn_put(conn);
@ -2813,14 +2868,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
} }
if (ev->status) { if (ev->status) {
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status);
hci_proto_connect_cfm(conn, ev->status); hci_proto_connect_cfm(conn, ev->status);
conn->state = BT_CLOSED; conn->state = BT_CLOSED;
hci_conn_del(conn); hci_conn_del(conn);
goto unlock; goto unlock;
} }
mgmt_connected(hdev->id, &ev->bdaddr, conn->type); mgmt_connected(hdev, &ev->bdaddr, conn->type);
conn->sec_level = BT_SECURITY_LOW; conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle); conn->handle = __le16_to_cpu(ev->handle);
@ -3104,5 +3159,5 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
kfree_skb(skb); kfree_skb(skb);
} }
module_param(enable_le, bool, 0444); module_param(enable_le, bool, 0644);
MODULE_PARM_DESC(enable_le, "Enable LE support"); MODULE_PARM_DESC(enable_le, "Enable LE support");

View File

@ -435,17 +435,12 @@ static const struct file_operations inquiry_cache_fops = {
static int blacklist_show(struct seq_file *f, void *p) static int blacklist_show(struct seq_file *f, void *p)
{ {
struct hci_dev *hdev = f->private; struct hci_dev *hdev = f->private;
struct list_head *l; struct bdaddr_list *b;
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
list_for_each(l, &hdev->blacklist) { list_for_each_entry(b, &hdev->blacklist, list)
struct bdaddr_list *b;
b = list_entry(l, struct bdaddr_list, list);
seq_printf(f, "%s\n", batostr(&b->bdaddr)); seq_printf(f, "%s\n", batostr(&b->bdaddr));
}
hci_dev_unlock_bh(hdev); hci_dev_unlock_bh(hdev);
@ -484,17 +479,12 @@ static void print_bt_uuid(struct seq_file *f, u8 *uuid)
static int uuids_show(struct seq_file *f, void *p) static int uuids_show(struct seq_file *f, void *p)
{ {
struct hci_dev *hdev = f->private; struct hci_dev *hdev = f->private;
struct list_head *l; struct bt_uuid *uuid;
hci_dev_lock_bh(hdev); hci_dev_lock_bh(hdev);
list_for_each(l, &hdev->uuids) { list_for_each_entry(uuid, &hdev->uuids, list)
struct bt_uuid *uuid;
uuid = list_entry(l, struct bt_uuid, list);
print_bt_uuid(f, uuid->uuid); print_bt_uuid(f, uuid->uuid);
}
hci_dev_unlock_bh(hdev); hci_dev_unlock_bh(hdev);
@ -542,22 +532,28 @@ static int auto_accept_delay_get(void *data, u64 *val)
DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
auto_accept_delay_set, "%llu\n"); auto_accept_delay_set, "%llu\n");
int hci_register_sysfs(struct hci_dev *hdev) void hci_init_sysfs(struct hci_dev *hdev)
{
struct device *dev = &hdev->dev;
dev->type = &bt_host;
dev->class = bt_class;
dev_set_drvdata(dev, hdev);
device_initialize(dev);
}
int hci_add_sysfs(struct hci_dev *hdev)
{ {
struct device *dev = &hdev->dev; struct device *dev = &hdev->dev;
int err; int err;
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
dev->type = &bt_host;
dev->class = bt_class;
dev->parent = hdev->parent; dev->parent = hdev->parent;
dev_set_name(dev, "%s", hdev->name); dev_set_name(dev, "%s", hdev->name);
dev_set_drvdata(dev, hdev); err = device_add(dev);
err = device_register(dev);
if (err < 0) if (err < 0)
return err; return err;
@ -581,7 +577,7 @@ int hci_register_sysfs(struct hci_dev *hdev)
return 0; return 0;
} }
void hci_unregister_sysfs(struct hci_dev *hdev) void hci_del_sysfs(struct hci_dev *hdev)
{ {
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);

View File

@ -81,24 +81,20 @@ static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
{ {
struct hidp_session *session; struct hidp_session *session;
struct list_head *p;
BT_DBG(""); BT_DBG("");
list_for_each(p, &hidp_session_list) { list_for_each_entry(session, &hidp_session_list, list) {
session = list_entry(p, struct hidp_session, list);
if (!bacmp(bdaddr, &session->bdaddr)) if (!bacmp(bdaddr, &session->bdaddr))
return session; return session;
} }
return NULL; return NULL;
} }
static void __hidp_link_session(struct hidp_session *session) static void __hidp_link_session(struct hidp_session *session)
{ {
__module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list); list_add(&session->list, &hidp_session_list);
hci_conn_hold_device(session->conn);
} }
static void __hidp_unlink_session(struct hidp_session *session) static void __hidp_unlink_session(struct hidp_session *session)
@ -106,7 +102,6 @@ static void __hidp_unlink_session(struct hidp_session *session)
hci_conn_put_device(session->conn); hci_conn_put_device(session->conn);
list_del(&session->list); list_del(&session->list);
module_put(THIS_MODULE);
} }
static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
@ -255,6 +250,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session,
BT_DBG("session %p data %p size %d", session, data, size); 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); skb = alloc_skb(size + 1, GFP_ATOMIC);
if (!skb) { if (!skb) {
BT_ERR("Can't allocate memory for new frame"); BT_ERR("Can't allocate memory for new frame");
@ -329,6 +327,7 @@ static int hidp_get_raw_report(struct hid_device *hid,
struct sk_buff *skb; struct sk_buff *skb;
size_t len; size_t len;
int numbered_reports = hid->report_enum[report_type].numbered; int numbered_reports = hid->report_enum[report_type].numbered;
int ret;
switch (report_type) { switch (report_type) {
case HID_FEATURE_REPORT: case HID_FEATURE_REPORT:
@ -352,8 +351,9 @@ static int hidp_get_raw_report(struct hid_device *hid,
session->waiting_report_number = numbered_reports ? report_number : -1; session->waiting_report_number = numbered_reports ? report_number : -1;
set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); set_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
data[0] = report_number; data[0] = report_number;
if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1);
goto err_eio; if (ret)
goto err;
/* Wait for the return of the report. The returned report /* Wait for the return of the report. The returned report
gets put in session->report_return. */ gets put in session->report_return. */
@ -365,11 +365,13 @@ static int hidp_get_raw_report(struct hid_device *hid,
5*HZ); 5*HZ);
if (res == 0) { if (res == 0) {
/* timeout */ /* timeout */
goto err_eio; ret = -EIO;
goto err;
} }
if (res < 0) { if (res < 0) {
/* signal */ /* signal */
goto err_restartsys; ret = -ERESTARTSYS;
goto err;
} }
} }
@ -390,14 +392,10 @@ static int hidp_get_raw_report(struct hid_device *hid,
return len; return len;
err_restartsys: err:
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex); mutex_unlock(&session->report_mutex);
return -ERESTARTSYS; return ret;
err_eio:
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
mutex_unlock(&session->report_mutex);
return -EIO;
} }
static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count,
@ -422,11 +420,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s
/* Set up our wait, and send the report request to the device. */ /* Set up our wait, and send the report request to the device. */
set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
if (hidp_send_ctrl_message(hid->driver_data, report_type, ret = hidp_send_ctrl_message(hid->driver_data, report_type, data,
data, count)) { count);
ret = -ENOMEM; if (ret)
goto err; goto err;
}
/* Wait for the ACK from the device. */ /* Wait for the ACK from the device. */
while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) {
@ -496,10 +493,9 @@ static void hidp_process_handshake(struct hidp_session *session,
case HIDP_HSHK_ERR_INVALID_REPORT_ID: case HIDP_HSHK_ERR_INVALID_REPORT_ID:
case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST:
case HIDP_HSHK_ERR_INVALID_PARAMETER: case HIDP_HSHK_ERR_INVALID_PARAMETER:
if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags))
clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags);
wake_up_interruptible(&session->report_queue); wake_up_interruptible(&session->report_queue);
}
/* FIXME: Call into SET_ GET_ handlers here */ /* FIXME: Call into SET_ GET_ handlers here */
break; break;
@ -520,10 +516,8 @@ static void hidp_process_handshake(struct hidp_session *session,
} }
/* Wake up the waiting thread. */ /* Wake up the waiting thread. */
if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags))
clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags);
wake_up_interruptible(&session->report_queue); wake_up_interruptible(&session->report_queue);
}
} }
static void hidp_process_hid_control(struct hidp_session *session, static void hidp_process_hid_control(struct hidp_session *session,
@ -663,7 +657,24 @@ static int hidp_send_frame(struct socket *sock, unsigned char *data, int len)
return kernel_sendmsg(sock, &msg, &iv, 1, len); return kernel_sendmsg(sock, &msg, &iv, 1, len);
} }
static void hidp_process_transmit(struct hidp_session *session) 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; struct sk_buff *skb;
@ -678,16 +689,6 @@ static void hidp_process_transmit(struct hidp_session *session)
hidp_set_timer(session); hidp_set_timer(session);
kfree_skb(skb); kfree_skb(skb);
} }
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 int hidp_session(void *arg) static int hidp_session(void *arg)
@ -700,6 +701,7 @@ static int hidp_session(void *arg)
BT_DBG("session %p", session); BT_DBG("session %p", session);
__module_get(THIS_MODULE);
set_user_nice(current, -15); set_user_nice(current, -15);
init_waitqueue_entry(&ctrl_wait, current); init_waitqueue_entry(&ctrl_wait, current);
@ -714,14 +716,6 @@ static int hidp_session(void *arg)
intr_sk->sk_state != BT_CONNECTED) intr_sk->sk_state != BT_CONNECTED)
break; break;
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);
}
while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
skb_orphan(skb); skb_orphan(skb);
if (!skb_linearize(skb)) if (!skb_linearize(skb))
@ -730,7 +724,17 @@ static int hidp_session(void *arg)
kfree_skb(skb); kfree_skb(skb);
} }
hidp_process_transmit(session); 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(); schedule();
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
@ -739,6 +743,10 @@ static int hidp_session(void *arg)
remove_wait_queue(sk_sleep(intr_sk), &intr_wait); remove_wait_queue(sk_sleep(intr_sk), &intr_wait);
remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_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); down_write(&hidp_session_sem);
hidp_del_timer(session); hidp_del_timer(session);
@ -772,34 +780,37 @@ static int hidp_session(void *arg)
kfree(session->rd_data); kfree(session->rd_data);
kfree(session); kfree(session);
module_put_and_exit(0);
return 0; return 0;
} }
static struct device *hidp_get_device(struct hidp_session *session) static struct hci_conn *hidp_get_connection(struct hidp_session *session)
{ {
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
struct device *device = NULL; struct hci_conn *conn;
struct hci_dev *hdev; struct hci_dev *hdev;
hdev = hci_get_route(dst, src); hdev = hci_get_route(dst, src);
if (!hdev) if (!hdev)
return NULL; return NULL;
session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); hci_dev_lock_bh(hdev);
if (session->conn) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
device = &session->conn->dev; if (conn)
hci_conn_hold_device(conn);
hci_dev_unlock_bh(hdev);
hci_dev_put(hdev); hci_dev_put(hdev);
return device; return conn;
} }
static int hidp_setup_input(struct hidp_session *session, static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req) struct hidp_connadd_req *req)
{ {
struct input_dev *input; struct input_dev *input;
int err, i; int i;
input = input_allocate_device(); input = input_allocate_device();
if (!input) if (!input)
@ -842,17 +853,10 @@ static int hidp_setup_input(struct hidp_session *session,
input->relbit[0] |= BIT_MASK(REL_WHEEL); input->relbit[0] |= BIT_MASK(REL_WHEEL);
} }
input->dev.parent = hidp_get_device(session); input->dev.parent = &session->conn->dev;
input->event = hidp_input_event; input->event = hidp_input_event;
err = input_register_device(input);
if (err < 0) {
input_free_device(input);
session->input = NULL;
return err;
}
return 0; return 0;
} }
@ -949,7 +953,7 @@ static int hidp_setup_hid(struct hidp_session *session,
strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
hid->dev.parent = hidp_get_device(session); hid->dev.parent = &session->conn->dev;
hid->ll_driver = &hidp_hid_driver; hid->ll_driver = &hidp_hid_driver;
hid->hid_get_raw_report = hidp_get_raw_report; hid->hid_get_raw_report = hidp_get_raw_report;
@ -976,18 +980,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst))
return -ENOTUNIQ; return -ENOTUNIQ;
session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL);
if (!session)
return -ENOMEM;
BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size);
down_write(&hidp_session_sem); down_write(&hidp_session_sem);
s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst);
if (s && s->state == BT_CONNECTED) { if (s && s->state == BT_CONNECTED) {
err = -EEXIST; up_write(&hidp_session_sem);
goto failed; 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); bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst);
@ -1003,6 +1009,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
session->intr_sock = intr_sock; session->intr_sock = intr_sock;
session->state = BT_CONNECTED; 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); setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session);
skb_queue_head_init(&session->ctrl_transmit); skb_queue_head_init(&session->ctrl_transmit);
@ -1015,9 +1027,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
session->idle_to = req->idle_to; session->idle_to = req->idle_to;
__hidp_link_session(session);
if (req->rd_size > 0) { if (req->rd_size > 0) {
err = hidp_setup_hid(session, req); err = hidp_setup_hid(session, req);
if (err && err != -ENODEV) if (err)
goto purge; goto purge;
} }
@ -1027,8 +1041,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
goto purge; goto purge;
} }
__hidp_link_session(session);
hidp_set_timer(session); hidp_set_timer(session);
if (session->hid) { if (session->hid) {
@ -1054,7 +1066,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
!session->waiting_for_startup); !session->waiting_for_startup);
} }
err = hid_add_device(session->hid); if (session->hid)
err = hid_add_device(session->hid);
else
err = input_register_device(session->input);
if (err < 0) { if (err < 0) {
atomic_inc(&session->terminate); atomic_inc(&session->terminate);
wake_up_process(session->task); wake_up_process(session->task);
@ -1077,8 +1093,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
unlink: unlink:
hidp_del_timer(session); hidp_del_timer(session);
__hidp_unlink_session(session);
if (session->input) { if (session->input) {
input_unregister_device(session->input); input_unregister_device(session->input);
session->input = NULL; session->input = NULL;
@ -1093,6 +1107,8 @@ unlink:
session->rd_data = NULL; session->rd_data = NULL;
purge: purge:
__hidp_unlink_session(session);
skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit); skb_queue_purge(&session->intr_transmit);
@ -1134,19 +1150,16 @@ int hidp_del_connection(struct hidp_conndel_req *req)
int hidp_get_connlist(struct hidp_connlist_req *req) int hidp_get_connlist(struct hidp_connlist_req *req)
{ {
struct list_head *p; struct hidp_session *session;
int err = 0, n = 0; int err = 0, n = 0;
BT_DBG(""); BT_DBG("");
down_read(&hidp_session_sem); down_read(&hidp_session_sem);
list_for_each(p, &hidp_session_list) { list_for_each_entry(session, &hidp_session_list, list) {
struct hidp_session *session;
struct hidp_conninfo ci; struct hidp_conninfo ci;
session = list_entry(p, struct hidp_session, list);
__hidp_copy_session(session, &ci); __hidp_copy_session(session, &ci);
if (copy_to_user(req->ci, &ci, sizeof(ci))) { if (copy_to_user(req->ci, &ci, sizeof(ci))) {

File diff suppressed because it is too large Load Diff

View File

@ -333,7 +333,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
opts.mode = chan->mode; opts.mode = chan->mode;
opts.fcs = chan->fcs; opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx; opts.max_tx = chan->max_tx;
opts.txwin_size = (__u16)chan->tx_win; opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, len, sizeof(opts)); len = min_t(unsigned int, len, sizeof(opts));
if (copy_to_user(optval, (char *) &opts, len)) if (copy_to_user(optval, (char *) &opts, len))
@ -358,10 +358,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break; break;
} }
if (chan->role_switch) if (test_bit(FLAG_ROLE_SWITCH, &chan->flags))
opt |= L2CAP_LM_MASTER; opt |= L2CAP_LM_MASTER;
if (chan->force_reliable) if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
opt |= L2CAP_LM_RELIABLE; opt |= L2CAP_LM_RELIABLE;
if (put_user(opt, (u32 __user *) optval)) if (put_user(opt, (u32 __user *) optval))
@ -448,7 +448,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break; break;
case BT_FLUSHABLE: case BT_FLUSHABLE:
if (put_user(chan->flushable, (u32 __user *) optval)) if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
(u32 __user *) optval))
err = -EFAULT; err = -EFAULT;
break; break;
@ -460,7 +461,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break; break;
} }
pwr.force_active = chan->force_active; pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags);
len = min_t(unsigned int, len, sizeof(pwr)); len = min_t(unsigned int, len, sizeof(pwr));
if (copy_to_user(optval, (char *) &pwr, len)) if (copy_to_user(optval, (char *) &pwr, len))
@ -468,6 +469,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
break; break;
case BT_CHANNEL_POLICY:
if (!enable_hs) {
err = -ENOPROTOOPT;
break;
}
if (put_user(chan->chan_policy, (u32 __user *) optval))
err = -EFAULT;
break;
default: default:
err = -ENOPROTOOPT; err = -ENOPROTOOPT;
break; break;
@ -502,7 +513,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
opts.mode = chan->mode; opts.mode = chan->mode;
opts.fcs = chan->fcs; opts.fcs = chan->fcs;
opts.max_tx = chan->max_tx; opts.max_tx = chan->max_tx;
opts.txwin_size = (__u16)chan->tx_win; opts.txwin_size = chan->tx_win;
len = min_t(unsigned int, sizeof(opts), optlen); len = min_t(unsigned int, sizeof(opts), optlen);
if (copy_from_user((char *) &opts, optval, len)) { if (copy_from_user((char *) &opts, optval, len)) {
@ -510,7 +521,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break; break;
} }
if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) {
err = -EINVAL; err = -EINVAL;
break; break;
} }
@ -534,7 +545,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
chan->omtu = opts.omtu; chan->omtu = opts.omtu;
chan->fcs = opts.fcs; chan->fcs = opts.fcs;
chan->max_tx = opts.max_tx; chan->max_tx = opts.max_tx;
chan->tx_win = (__u8)opts.txwin_size; chan->tx_win = opts.txwin_size;
break; break;
case L2CAP_LM: case L2CAP_LM:
@ -550,8 +561,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
if (opt & L2CAP_LM_SECURE) if (opt & L2CAP_LM_SECURE)
chan->sec_level = BT_SECURITY_HIGH; chan->sec_level = BT_SECURITY_HIGH;
chan->role_switch = (opt & L2CAP_LM_MASTER); if (opt & L2CAP_LM_MASTER)
chan->force_reliable = (opt & L2CAP_LM_RELIABLE); 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; break;
default: default:
@ -657,7 +675,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
} }
} }
chan->flushable = opt; if (opt)
set_bit(FLAG_FLUSHABLE, &chan->flags);
else
clear_bit(FLAG_FLUSHABLE, &chan->flags);
break; break;
case BT_POWER: case BT_POWER:
@ -674,7 +695,36 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
err = -EFAULT; err = -EFAULT;
break; break;
} }
chan->force_active = pwr.force_active;
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; break;
default: default:
@ -708,7 +758,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
return -ENOTCONN; return -ENOTCONN;
} }
err = l2cap_chan_send(chan, msg, len); err = l2cap_chan_send(chan, msg, len, sk->sk_priority);
release_sock(sk); release_sock(sk);
return err; return err;
@ -930,11 +980,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->fcs = pchan->fcs; chan->fcs = pchan->fcs;
chan->max_tx = pchan->max_tx; chan->max_tx = pchan->max_tx;
chan->tx_win = pchan->tx_win; chan->tx_win = pchan->tx_win;
chan->tx_win_max = pchan->tx_win_max;
chan->sec_level = pchan->sec_level; chan->sec_level = pchan->sec_level;
chan->role_switch = pchan->role_switch; chan->flags = pchan->flags;
chan->force_reliable = pchan->force_reliable;
chan->flushable = pchan->flushable;
chan->force_active = pchan->force_active;
security_sk_clone(parent, sk); security_sk_clone(parent, sk);
} else { } else {
@ -963,12 +1011,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->max_tx = L2CAP_DEFAULT_MAX_TX; chan->max_tx = L2CAP_DEFAULT_MAX_TX;
chan->fcs = L2CAP_FCS_CRC16; chan->fcs = L2CAP_FCS_CRC16;
chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; chan->tx_win = L2CAP_DEFAULT_TX_WINDOW;
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
chan->sec_level = BT_SECURITY_LOW; chan->sec_level = BT_SECURITY_LOW;
chan->role_switch = 0; chan->flags = 0;
chan->force_reliable = 0; set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
chan->flushable = BT_FLUSHABLE_OFF;
chan->force_active = BT_POWER_FORCE_ACTIVE_ON;
} }
/* Default config options */ /* Default config options */

File diff suppressed because it is too large Load Diff

View File

@ -65,7 +65,8 @@ static DEFINE_MUTEX(rfcomm_mutex);
static LIST_HEAD(session_list); static LIST_HEAD(session_list);
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
u32 priority);
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); 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_send_disc(struct rfcomm_session *s, u8 dlci);
static int rfcomm_queue_disc(struct rfcomm_dlc *d); static int rfcomm_queue_disc(struct rfcomm_dlc *d);
@ -377,13 +378,11 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
{ {
struct rfcomm_dlc *d; struct rfcomm_dlc *d;
struct list_head *p;
list_for_each(p, &s->dlcs) { list_for_each_entry(d, &s->dlcs, list)
d = list_entry(p, struct rfcomm_dlc, list);
if (d->dlci == dlci) if (d->dlci == dlci)
return d; return d;
}
return NULL; return NULL;
} }
@ -749,19 +748,34 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d
} }
/* ---- RFCOMM frame sending ---- */ /* ---- RFCOMM frame sending ---- */
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
u32 priority)
{ {
struct socket *sock = s->sock; struct socket *sock = s->sock;
struct sock *sk = sock->sk;
struct kvec iv = { data, len }; struct kvec iv = { data, len };
struct msghdr msg; struct msghdr msg;
BT_DBG("session %p len %d", s, len); BT_DBG("session %p len %d priority %u", s, len, priority);
if (sk->sk_priority != priority) {
lock_sock(sk);
sk->sk_priority = priority;
release_sock(sk);
}
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
return kernel_sendmsg(sock, &msg, &iv, 1, len); return kernel_sendmsg(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), HCI_PRIO_MAX);
}
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
{ {
struct rfcomm_cmd cmd; struct rfcomm_cmd cmd;
@ -773,7 +787,7 @@ static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0); cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd); cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); return rfcomm_send_cmd(s, &cmd);
} }
static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
@ -787,7 +801,7 @@ static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0); cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd); cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); return rfcomm_send_cmd(s, &cmd);
} }
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
@ -801,7 +815,7 @@ static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0); cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd); cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); return rfcomm_send_cmd(s, &cmd);
} }
static int rfcomm_queue_disc(struct rfcomm_dlc *d) static int rfcomm_queue_disc(struct rfcomm_dlc *d)
@ -815,6 +829,8 @@ static int rfcomm_queue_disc(struct rfcomm_dlc *d)
if (!skb) if (!skb)
return -ENOMEM; return -ENOMEM;
skb->priority = HCI_PRIO_MAX;
cmd = (void *) __skb_put(skb, sizeof(*cmd)); cmd = (void *) __skb_put(skb, sizeof(*cmd));
cmd->addr = d->addr; cmd->addr = d->addr;
cmd->ctrl = __ctrl(RFCOMM_DISC, 1); cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
@ -837,7 +853,7 @@ static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
cmd.len = __len8(0); cmd.len = __len8(0);
cmd.fcs = __fcs2((u8 *) &cmd); cmd.fcs = __fcs2((u8 *) &cmd);
return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); return rfcomm_send_cmd(s, &cmd);
} }
static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
@ -862,7 +878,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
@ -904,7 +920,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
@ -942,7 +958,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
@ -969,7 +985,7 @@ static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
@ -996,7 +1012,7 @@ static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
@ -1018,7 +1034,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
@ -1040,7 +1056,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
@ -1091,7 +1107,7 @@ static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
*ptr = __fcs(buf); ptr++; *ptr = __fcs(buf); ptr++;
return rfcomm_send_frame(s, buf, ptr - buf); return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
} }
static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
@ -1769,7 +1785,8 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
return skb_queue_len(&d->tx_queue); return skb_queue_len(&d->tx_queue);
while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
err = rfcomm_send_frame(d->session, skb->data, skb->len); err = rfcomm_send_frame(d->session, skb->data, skb->len,
skb->priority);
if (err < 0) { if (err < 0) {
skb_queue_head(&d->tx_queue, skb); skb_queue_head(&d->tx_queue, skb);
break; break;
@ -2120,15 +2137,13 @@ static struct hci_cb rfcomm_cb = {
static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
{ {
struct rfcomm_session *s; struct rfcomm_session *s;
struct list_head *pp, *p;
rfcomm_lock(); rfcomm_lock();
list_for_each(p, &session_list) { list_for_each_entry(s, &session_list, list) {
s = list_entry(p, struct rfcomm_session, list); struct rfcomm_dlc *d;
list_for_each(pp, &s->dlcs) { list_for_each_entry(d, &s->dlcs, list) {
struct sock *sk = s->sock->sk; struct sock *sk = s->sock->sk;
struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list);
seq_printf(f, "%s %s %ld %d %d %d %d\n", seq_printf(f, "%s %s %ld %d %d %d %d\n",
batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->src),

View File

@ -600,6 +600,8 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
break; break;
} }
skb->priority = sk->sk_priority;
err = rfcomm_dlc_send(d, skb); err = rfcomm_dlc_send(d, skb);
if (err < 0) { if (err < 0) {
kfree_skb(skb); kfree_skb(skb);

View File

@ -34,6 +34,7 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/workqueue.h>
#include <net/bluetooth/bluetooth.h> #include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h> #include <net/bluetooth/hci_core.h>
@ -65,7 +66,7 @@ struct rfcomm_dev {
struct rfcomm_dlc *dlc; struct rfcomm_dlc *dlc;
struct tty_struct *tty; struct tty_struct *tty;
wait_queue_head_t wait; wait_queue_head_t wait;
struct tasklet_struct wakeup_task; struct work_struct wakeup_task;
struct device *tty_dev; struct device *tty_dev;
@ -81,7 +82,7 @@ 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_state_change(struct rfcomm_dlc *dlc, int err);
static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig);
static void rfcomm_tty_wakeup(unsigned long arg); static void rfcomm_tty_wakeup(struct work_struct *work);
/* ---- Device functions ---- */ /* ---- Device functions ---- */
static void rfcomm_dev_destruct(struct rfcomm_dev *dev) static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
@ -133,13 +134,10 @@ static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
static struct rfcomm_dev *__rfcomm_dev_get(int id) static struct rfcomm_dev *__rfcomm_dev_get(int id)
{ {
struct rfcomm_dev *dev; struct rfcomm_dev *dev;
struct list_head *p;
list_for_each(p, &rfcomm_dev_list) { list_for_each_entry(dev, &rfcomm_dev_list, list)
dev = list_entry(p, struct rfcomm_dev, list);
if (dev->id == id) if (dev->id == id)
return dev; return dev;
}
return NULL; return NULL;
} }
@ -197,7 +195,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
{ {
struct rfcomm_dev *dev; struct rfcomm_dev *dev, *entry;
struct list_head *head = &rfcomm_dev_list, *p; struct list_head *head = &rfcomm_dev_list, *p;
int err = 0; int err = 0;
@ -212,8 +210,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
if (req->dev_id < 0) { if (req->dev_id < 0) {
dev->id = 0; dev->id = 0;
list_for_each(p, &rfcomm_dev_list) { list_for_each_entry(entry, &rfcomm_dev_list, list) {
if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) if (entry->id != dev->id)
break; break;
dev->id++; dev->id++;
@ -222,9 +220,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
} else { } else {
dev->id = req->dev_id; dev->id = req->dev_id;
list_for_each(p, &rfcomm_dev_list) { list_for_each_entry(entry, &rfcomm_dev_list, list) {
struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
if (entry->id == dev->id) { if (entry->id == dev->id) {
err = -EADDRINUSE; err = -EADDRINUSE;
goto out; goto out;
@ -257,7 +253,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
atomic_set(&dev->opened, 0); atomic_set(&dev->opened, 0);
init_waitqueue_head(&dev->wait); init_waitqueue_head(&dev->wait);
tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup);
skb_queue_head_init(&dev->pending); skb_queue_head_init(&dev->pending);
@ -351,7 +347,7 @@ static void rfcomm_wfree(struct sk_buff *skb)
struct rfcomm_dev *dev = (void *) skb->sk; struct rfcomm_dev *dev = (void *) skb->sk;
atomic_sub(skb->truesize, &dev->wmem_alloc); atomic_sub(skb->truesize, &dev->wmem_alloc);
if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
tasklet_schedule(&dev->wakeup_task); queue_work(system_nrt_wq, &dev->wakeup_task);
rfcomm_dev_put(dev); rfcomm_dev_put(dev);
} }
@ -455,9 +451,9 @@ static int rfcomm_release_dev(void __user *arg)
static int rfcomm_get_dev_list(void __user *arg) static int rfcomm_get_dev_list(void __user *arg)
{ {
struct rfcomm_dev *dev;
struct rfcomm_dev_list_req *dl; struct rfcomm_dev_list_req *dl;
struct rfcomm_dev_info *di; struct rfcomm_dev_info *di;
struct list_head *p;
int n = 0, size, err; int n = 0, size, err;
u16 dev_num; u16 dev_num;
@ -479,8 +475,7 @@ static int rfcomm_get_dev_list(void __user *arg)
read_lock_bh(&rfcomm_dev_lock); read_lock_bh(&rfcomm_dev_lock);
list_for_each(p, &rfcomm_dev_list) { list_for_each_entry(dev, &rfcomm_dev_list, list) {
struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
continue; continue;
(di + n)->id = dev->id; (di + n)->id = dev->id;
@ -635,9 +630,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig)
} }
/* ---- TTY functions ---- */ /* ---- TTY functions ---- */
static void rfcomm_tty_wakeup(unsigned long arg) static void rfcomm_tty_wakeup(struct work_struct *work)
{ {
struct rfcomm_dev *dev = (void *) arg; struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev,
wakeup_task);
struct tty_struct *tty = dev->tty; struct tty_struct *tty = dev->tty;
if (!tty) if (!tty)
return; return;
@ -762,7 +758,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
rfcomm_dlc_close(dev->dlc, 0); rfcomm_dlc_close(dev->dlc, 0);
clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
tasklet_kill(&dev->wakeup_task); cancel_work_sync(&dev->wakeup_task);
rfcomm_dlc_lock(dev->dlc); rfcomm_dlc_lock(dev->dlc);
tty->driver_data = NULL; tty->driver_data = NULL;
@ -1155,9 +1151,11 @@ static const struct tty_operations rfcomm_ops = {
int __init rfcomm_init_ttys(void) int __init rfcomm_init_ttys(void)
{ {
int error;
rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS);
if (!rfcomm_tty_driver) if (!rfcomm_tty_driver)
return -1; return -ENOMEM;
rfcomm_tty_driver->owner = THIS_MODULE; rfcomm_tty_driver->owner = THIS_MODULE;
rfcomm_tty_driver->driver_name = "rfcomm"; rfcomm_tty_driver->driver_name = "rfcomm";
@ -1172,10 +1170,11 @@ int __init rfcomm_init_ttys(void)
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
if (tty_register_driver(rfcomm_tty_driver)) { error = tty_register_driver(rfcomm_tty_driver);
if (error) {
BT_ERR("Can't register RFCOMM TTY driver"); BT_ERR("Can't register RFCOMM TTY driver");
put_tty_driver(rfcomm_tty_driver); put_tty_driver(rfcomm_tty_driver);
return -1; return error;
} }
BT_INFO("RFCOMM TTY layer initialized"); BT_INFO("RFCOMM TTY layer initialized");

View File

@ -181,7 +181,8 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
if (!skb) if (!skb)
return; return;
hci_send_acl(conn->hcon, skb, 0); skb->priority = HCI_PRIO_MAX;
hci_send_acl(conn->hchan, skb, 0);
mod_timer(&conn->security_timer, jiffies + mod_timer(&conn->security_timer, jiffies +
msecs_to_jiffies(SMP_TIMEOUT)); msecs_to_jiffies(SMP_TIMEOUT));