Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
This commit is contained in:
commit
f4f314bf6f
|
@ -31,6 +31,30 @@
|
|||
|
||||
#define VERSION "1.0"
|
||||
|
||||
#define ATH3K_DNLOAD 0x01
|
||||
#define ATH3K_GETSTATE 0x05
|
||||
#define ATH3K_SET_NORMAL_MODE 0x07
|
||||
#define ATH3K_GETVERSION 0x09
|
||||
#define USB_REG_SWITCH_VID_PID 0x0a
|
||||
|
||||
#define ATH3K_MODE_MASK 0x3F
|
||||
#define ATH3K_NORMAL_MODE 0x0E
|
||||
|
||||
#define ATH3K_PATCH_UPDATE 0x80
|
||||
#define ATH3K_SYSCFG_UPDATE 0x40
|
||||
|
||||
#define ATH3K_XTAL_FREQ_26M 0x00
|
||||
#define ATH3K_XTAL_FREQ_40M 0x01
|
||||
#define ATH3K_XTAL_FREQ_19P2 0x02
|
||||
#define ATH3K_NAME_LEN 0xFF
|
||||
|
||||
struct ath3k_version {
|
||||
unsigned int rom_version;
|
||||
unsigned int build_version;
|
||||
unsigned int ram_version;
|
||||
unsigned char ref_clock;
|
||||
unsigned char reserved[0x07];
|
||||
};
|
||||
|
||||
static struct usb_device_id ath3k_table[] = {
|
||||
/* Atheros AR3011 */
|
||||
|
@ -41,13 +65,29 @@ static struct usb_device_id ath3k_table[] = {
|
|||
|
||||
/* Atheros AR9285 Malbec with sflash firmware */
|
||||
{ USB_DEVICE(0x03F0, 0x311D) },
|
||||
|
||||
/* Atheros AR3012 with sflash firmware*/
|
||||
{ USB_DEVICE(0x0CF3, 0x3004) },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, ath3k_table);
|
||||
|
||||
#define BTUSB_ATH3012 0x80
|
||||
/* This table is to load patch and sysconfig files
|
||||
* for AR3012 */
|
||||
static struct usb_device_id ath3k_blist_tbl[] = {
|
||||
|
||||
/* Atheros AR3012 with sflash firmware*/
|
||||
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
#define USB_REQ_DFU_DNLOAD 1
|
||||
#define BULK_SIZE 4096
|
||||
#define FW_HDR_SIZE 20
|
||||
|
||||
static int ath3k_load_firmware(struct usb_device *udev,
|
||||
const struct firmware *firmware)
|
||||
|
@ -103,6 +143,215 @@ error:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
|
||||
{
|
||||
int pipe = 0;
|
||||
|
||||
pipe = usb_rcvctrlpipe(udev, 0);
|
||||
return usb_control_msg(udev, pipe, ATH3K_GETSTATE,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
|
||||
state, 0x01, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ath3k_get_version(struct usb_device *udev,
|
||||
struct ath3k_version *version)
|
||||
{
|
||||
int pipe = 0;
|
||||
|
||||
pipe = usb_rcvctrlpipe(udev, 0);
|
||||
return usb_control_msg(udev, pipe, ATH3K_GETVERSION,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, version,
|
||||
sizeof(struct ath3k_version),
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ath3k_load_fwfile(struct usb_device *udev,
|
||||
const struct firmware *firmware)
|
||||
{
|
||||
u8 *send_buf;
|
||||
int err, pipe, len, size, count, sent = 0;
|
||||
int ret;
|
||||
|
||||
count = firmware->size;
|
||||
|
||||
send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC);
|
||||
if (!send_buf) {
|
||||
BT_ERR("Can't allocate memory chunk for firmware");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
size = min_t(uint, count, FW_HDR_SIZE);
|
||||
memcpy(send_buf, firmware->data, size);
|
||||
|
||||
pipe = usb_sndctrlpipe(udev, 0);
|
||||
ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD,
|
||||
USB_TYPE_VENDOR, 0, 0, send_buf,
|
||||
size, USB_CTRL_SET_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Can't change to loading configuration err");
|
||||
kfree(send_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sent += size;
|
||||
count -= size;
|
||||
|
||||
while (count) {
|
||||
size = min_t(uint, count, BULK_SIZE);
|
||||
pipe = usb_sndbulkpipe(udev, 0x02);
|
||||
|
||||
memcpy(send_buf, firmware->data + sent, size);
|
||||
|
||||
err = usb_bulk_msg(udev, pipe, send_buf, size,
|
||||
&len, 3000);
|
||||
if (err || (len != size)) {
|
||||
BT_ERR("Error in firmware loading err = %d,"
|
||||
"len = %d, size = %d", err, len, size);
|
||||
kfree(send_buf);
|
||||
return err;
|
||||
}
|
||||
sent += size;
|
||||
count -= size;
|
||||
}
|
||||
|
||||
kfree(send_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath3k_switch_pid(struct usb_device *udev)
|
||||
{
|
||||
int pipe = 0;
|
||||
|
||||
pipe = usb_sndctrlpipe(udev, 0);
|
||||
return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID,
|
||||
USB_TYPE_VENDOR, 0, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ath3k_set_normal_mode(struct usb_device *udev)
|
||||
{
|
||||
unsigned char fw_state;
|
||||
int pipe = 0, ret;
|
||||
|
||||
ret = ath3k_get_state(udev, &fw_state);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Can't get state to change to normal mode err");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
|
||||
BT_DBG("firmware was already in normal mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pipe = usb_sndctrlpipe(udev, 0);
|
||||
return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE,
|
||||
USB_TYPE_VENDOR, 0, 0,
|
||||
NULL, 0, USB_CTRL_SET_TIMEOUT);
|
||||
}
|
||||
|
||||
static int ath3k_load_patch(struct usb_device *udev)
|
||||
{
|
||||
unsigned char fw_state;
|
||||
char filename[ATH3K_NAME_LEN] = {0};
|
||||
const struct firmware *firmware;
|
||||
struct ath3k_version fw_version, pt_version;
|
||||
int ret;
|
||||
|
||||
ret = ath3k_get_state(udev, &fw_state);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Can't get state to change to load ram patch err");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fw_state & ATH3K_PATCH_UPDATE) {
|
||||
BT_DBG("Patch was already downloaded");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ath3k_get_version(udev, &fw_version);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Can't get version to change to load ram patch err");
|
||||
return ret;
|
||||
}
|
||||
|
||||
snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
|
||||
fw_version.rom_version);
|
||||
|
||||
ret = request_firmware(&firmware, filename, &udev->dev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Patch file not found %s", filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pt_version.rom_version = *(int *)(firmware->data + firmware->size - 8);
|
||||
pt_version.build_version = *(int *)
|
||||
(firmware->data + firmware->size - 4);
|
||||
|
||||
if ((pt_version.rom_version != fw_version.rom_version) ||
|
||||
(pt_version.build_version <= fw_version.build_version)) {
|
||||
BT_ERR("Patch file version did not match with firmware");
|
||||
release_firmware(firmware);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ath3k_load_fwfile(udev, firmware);
|
||||
release_firmware(firmware);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath3k_load_syscfg(struct usb_device *udev)
|
||||
{
|
||||
unsigned char fw_state;
|
||||
char filename[ATH3K_NAME_LEN] = {0};
|
||||
const struct firmware *firmware;
|
||||
struct ath3k_version fw_version;
|
||||
int clk_value, ret;
|
||||
|
||||
ret = ath3k_get_state(udev, &fw_state);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Can't get state to change to load configration err");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ret = ath3k_get_version(udev, &fw_version);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Can't get version to change to load ram patch err");
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (fw_version.ref_clock) {
|
||||
|
||||
case ATH3K_XTAL_FREQ_26M:
|
||||
clk_value = 26;
|
||||
break;
|
||||
case ATH3K_XTAL_FREQ_40M:
|
||||
clk_value = 40;
|
||||
break;
|
||||
case ATH3K_XTAL_FREQ_19P2:
|
||||
clk_value = 19;
|
||||
break;
|
||||
default:
|
||||
clk_value = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
|
||||
fw_version.rom_version, clk_value, ".dfu");
|
||||
|
||||
ret = request_firmware(&firmware, filename, &udev->dev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Configuration file not found %s", filename);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ath3k_load_fwfile(udev, firmware);
|
||||
release_firmware(firmware);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath3k_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
|
@ -115,7 +364,37 @@ static int ath3k_probe(struct usb_interface *intf,
|
|||
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* match device ID in ath3k blacklist table */
|
||||
if (!id->driver_info) {
|
||||
const struct usb_device_id *match;
|
||||
match = usb_match_id(intf, ath3k_blist_tbl);
|
||||
if (match)
|
||||
id = match;
|
||||
}
|
||||
|
||||
/* load patch and sysconfig files for AR3012 */
|
||||
if (id->driver_info & BTUSB_ATH3012) {
|
||||
ret = ath3k_load_patch(udev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Loading patch file failed");
|
||||
return ret;
|
||||
}
|
||||
ret = ath3k_load_syscfg(udev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Loading sysconfig file failed");
|
||||
return ret;
|
||||
}
|
||||
ret = ath3k_set_normal_mode(udev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Set normal mode failed");
|
||||
return ret;
|
||||
}
|
||||
ath3k_switch_pid(udev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) {
|
||||
BT_ERR("Error loading firmware");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,9 @@ static struct usb_device_id blacklist_table[] = {
|
|||
/* Atheros AR9285 Malbec with sflash firmware */
|
||||
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
|
||||
|
||||
/* Atheros 3012 with sflash firmware */
|
||||
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
|
||||
|
||||
/* Broadcom BCM2035 */
|
||||
{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
|
||||
{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
|
||||
|
@ -711,15 +714,11 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||
pipe = usb_sndisocpipe(data->udev,
|
||||
data->isoc_tx_ep->bEndpointAddress);
|
||||
|
||||
urb->dev = data->udev;
|
||||
urb->pipe = pipe;
|
||||
urb->context = skb;
|
||||
urb->complete = btusb_isoc_tx_complete;
|
||||
urb->interval = data->isoc_tx_ep->bInterval;
|
||||
usb_fill_int_urb(urb, data->udev, pipe,
|
||||
skb->data, skb->len, btusb_isoc_tx_complete,
|
||||
skb, data->isoc_tx_ep->bInterval);
|
||||
|
||||
urb->transfer_flags = URB_ISO_ASAP;
|
||||
urb->transfer_buffer = skb->data;
|
||||
urb->transfer_buffer_length = skb->len;
|
||||
|
||||
__fill_isoc_descriptor(urb, skb->len,
|
||||
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
|
||||
|
|
|
@ -398,6 +398,7 @@ static int hci_uart_register_dev(struct hci_uart *hu)
|
|||
hdev->flush = hci_uart_flush;
|
||||
hdev->send = hci_uart_send_frame;
|
||||
hdev->destruct = hci_uart_destruct;
|
||||
hdev->parent = hu->tty->dev;
|
||||
|
||||
hdev->owner = THIS_MODULE;
|
||||
|
||||
|
|
|
@ -205,4 +205,32 @@ extern void bt_sysfs_cleanup(void);
|
|||
|
||||
extern struct dentry *bt_debugfs;
|
||||
|
||||
#ifdef CONFIG_BT_L2CAP
|
||||
int l2cap_init(void);
|
||||
void l2cap_exit(void);
|
||||
#else
|
||||
static inline int l2cap_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void l2cap_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_SCO
|
||||
int sco_init(void);
|
||||
void sco_exit(void);
|
||||
#else
|
||||
static inline int sco_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sco_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BLUETOOTH_H */
|
||||
|
|
|
@ -119,6 +119,7 @@ enum {
|
|||
#define HCI_PAIRING_TIMEOUT (60000) /* 60 seconds */
|
||||
#define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */
|
||||
#define HCI_INIT_TIMEOUT (10000) /* 10 seconds */
|
||||
#define HCI_CMD_TIMEOUT (1000) /* 1 seconds */
|
||||
|
||||
/* HCI data types */
|
||||
#define HCI_COMMAND_PKT 0x01
|
||||
|
@ -168,6 +169,8 @@ enum {
|
|||
#define SCO_LINK 0x00
|
||||
#define ACL_LINK 0x01
|
||||
#define ESCO_LINK 0x02
|
||||
/* Low Energy links do not have defined link type. Use invented one */
|
||||
#define LE_LINK 0x80
|
||||
|
||||
/* LMP features */
|
||||
#define LMP_3SLOT 0x01
|
||||
|
@ -242,6 +245,8 @@ enum {
|
|||
#define HCI_AT_GENERAL_BONDING_MITM 0x05
|
||||
|
||||
/* ----- HCI Commands ---- */
|
||||
#define HCI_OP_NOP 0x0000
|
||||
|
||||
#define HCI_OP_INQUIRY 0x0401
|
||||
struct hci_cp_inquiry {
|
||||
__u8 lap[3];
|
||||
|
@ -642,6 +647,47 @@ struct hci_rp_read_bd_addr {
|
|||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_EVENT_MASK 0x2001
|
||||
struct hci_cp_le_set_event_mask {
|
||||
__u8 mask[8];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_BUFFER_SIZE 0x2002
|
||||
struct hci_rp_le_read_buffer_size {
|
||||
__u8 status;
|
||||
__le16 le_mtu;
|
||||
__u8 le_max_pkt;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_CREATE_CONN 0x200d
|
||||
struct hci_cp_le_create_conn {
|
||||
__le16 scan_interval;
|
||||
__le16 scan_window;
|
||||
__u8 filter_policy;
|
||||
__u8 peer_addr_type;
|
||||
bdaddr_t peer_addr;
|
||||
__u8 own_address_type;
|
||||
__le16 conn_interval_min;
|
||||
__le16 conn_interval_max;
|
||||
__le16 conn_latency;
|
||||
__le16 supervision_timeout;
|
||||
__le16 min_ce_len;
|
||||
__le16 max_ce_len;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_CREATE_CONN_CANCEL 0x200e
|
||||
|
||||
#define HCI_OP_LE_CONN_UPDATE 0x2013
|
||||
struct hci_cp_le_conn_update {
|
||||
__le16 handle;
|
||||
__le16 conn_interval_min;
|
||||
__le16 conn_interval_max;
|
||||
__le16 conn_latency;
|
||||
__le16 supervision_timeout;
|
||||
__le16 min_ce_len;
|
||||
__le16 max_ce_len;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
#define HCI_EV_INQUIRY_COMPLETE 0x01
|
||||
|
||||
|
@ -902,6 +948,25 @@ struct hci_ev_remote_host_features {
|
|||
__u8 features[8];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_META 0x3e
|
||||
struct hci_ev_le_meta {
|
||||
__u8 subevent;
|
||||
} __packed;
|
||||
|
||||
/* Low energy meta events */
|
||||
#define HCI_EV_LE_CONN_COMPLETE 0x01
|
||||
struct hci_ev_le_conn_complete {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__u8 role;
|
||||
__u8 bdaddr_type;
|
||||
bdaddr_t bdaddr;
|
||||
__le16 interval;
|
||||
__le16 latency;
|
||||
__le16 supervision_timeout;
|
||||
__u8 clk_accurancy;
|
||||
} __packed;
|
||||
|
||||
/* Internal events generated by Bluetooth stack */
|
||||
#define HCI_EV_STACK_INTERNAL 0xfd
|
||||
struct hci_ev_stack_internal {
|
||||
|
|
|
@ -60,6 +60,7 @@ struct hci_conn_hash {
|
|||
spinlock_t lock;
|
||||
unsigned int acl_num;
|
||||
unsigned int sco_num;
|
||||
unsigned int le_num;
|
||||
};
|
||||
|
||||
struct bdaddr_list {
|
||||
|
@ -122,15 +123,18 @@ struct hci_dev {
|
|||
atomic_t cmd_cnt;
|
||||
unsigned int acl_cnt;
|
||||
unsigned int sco_cnt;
|
||||
unsigned int le_cnt;
|
||||
|
||||
unsigned int acl_mtu;
|
||||
unsigned int sco_mtu;
|
||||
unsigned int le_mtu;
|
||||
unsigned int acl_pkts;
|
||||
unsigned int sco_pkts;
|
||||
unsigned int le_pkts;
|
||||
|
||||
unsigned long cmd_last_tx;
|
||||
unsigned long acl_last_tx;
|
||||
unsigned long sco_last_tx;
|
||||
unsigned long le_last_tx;
|
||||
|
||||
struct workqueue_struct *workqueue;
|
||||
|
||||
|
@ -138,6 +142,7 @@ struct hci_dev {
|
|||
struct work_struct power_off;
|
||||
struct timer_list off_timer;
|
||||
|
||||
struct timer_list cmd_timer;
|
||||
struct tasklet_struct cmd_task;
|
||||
struct tasklet_struct rx_task;
|
||||
struct tasklet_struct tx_task;
|
||||
|
@ -194,37 +199,37 @@ struct hci_dev {
|
|||
struct hci_conn {
|
||||
struct list_head list;
|
||||
|
||||
atomic_t refcnt;
|
||||
spinlock_t lock;
|
||||
atomic_t refcnt;
|
||||
spinlock_t lock;
|
||||
|
||||
bdaddr_t dst;
|
||||
__u16 handle;
|
||||
__u16 state;
|
||||
__u8 mode;
|
||||
__u8 type;
|
||||
__u8 out;
|
||||
__u8 attempt;
|
||||
__u8 dev_class[3];
|
||||
__u8 features[8];
|
||||
__u8 ssp_mode;
|
||||
__u16 interval;
|
||||
__u16 pkt_type;
|
||||
__u16 link_policy;
|
||||
__u32 link_mode;
|
||||
__u8 auth_type;
|
||||
__u8 sec_level;
|
||||
__u8 pending_sec_level;
|
||||
__u8 pin_length;
|
||||
__u8 io_capability;
|
||||
__u8 power_save;
|
||||
__u16 disc_timeout;
|
||||
unsigned long pend;
|
||||
bdaddr_t dst;
|
||||
__u16 handle;
|
||||
__u16 state;
|
||||
__u8 mode;
|
||||
__u8 type;
|
||||
__u8 out;
|
||||
__u8 attempt;
|
||||
__u8 dev_class[3];
|
||||
__u8 features[8];
|
||||
__u8 ssp_mode;
|
||||
__u16 interval;
|
||||
__u16 pkt_type;
|
||||
__u16 link_policy;
|
||||
__u32 link_mode;
|
||||
__u8 auth_type;
|
||||
__u8 sec_level;
|
||||
__u8 pending_sec_level;
|
||||
__u8 pin_length;
|
||||
__u8 io_capability;
|
||||
__u8 power_save;
|
||||
__u16 disc_timeout;
|
||||
unsigned long pend;
|
||||
|
||||
__u8 remote_cap;
|
||||
__u8 remote_oob;
|
||||
__u8 remote_auth;
|
||||
|
||||
unsigned int sent;
|
||||
unsigned int sent;
|
||||
|
||||
struct sk_buff_head data_q;
|
||||
|
||||
|
@ -309,24 +314,40 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
|
|||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
list_add(&c->list, &h->list);
|
||||
if (c->type == ACL_LINK)
|
||||
switch (c->type) {
|
||||
case ACL_LINK:
|
||||
h->acl_num++;
|
||||
else
|
||||
break;
|
||||
case LE_LINK:
|
||||
h->le_num++;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
h->sco_num++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
list_del(&c->list);
|
||||
if (c->type == ACL_LINK)
|
||||
switch (c->type) {
|
||||
case ACL_LINK:
|
||||
h->acl_num--;
|
||||
else
|
||||
break;
|
||||
case LE_LINK:
|
||||
h->le_num--;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
h->sco_num--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
|
||||
__u16 handle)
|
||||
__u16 handle)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct list_head *p;
|
||||
|
@ -341,7 +362,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
|
||||
__u8 type, bdaddr_t *ba)
|
||||
__u8 type, bdaddr_t *ba)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct list_head *p;
|
||||
|
@ -356,7 +377,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_ba(struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
|
||||
__u8 type, __u16 state)
|
||||
__u8 type, __u16 state)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct list_head *p;
|
||||
|
@ -504,6 +525,7 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
|||
#define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO)
|
||||
#define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR)
|
||||
#define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH)
|
||||
#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE)
|
||||
|
||||
/* ----- HCI protocols ----- */
|
||||
struct hci_proto {
|
||||
|
@ -755,4 +777,6 @@ struct hci_sec_filter {
|
|||
|
||||
void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result);
|
||||
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||
u16 latency, u16 to_multiplier);
|
||||
#endif /* __HCI_CORE_H */
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
|
||||
#define L2CAP_DEFAULT_ACK_TO 200
|
||||
#define L2CAP_LOCAL_BUSY_TRIES 12
|
||||
#define L2CAP_LE_DEFAULT_MTU 23
|
||||
|
||||
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
|
||||
#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */
|
||||
|
@ -88,6 +89,8 @@ struct l2cap_conninfo {
|
|||
#define L2CAP_ECHO_RSP 0x09
|
||||
#define L2CAP_INFO_REQ 0x0a
|
||||
#define L2CAP_INFO_RSP 0x0b
|
||||
#define L2CAP_CONN_PARAM_UPDATE_REQ 0x12
|
||||
#define L2CAP_CONN_PARAM_UPDATE_RSP 0x13
|
||||
|
||||
/* L2CAP feature mask */
|
||||
#define L2CAP_FEAT_FLOWCTL 0x00000001
|
||||
|
@ -160,6 +163,9 @@ struct l2cap_conn_rsp {
|
|||
/* channel indentifier */
|
||||
#define L2CAP_CID_SIGNALING 0x0001
|
||||
#define L2CAP_CID_CONN_LESS 0x0002
|
||||
#define L2CAP_CID_LE_DATA 0x0004
|
||||
#define L2CAP_CID_LE_SIGNALING 0x0005
|
||||
#define L2CAP_CID_SMP 0x0006
|
||||
#define L2CAP_CID_DYN_START 0x0040
|
||||
#define L2CAP_CID_DYN_END 0xffff
|
||||
|
||||
|
@ -255,6 +261,21 @@ struct l2cap_info_rsp {
|
|||
#define L2CAP_IR_SUCCESS 0x0000
|
||||
#define L2CAP_IR_NOTSUPP 0x0001
|
||||
|
||||
struct l2cap_conn_param_update_req {
|
||||
__le16 min;
|
||||
__le16 max;
|
||||
__le16 latency;
|
||||
__le16 to_multiplier;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_conn_param_update_rsp {
|
||||
__le16 result;
|
||||
} __packed;
|
||||
|
||||
/* Connection Parameters result */
|
||||
#define L2CAP_CONN_PARAM_ACCEPTED 0x0000
|
||||
#define L2CAP_CONN_PARAM_REJECTED 0x0001
|
||||
|
||||
/* ----- L2CAP connections ----- */
|
||||
struct l2cap_chan_list {
|
||||
struct sock *head;
|
||||
|
@ -455,6 +476,4 @@ void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk, int err);
|
|||
void l2cap_chan_del(struct sock *sk, int err);
|
||||
int l2cap_do_connect(struct sock *sk);
|
||||
|
||||
void l2cap_load(void);
|
||||
|
||||
#endif /* __L2CAP_H */
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef __SMP_H
|
||||
#define __SMP_H
|
||||
|
||||
struct smp_command_hdr {
|
||||
__u8 code;
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_PAIRING_REQ 0x01
|
||||
#define SMP_CMD_PAIRING_RSP 0x02
|
||||
struct smp_cmd_pairing {
|
||||
__u8 io_capability;
|
||||
__u8 oob_flag;
|
||||
__u8 auth_req;
|
||||
__u8 max_key_size;
|
||||
__u8 init_key_dist;
|
||||
__u8 resp_key_dist;
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_PAIRING_CONFIRM 0x03
|
||||
struct smp_cmd_pairing_confirm {
|
||||
__u8 confirm_val[16];
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_PAIRING_RANDOM 0x04
|
||||
struct smp_cmd_pairing_random {
|
||||
__u8 rand_val[16];
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_PAIRING_FAIL 0x05
|
||||
struct smp_cmd_pairing_fail {
|
||||
__u8 reason;
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_ENCRYPT_INFO 0x06
|
||||
struct smp_cmd_encrypt_info {
|
||||
__u8 ltk[16];
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_MASTER_IDENT 0x07
|
||||
struct smp_cmd_master_ident {
|
||||
__u16 ediv;
|
||||
__u8 rand[8];
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_IDENT_INFO 0x08
|
||||
struct smp_cmd_ident_info {
|
||||
__u8 irk[16];
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_IDENT_ADDR_INFO 0x09
|
||||
struct smp_cmd_ident_addr_info {
|
||||
__u8 addr_type;
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_SIGN_INFO 0x0a
|
||||
struct smp_cmd_sign_info {
|
||||
__u8 csrk[16];
|
||||
} __packed;
|
||||
|
||||
#define SMP_CMD_SECURITY_REQ 0x0b
|
||||
struct smp_cmd_security_req {
|
||||
__u8 auth_req;
|
||||
} __packed;
|
||||
|
||||
#define SMP_PASSKEY_ENTRY_FAILED 0x01
|
||||
#define SMP_OOB_NOT_AVAIL 0x02
|
||||
#define SMP_AUTH_REQUIREMENTS 0x03
|
||||
#define SMP_CONFIRM_FAILED 0x04
|
||||
#define SMP_PAIRING_NOTSUPP 0x05
|
||||
#define SMP_ENC_KEY_SIZE 0x06
|
||||
#define SMP_CMD_NOTSUPP 0x07
|
||||
#define SMP_UNSPECIFIED 0x08
|
||||
#define SMP_REPEATED_ATTEMPTS 0x09
|
||||
|
||||
#endif /* __SMP_H */
|
|
@ -32,7 +32,7 @@ menuconfig BT
|
|||
more information, see <http://www.bluez.org/>.
|
||||
|
||||
config BT_L2CAP
|
||||
tristate "L2CAP protocol support"
|
||||
bool "L2CAP protocol support"
|
||||
depends on BT
|
||||
select CRC16
|
||||
help
|
||||
|
@ -40,19 +40,13 @@ config BT_L2CAP
|
|||
connection oriented and connection-less data transport. L2CAP
|
||||
support is required for most Bluetooth applications.
|
||||
|
||||
Say Y here to compile L2CAP support into the kernel or say M to
|
||||
compile it as module (l2cap).
|
||||
|
||||
config BT_SCO
|
||||
tristate "SCO links support"
|
||||
bool "SCO links support"
|
||||
depends on BT
|
||||
help
|
||||
SCO link provides voice transport over Bluetooth. SCO support is
|
||||
required for voice applications like Headset and Audio.
|
||||
|
||||
Say Y here to compile SCO support into the kernel or say M to
|
||||
compile it as module (sco).
|
||||
|
||||
source "net/bluetooth/rfcomm/Kconfig"
|
||||
|
||||
source "net/bluetooth/bnep/Kconfig"
|
||||
|
|
|
@ -3,12 +3,11 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_BT) += bluetooth.o
|
||||
obj-$(CONFIG_BT_L2CAP) += l2cap.o
|
||||
obj-$(CONFIG_BT_SCO) += sco.o
|
||||
obj-$(CONFIG_BT_RFCOMM) += rfcomm/
|
||||
obj-$(CONFIG_BT_BNEP) += bnep/
|
||||
obj-$(CONFIG_BT_CMTP) += cmtp/
|
||||
obj-$(CONFIG_BT_HIDP) += hidp/
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
||||
l2cap-y := l2cap_core.o l2cap_sock.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o
|
||||
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#define VERSION "2.15"
|
||||
#define VERSION "2.16"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
|
@ -397,7 +397,7 @@ static inline unsigned int bt_accept_poll(struct sock *parent)
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned int bt_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
|
||||
unsigned int bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
unsigned int mask = 0;
|
||||
|
@ -545,13 +545,41 @@ static int __init bt_init(void)
|
|||
|
||||
BT_INFO("HCI device and connection manager initialized");
|
||||
|
||||
hci_sock_init();
|
||||
err = hci_sock_init();
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
err = l2cap_init();
|
||||
if (err < 0) {
|
||||
hci_sock_cleanup();
|
||||
goto sock_err;
|
||||
}
|
||||
|
||||
err = sco_init();
|
||||
if (err < 0) {
|
||||
l2cap_exit();
|
||||
goto sock_err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
sock_err:
|
||||
hci_sock_cleanup();
|
||||
|
||||
error:
|
||||
sock_unregister(PF_BLUETOOTH);
|
||||
bt_sysfs_cleanup();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit bt_exit(void)
|
||||
{
|
||||
|
||||
sco_exit();
|
||||
|
||||
l2cap_exit();
|
||||
|
||||
hci_sock_cleanup();
|
||||
|
||||
sock_unregister(PF_BLUETOOTH);
|
||||
|
|
|
@ -708,8 +708,6 @@ static int __init bnep_init(void)
|
|||
{
|
||||
char flt[50] = "";
|
||||
|
||||
l2cap_load();
|
||||
|
||||
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
|
||||
strcat(flt, "protocol ");
|
||||
#endif
|
||||
|
|
|
@ -88,6 +88,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
|
|||
sockfd_put(nsock);
|
||||
return -EBADFD;
|
||||
}
|
||||
ca.device[sizeof(ca.device)-1] = 0;
|
||||
|
||||
err = bnep_add_connection(&ca, nsock);
|
||||
if (!err) {
|
||||
|
|
|
@ -469,8 +469,6 @@ int cmtp_get_conninfo(struct cmtp_conninfo *ci)
|
|||
|
||||
static int __init cmtp_init(void)
|
||||
{
|
||||
l2cap_load();
|
||||
|
||||
BT_INFO("CMTP (CAPI Emulation) ver %s", VERSION);
|
||||
|
||||
cmtp_init_sockets();
|
||||
|
|
|
@ -45,6 +45,33 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
static void hci_le_connect(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_create_conn cp;
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = 1;
|
||||
conn->link_mode |= HCI_LM_MASTER;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.scan_interval = cpu_to_le16(0x0004);
|
||||
cp.scan_window = cpu_to_le16(0x0004);
|
||||
bacpy(&cp.peer_addr, &conn->dst);
|
||||
cp.conn_interval_min = cpu_to_le16(0x0008);
|
||||
cp.conn_interval_max = cpu_to_le16(0x0100);
|
||||
cp.supervision_timeout = cpu_to_le16(0x0064);
|
||||
cp.min_ce_len = cpu_to_le16(0x0001);
|
||||
cp.max_ce_len = cpu_to_le16(0x0001);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_connect_cancel(struct hci_conn *conn)
|
||||
{
|
||||
hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
|
||||
}
|
||||
|
||||
void hci_acl_connect(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
@ -156,6 +183,26 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
|||
hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
||||
u16 latency, u16 to_multiplier)
|
||||
{
|
||||
struct hci_cp_le_conn_update cp;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.conn_interval_min = cpu_to_le16(min);
|
||||
cp.conn_interval_max = cpu_to_le16(max);
|
||||
cp.conn_latency = cpu_to_le16(latency);
|
||||
cp.supervision_timeout = cpu_to_le16(to_multiplier);
|
||||
cp.min_ce_len = cpu_to_le16(0x0001);
|
||||
cp.max_ce_len = cpu_to_le16(0x0001);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_le_conn_update);
|
||||
|
||||
/* Device _must_ be locked */
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
|
@ -193,8 +240,12 @@ static void hci_conn_timeout(unsigned long arg)
|
|||
switch (conn->state) {
|
||||
case BT_CONNECT:
|
||||
case BT_CONNECT2:
|
||||
if (conn->type == ACL_LINK && conn->out)
|
||||
hci_acl_connect_cancel(conn);
|
||||
if (conn->out) {
|
||||
if (conn->type == ACL_LINK)
|
||||
hci_acl_connect_cancel(conn);
|
||||
else if (conn->type == LE_LINK)
|
||||
hci_le_connect_cancel(conn);
|
||||
}
|
||||
break;
|
||||
case BT_CONFIG:
|
||||
case BT_CONNECTED:
|
||||
|
@ -296,6 +347,11 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
|
||||
/* Unacked frames */
|
||||
hdev->acl_cnt += conn->sent;
|
||||
} else if (conn->type == LE_LINK) {
|
||||
if (hdev->le_pkts)
|
||||
hdev->le_cnt += conn->sent;
|
||||
else
|
||||
hdev->acl_cnt += conn->sent;
|
||||
} else {
|
||||
struct hci_conn *acl = conn->link;
|
||||
if (acl) {
|
||||
|
@ -361,15 +417,30 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
|||
}
|
||||
EXPORT_SYMBOL(hci_get_route);
|
||||
|
||||
/* Create SCO or ACL connection.
|
||||
/* Create SCO, ACL or LE connection.
|
||||
* Device _must_ be locked */
|
||||
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
|
||||
{
|
||||
struct hci_conn *acl;
|
||||
struct hci_conn *sco;
|
||||
struct hci_conn *le;
|
||||
|
||||
BT_DBG("%s dst %s", hdev->name, batostr(dst));
|
||||
|
||||
if (type == LE_LINK) {
|
||||
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
|
||||
if (!le)
|
||||
le = hci_conn_add(hdev, LE_LINK, dst);
|
||||
if (!le)
|
||||
return NULL;
|
||||
if (le->state == BT_OPEN)
|
||||
hci_le_connect(le);
|
||||
|
||||
hci_conn_hold(le);
|
||||
|
||||
return le;
|
||||
}
|
||||
|
||||
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
|
||||
if (!acl) {
|
||||
acl = hci_conn_add(hdev, ACL_LINK, dst);
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/timer.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
|
@ -123,7 +124,7 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
|
|||
|
||||
/* Execute request and wait for completion. */
|
||||
static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
|
||||
unsigned long opt, __u32 timeout)
|
||||
unsigned long opt, __u32 timeout)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int err = 0;
|
||||
|
@ -165,7 +166,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
static inline int hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
|
||||
unsigned long opt, __u32 timeout)
|
||||
unsigned long opt, __u32 timeout)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -263,6 +264,14 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
|||
hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
{
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
/* Read LE buffer size */
|
||||
hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
|
||||
}
|
||||
|
||||
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
|
||||
{
|
||||
__u8 scan = opt;
|
||||
|
@ -456,7 +465,7 @@ int hci_inquiry(void __user *arg)
|
|||
/* cache_dump can't sleep. Therefore we allocate temp buffer and then
|
||||
* copy it to the user space.
|
||||
*/
|
||||
buf = kmalloc(sizeof(struct inquiry_info) *max_rsp, GFP_KERNEL);
|
||||
buf = kmalloc(sizeof(struct inquiry_info) * max_rsp, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
|
@ -525,10 +534,13 @@ int hci_dev_open(__u16 dev)
|
|||
set_bit(HCI_INIT, &hdev->flags);
|
||||
hdev->init_last_cmd = 0;
|
||||
|
||||
//__hci_request(hdev, hci_reset_req, 0, HZ);
|
||||
ret = __hci_request(hdev, hci_init_req, 0,
|
||||
msecs_to_jiffies(HCI_INIT_TIMEOUT));
|
||||
|
||||
if (lmp_le_capable(hdev))
|
||||
ret = __hci_request(hdev, hci_le_init_req, 0,
|
||||
msecs_to_jiffies(HCI_INIT_TIMEOUT));
|
||||
|
||||
clear_bit(HCI_INIT, &hdev->flags);
|
||||
}
|
||||
|
||||
|
@ -611,6 +623,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||
|
||||
/* Drop last sent command */
|
||||
if (hdev->sent_cmd) {
|
||||
del_timer_sync(&hdev->cmd_timer);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
hdev->sent_cmd = NULL;
|
||||
}
|
||||
|
@ -671,7 +684,7 @@ int hci_dev_reset(__u16 dev)
|
|||
hdev->flush(hdev);
|
||||
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
hdev->acl_cnt = 0; hdev->sco_cnt = 0;
|
||||
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
|
||||
|
||||
if (!test_bit(HCI_RAW, &hdev->flags))
|
||||
ret = __hci_request(hdev, hci_reset_req, 0,
|
||||
|
@ -1054,6 +1067,16 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* HCI command timer function */
|
||||
static void hci_cmd_timer(unsigned long arg)
|
||||
{
|
||||
struct hci_dev *hdev = (void *) arg;
|
||||
|
||||
BT_ERR("%s command tx timeout", hdev->name);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
}
|
||||
|
||||
/* Register HCI device */
|
||||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
|
@ -1100,6 +1123,8 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
skb_queue_head_init(&hdev->cmd_q);
|
||||
skb_queue_head_init(&hdev->raw_q);
|
||||
|
||||
setup_timer(&hdev->cmd_timer, hci_cmd_timer, (unsigned long) hdev);
|
||||
|
||||
for (i = 0; i < NUM_REASSEMBLY; i++)
|
||||
hdev->reassembly[i] = NULL;
|
||||
|
||||
|
@ -1187,6 +1212,8 @@ int hci_unregister_dev(struct hci_dev *hdev)
|
|||
|
||||
hci_unregister_sysfs(hdev);
|
||||
|
||||
hci_del_off_timer(hdev);
|
||||
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
@ -1672,8 +1699,25 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
|
|||
}
|
||||
|
||||
if (conn) {
|
||||
int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
|
||||
int q = cnt / num;
|
||||
int cnt, q;
|
||||
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
cnt = hdev->acl_cnt;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
cnt = hdev->sco_cnt;
|
||||
break;
|
||||
case LE_LINK:
|
||||
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
break;
|
||||
default:
|
||||
cnt = 0;
|
||||
BT_ERR("Unknown link type");
|
||||
}
|
||||
|
||||
q = cnt / num;
|
||||
*quote = q ? q : 1;
|
||||
} else
|
||||
*quote = 0;
|
||||
|
@ -1682,19 +1726,19 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int
|
|||
return conn;
|
||||
}
|
||||
|
||||
static inline void hci_acl_tx_to(struct hci_dev *hdev)
|
||||
static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct list_head *p;
|
||||
struct hci_conn *c;
|
||||
|
||||
BT_ERR("%s ACL tx timeout", hdev->name);
|
||||
BT_ERR("%s link tx timeout", hdev->name);
|
||||
|
||||
/* Kill stalled connections */
|
||||
list_for_each(p, &h->list) {
|
||||
c = list_entry(p, struct hci_conn, list);
|
||||
if (c->type == ACL_LINK && c->sent) {
|
||||
BT_ERR("%s killing stalled ACL connection %s",
|
||||
if (c->type == type && c->sent) {
|
||||
BT_ERR("%s killing stalled connection %s",
|
||||
hdev->name, batostr(&c->dst));
|
||||
hci_acl_disconn(c, 0x13);
|
||||
}
|
||||
|
@ -1713,7 +1757,7 @@ static inline void hci_sched_acl(struct hci_dev *hdev)
|
|||
/* ACL tx timeout must be longer than maximum
|
||||
* link supervision timeout (40.9 seconds) */
|
||||
if (!hdev->acl_cnt && time_after(jiffies, hdev->acl_last_tx + HZ * 45))
|
||||
hci_acl_tx_to(hdev);
|
||||
hci_link_tx_to(hdev, ACL_LINK);
|
||||
}
|
||||
|
||||
while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) {
|
||||
|
@ -1772,6 +1816,40 @@ static inline void hci_sched_esco(struct hci_dev *hdev)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void hci_sched_le(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff *skb;
|
||||
int quote, cnt;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!test_bit(HCI_RAW, &hdev->flags)) {
|
||||
/* LE tx timeout must be longer than maximum
|
||||
* link supervision timeout (40.9 seconds) */
|
||||
if (!hdev->le_cnt && hdev->le_pkts &&
|
||||
time_after(jiffies, hdev->le_last_tx + HZ * 45))
|
||||
hci_link_tx_to(hdev, LE_LINK);
|
||||
}
|
||||
|
||||
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
|
||||
while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
|
||||
hci_send_frame(skb);
|
||||
hdev->le_last_tx = jiffies;
|
||||
|
||||
cnt--;
|
||||
conn->sent++;
|
||||
}
|
||||
}
|
||||
if (hdev->le_pkts)
|
||||
hdev->le_cnt = cnt;
|
||||
else
|
||||
hdev->acl_cnt = cnt;
|
||||
}
|
||||
|
||||
static void hci_tx_task(unsigned long arg)
|
||||
{
|
||||
struct hci_dev *hdev = (struct hci_dev *) arg;
|
||||
|
@ -1779,7 +1857,8 @@ static void hci_tx_task(unsigned long arg)
|
|||
|
||||
read_lock(&hci_task_lock);
|
||||
|
||||
BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
|
||||
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
|
||||
hdev->sco_cnt, hdev->le_cnt);
|
||||
|
||||
/* Schedule queues and send stuff to HCI driver */
|
||||
|
||||
|
@ -1789,6 +1868,8 @@ static void hci_tx_task(unsigned long arg)
|
|||
|
||||
hci_sched_esco(hdev);
|
||||
|
||||
hci_sched_le(hdev);
|
||||
|
||||
/* Send next queued raw (unknown type) packet */
|
||||
while ((skb = skb_dequeue(&hdev->raw_q)))
|
||||
hci_send_frame(skb);
|
||||
|
@ -1936,11 +2017,6 @@ static void hci_cmd_task(unsigned long arg)
|
|||
|
||||
BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
|
||||
|
||||
if (!atomic_read(&hdev->cmd_cnt) && time_after(jiffies, hdev->cmd_last_tx + HZ)) {
|
||||
BT_ERR("%s command tx timeout", hdev->name);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
}
|
||||
|
||||
/* Send queued commands */
|
||||
if (atomic_read(&hdev->cmd_cnt)) {
|
||||
skb = skb_dequeue(&hdev->cmd_q);
|
||||
|
@ -1953,7 +2029,8 @@ static void hci_cmd_task(unsigned long arg)
|
|||
if (hdev->sent_cmd) {
|
||||
atomic_dec(&hdev->cmd_cnt);
|
||||
hci_send_frame(skb);
|
||||
hdev->cmd_last_tx = jiffies;
|
||||
mod_timer(&hdev->cmd_timer,
|
||||
jiffies + msecs_to_jiffies(HCI_CMD_TIMEOUT));
|
||||
} else {
|
||||
skb_queue_head(&hdev->cmd_q, skb);
|
||||
tasklet_schedule(&hdev->cmd_task);
|
||||
|
|
|
@ -776,6 +776,25 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
|
||||
rp->status);
|
||||
}
|
||||
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_buffer_size *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hdev->le_mtu = __le16_to_cpu(rp->le_mtu);
|
||||
hdev->le_pkts = rp->le_max_pkt;
|
||||
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
|
||||
BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
|
||||
}
|
||||
|
||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
|
@ -919,7 +938,7 @@ static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status)
|
|||
}
|
||||
|
||||
static int hci_outgoing_auth_needed(struct hci_dev *hdev,
|
||||
struct hci_conn *conn)
|
||||
struct hci_conn *conn)
|
||||
{
|
||||
if (conn->state != BT_CONFIG || !conn->out)
|
||||
return 0;
|
||||
|
@ -1107,6 +1126,43 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
struct hci_cp_le_create_conn *cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
|
||||
|
||||
BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
|
||||
conn);
|
||||
|
||||
if (status) {
|
||||
if (conn && conn->state == BT_CONNECT) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_proto_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
} else {
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
|
||||
if (conn)
|
||||
conn->out = 1;
|
||||
else
|
||||
BT_ERR("No memory for new connection");
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
@ -1237,7 +1293,8 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
|
||||
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
|
||||
|
||||
if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
||||
if ((mask & HCI_LM_ACCEPT) &&
|
||||
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
||||
/* Connection accepted */
|
||||
struct inquiry_entry *ie;
|
||||
struct hci_conn *conn;
|
||||
|
@ -1667,11 +1724,18 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
hci_cc_pin_code_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_BUFFER_SIZE:
|
||||
hci_cc_le_read_buffer_size(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ev->opcode != HCI_OP_NOP)
|
||||
del_timer(&hdev->cmd_timer);
|
||||
|
||||
if (ev->ncmd) {
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
if (!skb_queue_empty(&hdev->cmd_q))
|
||||
|
@ -1738,11 +1802,18 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
mgmt_disconnect_failed(hdev->id);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_CREATE_CONN:
|
||||
hci_cs_le_create_conn(hdev, ev->status);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ev->opcode != HCI_OP_NOP)
|
||||
del_timer(&hdev->cmd_timer);
|
||||
|
||||
if (ev->ncmd) {
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
if (!skb_queue_empty(&hdev->cmd_q))
|
||||
|
@ -1808,6 +1879,16 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s
|
|||
hdev->acl_cnt += count;
|
||||
if (hdev->acl_cnt > hdev->acl_pkts)
|
||||
hdev->acl_cnt = hdev->acl_pkts;
|
||||
} else if (conn->type == LE_LINK) {
|
||||
if (hdev->le_pkts) {
|
||||
hdev->le_cnt += count;
|
||||
if (hdev->le_cnt > hdev->le_pkts)
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
} else {
|
||||
hdev->acl_cnt += count;
|
||||
if (hdev->acl_cnt > hdev->acl_pkts)
|
||||
hdev->acl_cnt = hdev->acl_pkts;
|
||||
}
|
||||
} else {
|
||||
hdev->sco_cnt += count;
|
||||
if (hdev->sco_cnt > hdev->sco_pkts)
|
||||
|
@ -2021,7 +2102,8 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct
|
|||
hci_dev_lock(hdev);
|
||||
|
||||
if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) {
|
||||
struct inquiry_info_with_rssi_and_pscan_mode *info = (void *) (skb->data + 1);
|
||||
struct inquiry_info_with_rssi_and_pscan_mode *info;
|
||||
info = (void *) (skb->data + 1);
|
||||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
|
@ -2162,17 +2244,8 @@ static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buf
|
|||
static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_sniff_subrate *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status %d", hdev->name, ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -2190,12 +2263,12 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct
|
|||
|
||||
for (; num_rsp; num_rsp--) {
|
||||
bacpy(&data.bdaddr, &info->bdaddr);
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
data.pscan_mode = 0x00;
|
||||
data.pscan_rep_mode = info->pscan_rep_mode;
|
||||
data.pscan_period_mode = info->pscan_period_mode;
|
||||
data.pscan_mode = 0x00;
|
||||
memcpy(data.dev_class, info->dev_class, 3);
|
||||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.clock_offset = info->clock_offset;
|
||||
data.rssi = info->rssi;
|
||||
data.ssp_mode = 0x01;
|
||||
info++;
|
||||
hci_inquiry_cache_update(hdev, &data);
|
||||
|
@ -2321,6 +2394,60 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status %d", hdev->name, ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr);
|
||||
if (!conn) {
|
||||
BT_ERR("No memory for new connection");
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->status) {
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
conn->state = BT_CLOSED;
|
||||
hci_conn_del(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_conn_hold_device(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
||||
|
||||
skb_pull(skb, sizeof(*le_ev));
|
||||
|
||||
switch (le_ev->subevent) {
|
||||
case HCI_EV_LE_CONN_COMPLETE:
|
||||
hci_le_conn_complete_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_event_hdr *hdr = (void *) skb->data;
|
||||
|
@ -2461,6 +2588,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_remote_host_features_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_META:
|
||||
hci_le_meta_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s event 0x%x", hdev->name, event);
|
||||
break;
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
static struct class *bt_class;
|
||||
|
||||
struct dentry *bt_debugfs = NULL;
|
||||
struct dentry *bt_debugfs;
|
||||
EXPORT_SYMBOL_GPL(bt_debugfs);
|
||||
|
||||
static inline char *link_typetostr(int type)
|
||||
|
@ -51,8 +51,8 @@ static ssize_t show_link_features(struct device *dev, struct device_attribute *a
|
|||
conn->features[6], conn->features[7]);
|
||||
}
|
||||
|
||||
#define LINK_ATTR(_name,_mode,_show,_store) \
|
||||
struct device_attribute link_attr_##_name = __ATTR(_name,_mode,_show,_store)
|
||||
#define LINK_ATTR(_name, _mode, _show, _store) \
|
||||
struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store)
|
||||
|
||||
static LINK_ATTR(type, S_IRUGO, show_link_type, NULL);
|
||||
static LINK_ATTR(address, S_IRUGO, show_link_address, NULL);
|
||||
|
|
|
@ -1019,8 +1019,6 @@ static int __init hidp_init(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
l2cap_load();
|
||||
|
||||
BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
|
||||
|
||||
ret = hid_register_driver(&hidp_driver);
|
||||
|
|
|
@ -55,8 +55,6 @@
|
|||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#define VERSION "2.15"
|
||||
|
||||
int disable_ertm;
|
||||
|
||||
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
|
||||
|
@ -183,8 +181,16 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
|
|||
l2cap_pi(sk)->conn = conn;
|
||||
|
||||
if (sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM) {
|
||||
/* Alloc CID for connection-oriented socket */
|
||||
l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
|
||||
if (conn->hcon->type == LE_LINK) {
|
||||
/* LE connection */
|
||||
l2cap_pi(sk)->omtu = L2CAP_LE_DEFAULT_MTU;
|
||||
l2cap_pi(sk)->scid = L2CAP_CID_LE_DATA;
|
||||
l2cap_pi(sk)->dcid = L2CAP_CID_LE_DATA;
|
||||
} else {
|
||||
/* Alloc CID for connection-oriented socket */
|
||||
l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
|
||||
l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
|
||||
}
|
||||
} else if (sk->sk_type == SOCK_DGRAM) {
|
||||
/* Connectionless socket */
|
||||
l2cap_pi(sk)->scid = L2CAP_CID_CONN_LESS;
|
||||
|
@ -583,6 +589,82 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
}
|
||||
}
|
||||
|
||||
/* Find socket with cid and source bdaddr.
|
||||
* Returns closest match, locked.
|
||||
*/
|
||||
static struct sock *l2cap_get_sock_by_scid(int state, __le16 cid, bdaddr_t *src)
|
||||
{
|
||||
struct sock *s, *sk = NULL, *sk1 = NULL;
|
||||
struct hlist_node *node;
|
||||
|
||||
read_lock(&l2cap_sk_list.lock);
|
||||
|
||||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
||||
if (state && sk->sk_state != state)
|
||||
continue;
|
||||
|
||||
if (l2cap_pi(sk)->scid == cid) {
|
||||
/* Exact match. */
|
||||
if (!bacmp(&bt_sk(sk)->src, src))
|
||||
break;
|
||||
|
||||
/* Closest match */
|
||||
if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
|
||||
sk1 = sk;
|
||||
}
|
||||
}
|
||||
s = node ? sk : sk1;
|
||||
if (s)
|
||||
bh_lock_sock(s);
|
||||
read_unlock(&l2cap_sk_list.lock);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
||||
{
|
||||
struct l2cap_chan_list *list = &conn->chan_list;
|
||||
struct sock *parent, *uninitialized_var(sk);
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
/* Check if we have socket listening on cid */
|
||||
parent = l2cap_get_sock_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
|
||||
conn->src);
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
/* Check for backlog size */
|
||||
if (sk_acceptq_is_full(parent)) {
|
||||
BT_DBG("backlog full %d", parent->sk_ack_backlog);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP, GFP_ATOMIC);
|
||||
if (!sk)
|
||||
goto clean;
|
||||
|
||||
write_lock_bh(&list->lock);
|
||||
|
||||
hci_conn_hold(conn->hcon);
|
||||
|
||||
l2cap_sock_init(sk, parent);
|
||||
bacpy(&bt_sk(sk)->src, conn->src);
|
||||
bacpy(&bt_sk(sk)->dst, conn->dst);
|
||||
|
||||
__l2cap_chan_add(conn, sk, parent);
|
||||
|
||||
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
parent->sk_data_ready(parent, 0);
|
||||
|
||||
write_unlock_bh(&list->lock);
|
||||
|
||||
clean:
|
||||
bh_unlock_sock(parent);
|
||||
}
|
||||
|
||||
static void l2cap_conn_ready(struct l2cap_conn *conn)
|
||||
{
|
||||
struct l2cap_chan_list *l = &conn->chan_list;
|
||||
|
@ -590,11 +672,20 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (!conn->hcon->out && conn->hcon->type == LE_LINK)
|
||||
l2cap_le_conn_ready(conn);
|
||||
|
||||
read_lock(&l->lock);
|
||||
|
||||
for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if (conn->hcon->type == LE_LINK) {
|
||||
l2cap_sock_clear_timer(sk);
|
||||
sk->sk_state = BT_CONNECTED;
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
|
||||
if (sk->sk_type != SOCK_SEQPACKET &&
|
||||
sk->sk_type != SOCK_STREAM) {
|
||||
l2cap_sock_clear_timer(sk);
|
||||
|
@ -653,7 +744,11 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|||
|
||||
BT_DBG("hcon %p conn %p", hcon, conn);
|
||||
|
||||
conn->mtu = hcon->hdev->acl_mtu;
|
||||
if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
|
||||
conn->mtu = hcon->hdev->le_mtu;
|
||||
else
|
||||
conn->mtu = hcon->hdev->acl_mtu;
|
||||
|
||||
conn->src = &hcon->hdev->bdaddr;
|
||||
conn->dst = &hcon->dst;
|
||||
|
||||
|
@ -662,7 +757,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
|||
spin_lock_init(&conn->lock);
|
||||
rwlock_init(&conn->chan_list.lock);
|
||||
|
||||
setup_timer(&conn->info_timer, l2cap_info_timeout,
|
||||
if (hcon->type != LE_LINK)
|
||||
setup_timer(&conn->info_timer, l2cap_info_timeout,
|
||||
(unsigned long) conn);
|
||||
|
||||
conn->disc_reason = 0x13;
|
||||
|
@ -760,8 +856,13 @@ int l2cap_do_connect(struct sock *sk)
|
|||
|
||||
auth_type = l2cap_get_auth_type(sk);
|
||||
|
||||
hcon = hci_connect(hdev, ACL_LINK, dst,
|
||||
if (l2cap_pi(sk)->dcid == L2CAP_CID_LE_DATA)
|
||||
hcon = hci_connect(hdev, LE_LINK, dst,
|
||||
l2cap_pi(sk)->sec_level, auth_type);
|
||||
else
|
||||
hcon = hci_connect(hdev, ACL_LINK, dst,
|
||||
l2cap_pi(sk)->sec_level, auth_type);
|
||||
|
||||
if (!hcon)
|
||||
goto done;
|
||||
|
||||
|
@ -1327,7 +1428,11 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
|
|||
|
||||
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
|
||||
lh->len = cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
|
||||
lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
|
||||
|
||||
if (conn->hcon->type == LE_LINK)
|
||||
lh->cid = cpu_to_le16(L2CAP_CID_LE_SIGNALING);
|
||||
else
|
||||
lh->cid = cpu_to_le16(L2CAP_CID_SIGNALING);
|
||||
|
||||
cmd = (struct l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
|
||||
cmd->code = code;
|
||||
|
@ -1566,10 +1671,6 @@ done:
|
|||
break;
|
||||
}
|
||||
|
||||
/* FIXME: Need actual value of the flush timeout */
|
||||
//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
|
||||
// l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
|
||||
|
||||
req->dcid = cpu_to_le16(pi->dcid);
|
||||
req->flags = cpu_to_le16(0);
|
||||
|
||||
|
@ -2396,12 +2497,153 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
|
||||
u16 to_multiplier)
|
||||
{
|
||||
u16 max_latency;
|
||||
|
||||
if (min > max || min < 6 || max > 3200)
|
||||
return -EINVAL;
|
||||
|
||||
if (to_multiplier < 10 || to_multiplier > 3200)
|
||||
return -EINVAL;
|
||||
|
||||
if (max >= to_multiplier * 8)
|
||||
return -EINVAL;
|
||||
|
||||
max_latency = (to_multiplier * 8 / max) - 1;
|
||||
if (latency > 499 || latency > max_latency)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u8 *data)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct l2cap_conn_param_update_req *req;
|
||||
struct l2cap_conn_param_update_rsp rsp;
|
||||
u16 min, max, latency, to_multiplier, cmd_len;
|
||||
int err;
|
||||
|
||||
if (!(hcon->link_mode & HCI_LM_MASTER))
|
||||
return -EINVAL;
|
||||
|
||||
cmd_len = __le16_to_cpu(cmd->len);
|
||||
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
|
||||
return -EPROTO;
|
||||
|
||||
req = (struct l2cap_conn_param_update_req *) data;
|
||||
min = __le16_to_cpu(req->min);
|
||||
max = __le16_to_cpu(req->max);
|
||||
latency = __le16_to_cpu(req->latency);
|
||||
to_multiplier = __le16_to_cpu(req->to_multiplier);
|
||||
|
||||
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x",
|
||||
min, max, latency, to_multiplier);
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
err = l2cap_check_conn_param(min, max, latency, to_multiplier);
|
||||
if (err)
|
||||
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
|
||||
else
|
||||
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);
|
||||
|
||||
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
|
||||
sizeof(rsp), &rsp);
|
||||
|
||||
if (!err)
|
||||
hci_le_conn_update(hcon, min, max, latency, to_multiplier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
switch (cmd->code) {
|
||||
case L2CAP_COMMAND_REJ:
|
||||
l2cap_command_rej(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONN_REQ:
|
||||
err = l2cap_connect_req(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONN_RSP:
|
||||
err = l2cap_connect_rsp(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_REQ:
|
||||
err = l2cap_config_req(conn, cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_RSP:
|
||||
err = l2cap_config_rsp(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_DISCONN_REQ:
|
||||
err = l2cap_disconnect_req(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_DISCONN_RSP:
|
||||
err = l2cap_disconnect_rsp(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_ECHO_REQ:
|
||||
l2cap_send_cmd(conn, cmd->ident, L2CAP_ECHO_RSP, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_ECHO_RSP:
|
||||
break;
|
||||
|
||||
case L2CAP_INFO_REQ:
|
||||
err = l2cap_information_req(conn, cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_INFO_RSP:
|
||||
err = l2cap_information_rsp(conn, cmd, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
||||
struct l2cap_cmd_hdr *cmd, u8 *data)
|
||||
{
|
||||
switch (cmd->code) {
|
||||
case L2CAP_COMMAND_REJ:
|
||||
return 0;
|
||||
|
||||
case L2CAP_CONN_PARAM_UPDATE_REQ:
|
||||
return l2cap_conn_param_update_req(conn, cmd, data);
|
||||
|
||||
case L2CAP_CONN_PARAM_UPDATE_RSP:
|
||||
return 0;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
u8 *data = skb->data;
|
||||
int len = skb->len;
|
||||
struct l2cap_cmd_hdr cmd;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
l2cap_raw_recv(conn, skb);
|
||||
|
||||
|
@ -2420,55 +2662,10 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk
|
|||
break;
|
||||
}
|
||||
|
||||
switch (cmd.code) {
|
||||
case L2CAP_COMMAND_REJ:
|
||||
l2cap_command_rej(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONN_REQ:
|
||||
err = l2cap_connect_req(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONN_RSP:
|
||||
err = l2cap_connect_rsp(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_REQ:
|
||||
err = l2cap_config_req(conn, &cmd, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_CONF_RSP:
|
||||
err = l2cap_config_rsp(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_DISCONN_REQ:
|
||||
err = l2cap_disconnect_req(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_DISCONN_RSP:
|
||||
err = l2cap_disconnect_rsp(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_ECHO_REQ:
|
||||
l2cap_send_cmd(conn, cmd.ident, L2CAP_ECHO_RSP, cmd_len, data);
|
||||
break;
|
||||
|
||||
case L2CAP_ECHO_RSP:
|
||||
break;
|
||||
|
||||
case L2CAP_INFO_REQ:
|
||||
err = l2cap_information_req(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
case L2CAP_INFO_RSP:
|
||||
err = l2cap_information_rsp(conn, &cmd, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown signaling command 0x%2.2x", cmd.code);
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (conn->hcon->type == LE_LINK)
|
||||
err = l2cap_le_sig_cmd(conn, &cmd, data);
|
||||
else
|
||||
err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
|
||||
|
||||
if (err) {
|
||||
struct l2cap_cmd_rej rej;
|
||||
|
@ -3465,6 +3662,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
BT_DBG("len %d, cid 0x%4.4x", len, cid);
|
||||
|
||||
switch (cid) {
|
||||
case L2CAP_CID_LE_SIGNALING:
|
||||
case L2CAP_CID_SIGNALING:
|
||||
l2cap_sig_channel(conn, skb);
|
||||
break;
|
||||
|
@ -3522,7 +3720,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
|||
|
||||
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
|
||||
|
||||
if (hcon->type != ACL_LINK)
|
||||
if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
|
||||
return -EINVAL;
|
||||
|
||||
if (!status) {
|
||||
|
@ -3551,7 +3749,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
|||
{
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
if (hcon->type != ACL_LINK)
|
||||
if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
|
||||
return -EINVAL;
|
||||
|
||||
l2cap_conn_del(hcon, bt_err(reason));
|
||||
|
@ -3768,12 +3966,13 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
|
|||
sk_for_each(sk, node, &l2cap_sk_list.head) {
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
|
||||
seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
|
||||
batostr(&bt_sk(sk)->src),
|
||||
batostr(&bt_sk(sk)->dst),
|
||||
sk->sk_state, __le16_to_cpu(pi->psm),
|
||||
pi->scid, pi->dcid,
|
||||
pi->imtu, pi->omtu, pi->sec_level);
|
||||
pi->imtu, pi->omtu, pi->sec_level,
|
||||
pi->mode);
|
||||
}
|
||||
|
||||
read_unlock_bh(&l2cap_sk_list.lock);
|
||||
|
@ -3806,7 +4005,7 @@ static struct hci_proto l2cap_hci_proto = {
|
|||
.recv_acldata = l2cap_recv_acldata
|
||||
};
|
||||
|
||||
static int __init l2cap_init(void)
|
||||
int __init l2cap_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
@ -3834,7 +4033,6 @@ static int __init l2cap_init(void)
|
|||
BT_ERR("Failed to create L2CAP debug file");
|
||||
}
|
||||
|
||||
BT_INFO("L2CAP ver %s", VERSION);
|
||||
BT_INFO("L2CAP socket layer initialized");
|
||||
|
||||
return 0;
|
||||
|
@ -3845,7 +4043,7 @@ error:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void __exit l2cap_exit(void)
|
||||
void l2cap_exit(void)
|
||||
{
|
||||
debugfs_remove(l2cap_debugfs);
|
||||
|
||||
|
@ -3858,22 +4056,5 @@ static void __exit l2cap_exit(void)
|
|||
l2cap_cleanup_sockets();
|
||||
}
|
||||
|
||||
void l2cap_load(void)
|
||||
{
|
||||
/* Dummy function to trigger automatic L2CAP module loading by
|
||||
* other modules that use L2CAP sockets but don't use any other
|
||||
* symbols from it. */
|
||||
}
|
||||
EXPORT_SYMBOL(l2cap_load);
|
||||
|
||||
module_init(l2cap_init);
|
||||
module_exit(l2cap_exit);
|
||||
|
||||
module_param(disable_ertm, bool, 0644);
|
||||
MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode");
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("bt-proto-0");
|
||||
|
|
|
@ -103,7 +103,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||
len = min_t(unsigned int, sizeof(la), alen);
|
||||
memcpy(&la, addr, len);
|
||||
|
||||
if (la.l2_cid)
|
||||
if (la.l2_cid && la.l2_psm)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
|
@ -145,6 +145,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
|
|||
l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
|
||||
}
|
||||
|
||||
if (la.l2_cid)
|
||||
l2cap_pi(sk)->scid = la.l2_cid;
|
||||
|
||||
write_unlock_bh(&l2cap_sk_list.lock);
|
||||
|
||||
done:
|
||||
|
@ -168,13 +171,13 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
|||
len = min_t(unsigned int, sizeof(la), alen);
|
||||
memcpy(&la, addr, len);
|
||||
|
||||
if (la.l2_cid)
|
||||
if (la.l2_cid && la.l2_psm)
|
||||
return -EINVAL;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if ((sk->sk_type == SOCK_SEQPACKET || sk->sk_type == SOCK_STREAM)
|
||||
&& !la.l2_psm) {
|
||||
&& !(la.l2_psm || la.l2_cid)) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
@ -216,7 +219,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
|||
|
||||
/* PSM must be odd and lsb of upper byte must be 0 */
|
||||
if ((__le16_to_cpu(la.l2_psm) & 0x0101) != 0x0001 &&
|
||||
sk->sk_type != SOCK_RAW) {
|
||||
sk->sk_type != SOCK_RAW && !la.l2_cid) {
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
@ -224,6 +227,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
|
|||
/* Set destination address and psm */
|
||||
bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
|
||||
l2cap_pi(sk)->psm = la.l2_psm;
|
||||
l2cap_pi(sk)->dcid = la.l2_cid;
|
||||
|
||||
err = l2cap_do_connect(sk);
|
||||
if (err)
|
||||
|
@ -265,7 +269,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (!l2cap_pi(sk)->psm) {
|
||||
if (!l2cap_pi(sk)->psm && !l2cap_pi(sk)->dcid) {
|
||||
bdaddr_t *src = &bt_sk(sk)->src;
|
||||
u16 psm;
|
||||
|
||||
|
@ -392,6 +396,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
|
|||
|
||||
switch (optname) {
|
||||
case L2CAP_OPTIONS:
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
opts.imtu = l2cap_pi(sk)->imtu;
|
||||
opts.omtu = l2cap_pi(sk)->omtu;
|
||||
opts.flush_to = l2cap_pi(sk)->flush_to;
|
||||
|
@ -880,6 +885,8 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
|
|||
|
||||
void __l2cap_sock_close(struct sock *sk, int reason)
|
||||
{
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
|
||||
BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
|
||||
|
||||
switch (sk->sk_state) {
|
||||
|
@ -889,10 +896,9 @@ void __l2cap_sock_close(struct sock *sk, int reason)
|
|||
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
if (sk->sk_type == SOCK_SEQPACKET ||
|
||||
sk->sk_type == SOCK_STREAM) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
|
||||
if ((sk->sk_type == SOCK_SEQPACKET ||
|
||||
sk->sk_type == SOCK_STREAM) &&
|
||||
conn->hcon->type == ACL_LINK) {
|
||||
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
|
||||
l2cap_send_disconn_req(conn, sk, reason);
|
||||
} else
|
||||
|
@ -900,9 +906,9 @@ void __l2cap_sock_close(struct sock *sk, int reason)
|
|||
break;
|
||||
|
||||
case BT_CONNECT2:
|
||||
if (sk->sk_type == SOCK_SEQPACKET ||
|
||||
sk->sk_type == SOCK_STREAM) {
|
||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||
if ((sk->sk_type == SOCK_SEQPACKET ||
|
||||
sk->sk_type == SOCK_STREAM) &&
|
||||
conn->hcon->type == ACL_LINK) {
|
||||
struct l2cap_conn_rsp rsp;
|
||||
__u16 result;
|
||||
|
||||
|
@ -1121,30 +1127,30 @@ static const struct net_proto_family l2cap_sock_family_ops = {
|
|||
|
||||
int __init l2cap_init_sockets(void)
|
||||
{
|
||||
int err;
|
||||
int err;
|
||||
|
||||
err = proto_register(&l2cap_proto, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = proto_register(&l2cap_proto, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = bt_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
BT_INFO("L2CAP socket layer initialized");
|
||||
BT_INFO("L2CAP socket layer initialized");
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
BT_ERR("L2CAP socket registration failed");
|
||||
proto_unregister(&l2cap_proto);
|
||||
return err;
|
||||
BT_ERR("L2CAP socket registration failed");
|
||||
proto_unregister(&l2cap_proto);
|
||||
return err;
|
||||
}
|
||||
|
||||
void l2cap_cleanup_sockets(void)
|
||||
{
|
||||
if (bt_sock_unregister(BTPROTO_L2CAP) < 0)
|
||||
BT_ERR("L2CAP socket unregistration failed");
|
||||
if (bt_sock_unregister(BTPROTO_L2CAP) < 0)
|
||||
BT_ERR("L2CAP socket unregistration failed");
|
||||
|
||||
proto_unregister(&l2cap_proto);
|
||||
proto_unregister(&l2cap_proto);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/* Bluetooth HCI Management interface */
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
|
|
@ -2154,8 +2154,6 @@ static int __init rfcomm_init(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
l2cap_load();
|
||||
|
||||
hci_register_cb(&rfcomm_cb);
|
||||
|
||||
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
|
||||
|
|
|
@ -50,8 +50,6 @@
|
|||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/sco.h>
|
||||
|
||||
#define VERSION "0.6"
|
||||
|
||||
static int disable_esco;
|
||||
|
||||
static const struct proto_ops sco_sock_ops;
|
||||
|
@ -703,6 +701,7 @@ static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user
|
|||
break;
|
||||
}
|
||||
|
||||
memset(&cinfo, 0, sizeof(cinfo));
|
||||
cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle;
|
||||
memcpy(cinfo.dev_class, sco_pi(sk)->conn->hcon->dev_class, 3);
|
||||
|
||||
|
@ -1023,7 +1022,7 @@ static struct hci_proto sco_hci_proto = {
|
|||
.recv_scodata = sco_recv_scodata
|
||||
};
|
||||
|
||||
static int __init sco_init(void)
|
||||
int __init sco_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
@ -1051,7 +1050,6 @@ static int __init sco_init(void)
|
|||
BT_ERR("Failed to create SCO debug file");
|
||||
}
|
||||
|
||||
BT_INFO("SCO (Voice Link) ver %s", VERSION);
|
||||
BT_INFO("SCO socket layer initialized");
|
||||
|
||||
return 0;
|
||||
|
@ -1061,7 +1059,7 @@ error:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void __exit sco_exit(void)
|
||||
void __exit sco_exit(void)
|
||||
{
|
||||
debugfs_remove(sco_debugfs);
|
||||
|
||||
|
@ -1074,14 +1072,5 @@ static void __exit sco_exit(void)
|
|||
proto_unregister(&sco_proto);
|
||||
}
|
||||
|
||||
module_init(sco_init);
|
||||
module_exit(sco_exit);
|
||||
|
||||
module_param(disable_esco, bool, 0644);
|
||||
MODULE_PARM_DESC(disable_esco, "Disable eSCO connection creation");
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth SCO ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("bt-proto-2");
|
||||
|
|
Loading…
Reference in New Issue