From a9de9248064bfc8eb0a183a6a951a4e7b5ca10a4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:33:56 +0200 Subject: [PATCH 01/30] [Bluetooth] Switch from OGF+OCF to using only opcodes The Bluetooth HCI commands are divided into logical OGF groups for easier identification of their purposes. While this still makes sense for the written specification, its makes the code only more complex and harder to read. So instead of using separate OGF and OCF values to identify the commands, use a common 16-bit opcode that combines both values. As a side effect this also reduces the complexity of OGF and OCF calculations during command header parsing. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- drivers/bluetooth/hci_bcsp.c | 3 +- include/net/bluetooth/hci.h | 884 +++++++------- include/net/bluetooth/hci_core.h | 12 +- net/bluetooth/hci_conn.c | 43 +- net/bluetooth/hci_core.c | 48 +- net/bluetooth/hci_event.c | 1866 ++++++++++++++++-------------- net/bluetooth/hci_sock.c | 2 +- net/bluetooth/hci_sysfs.c | 37 + 8 files changed, 1589 insertions(+), 1306 deletions(-) diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index d66064ccb31c..696f7528f022 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -237,7 +237,8 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data, if (hciextn && chan == 5) { struct hci_command_hdr *hdr = (struct hci_command_hdr *) data; - if (hci_opcode_ogf(__le16_to_cpu(hdr->opcode)) == OGF_VENDOR_CMD) { + /* Vendor specific commands */ + if (hci_opcode_ogf(__le16_to_cpu(hdr->opcode)) == 0x3f) { u8 desc = *(data + HCI_COMMAND_HDR_SIZE); if ((desc & 0xf0) == 0xc0) { data += HCI_COMMAND_HDR_SIZE + 1; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ebfb96b41106..a8a9eb6af966 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -200,65 +200,224 @@ enum { #define HCI_LM_SECURE 0x0020 /* ----- HCI Commands ---- */ -/* OGF & OCF values */ - -/* Informational Parameters */ -#define OGF_INFO_PARAM 0x04 - -#define OCF_READ_LOCAL_VERSION 0x0001 -struct hci_rp_read_loc_version { - __u8 status; - __u8 hci_ver; - __le16 hci_rev; - __u8 lmp_ver; - __le16 manufacturer; - __le16 lmp_subver; +#define HCI_OP_INQUIRY 0x0401 +struct hci_cp_inquiry { + __u8 lap[3]; + __u8 length; + __u8 num_rsp; } __attribute__ ((packed)); -#define OCF_READ_LOCAL_FEATURES 0x0003 -struct hci_rp_read_local_features { - __u8 status; - __u8 features[8]; +#define HCI_OP_INQUIRY_CANCEL 0x0402 + +#define HCI_OP_EXIT_PERIODIC_INQ 0x0404 + +#define HCI_OP_CREATE_CONN 0x0405 +struct hci_cp_create_conn { + bdaddr_t bdaddr; + __le16 pkt_type; + __u8 pscan_rep_mode; + __u8 pscan_mode; + __le16 clock_offset; + __u8 role_switch; } __attribute__ ((packed)); -#define OCF_READ_BUFFER_SIZE 0x0005 -struct hci_rp_read_buffer_size { - __u8 status; - __le16 acl_mtu; - __u8 sco_mtu; - __le16 acl_max_pkt; - __le16 sco_max_pkt; +#define HCI_OP_DISCONNECT 0x0406 +struct hci_cp_disconnect { + __le16 handle; + __u8 reason; } __attribute__ ((packed)); -#define OCF_READ_BD_ADDR 0x0009 -struct hci_rp_read_bd_addr { - __u8 status; +#define HCI_OP_ADD_SCO 0x0407 +struct hci_cp_add_sco { + __le16 handle; + __le16 pkt_type; +} __attribute__ ((packed)); + +#define HCI_OP_CREATE_CONN_CANCEL 0x0408 +struct hci_cp_create_conn_cancel { bdaddr_t bdaddr; } __attribute__ ((packed)); -/* Host Controller and Baseband */ -#define OGF_HOST_CTL 0x03 -#define OCF_RESET 0x0003 -#define OCF_READ_AUTH_ENABLE 0x001F -#define OCF_WRITE_AUTH_ENABLE 0x0020 - #define AUTH_DISABLED 0x00 - #define AUTH_ENABLED 0x01 +#define HCI_OP_ACCEPT_CONN_REQ 0x0409 +struct hci_cp_accept_conn_req { + bdaddr_t bdaddr; + __u8 role; +} __attribute__ ((packed)); -#define OCF_READ_ENCRYPT_MODE 0x0021 -#define OCF_WRITE_ENCRYPT_MODE 0x0022 - #define ENCRYPT_DISABLED 0x00 - #define ENCRYPT_P2P 0x01 - #define ENCRYPT_BOTH 0x02 +#define HCI_OP_REJECT_CONN_REQ 0x040a +struct hci_cp_reject_conn_req { + bdaddr_t bdaddr; + __u8 reason; +} __attribute__ ((packed)); -#define OCF_WRITE_CA_TIMEOUT 0x0016 -#define OCF_WRITE_PG_TIMEOUT 0x0018 +#define HCI_OP_LINK_KEY_REPLY 0x040b +struct hci_cp_link_key_reply { + bdaddr_t bdaddr; + __u8 link_key[16]; +} __attribute__ ((packed)); -#define OCF_WRITE_SCAN_ENABLE 0x001A - #define SCAN_DISABLED 0x00 - #define SCAN_INQUIRY 0x01 - #define SCAN_PAGE 0x02 +#define HCI_OP_LINK_KEY_NEG_REPLY 0x040c +struct hci_cp_link_key_neg_reply { + bdaddr_t bdaddr; +} __attribute__ ((packed)); -#define OCF_SET_EVENT_FLT 0x0005 +#define HCI_OP_PIN_CODE_REPLY 0x040d +struct hci_cp_pin_code_reply { + bdaddr_t bdaddr; + __u8 pin_len; + __u8 pin_code[16]; +} __attribute__ ((packed)); + +#define HCI_OP_PIN_CODE_NEG_REPLY 0x040e +struct hci_cp_pin_code_neg_reply { + bdaddr_t bdaddr; +} __attribute__ ((packed)); + +#define HCI_OP_CHANGE_CONN_PTYPE 0x040f +struct hci_cp_change_conn_ptype { + __le16 handle; + __le16 pkt_type; +} __attribute__ ((packed)); + +#define HCI_OP_AUTH_REQUESTED 0x0411 +struct hci_cp_auth_requested { + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_OP_SET_CONN_ENCRYPT 0x0413 +struct hci_cp_set_conn_encrypt { + __le16 handle; + __u8 encrypt; +} __attribute__ ((packed)); + +#define HCI_OP_CHANGE_CONN_LINK_KEY 0x0415 +struct hci_cp_change_conn_link_key { + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_OP_REMOTE_NAME_REQ 0x0419 +struct hci_cp_remote_name_req { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; + __u8 pscan_mode; + __le16 clock_offset; +} __attribute__ ((packed)); + +#define HCI_OP_REMOTE_NAME_REQ_CANCEL 0x041a +struct hci_cp_remote_name_req_cancel { + bdaddr_t bdaddr; +} __attribute__ ((packed)); + +#define HCI_OP_READ_REMOTE_FEATURES 0x041b +struct hci_cp_read_remote_features { + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_OP_READ_REMOTE_EXT_FEATURES 0x041c +struct hci_cp_read_remote_ext_features { + __le16 handle; + __u8 page; +} __attribute__ ((packed)); + +#define HCI_OP_READ_REMOTE_VERSION 0x041d +struct hci_cp_read_remote_version { + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_OP_SETUP_SYNC_CONN 0x0428 +struct hci_cp_setup_sync_conn { + __le16 handle; + __le32 tx_bandwidth; + __le32 rx_bandwidth; + __le16 max_latency; + __le16 voice_setting; + __u8 retrans_effort; + __le16 pkt_type; +} __attribute__ ((packed)); + +#define HCI_OP_ACCEPT_SYNC_CONN_REQ 0x0429 +struct hci_cp_accept_sync_conn_req { + bdaddr_t bdaddr; + __le32 tx_bandwidth; + __le32 rx_bandwidth; + __le16 max_latency; + __le16 content_format; + __u8 retrans_effort; + __le16 pkt_type; +} __attribute__ ((packed)); + +#define HCI_OP_REJECT_SYNC_CONN_REQ 0x042a +struct hci_cp_reject_sync_conn_req { + bdaddr_t bdaddr; + __u8 reason; +} __attribute__ ((packed)); + +#define HCI_OP_SNIFF_MODE 0x0803 +struct hci_cp_sniff_mode { + __le16 handle; + __le16 max_interval; + __le16 min_interval; + __le16 attempt; + __le16 timeout; +} __attribute__ ((packed)); + +#define HCI_OP_EXIT_SNIFF_MODE 0x0804 +struct hci_cp_exit_sniff_mode { + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_OP_ROLE_DISCOVERY 0x0809 +struct hci_cp_role_discovery { + __le16 handle; +} __attribute__ ((packed)); +struct hci_rp_role_discovery { + __u8 status; + __le16 handle; + __u8 role; +} __attribute__ ((packed)); + +#define HCI_OP_SWITCH_ROLE 0x080b +struct hci_cp_switch_role { + bdaddr_t bdaddr; + __u8 role; +} __attribute__ ((packed)); + +#define HCI_OP_READ_LINK_POLICY 0x080c +struct hci_cp_read_link_policy { + __le16 handle; +} __attribute__ ((packed)); +struct hci_rp_read_link_policy { + __u8 status; + __le16 handle; + __le16 policy; +} __attribute__ ((packed)); + +#define HCI_OP_WRITE_LINK_POLICY 0x080d +struct hci_cp_write_link_policy { + __le16 handle; + __le16 policy; +} __attribute__ ((packed)); +struct hci_rp_write_link_policy { + __u8 status; + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_OP_SNIFF_SUBRATE 0x0811 +struct hci_cp_sniff_subrate { + __le16 handle; + __le16 max_latency; + __le16 min_remote_timeout; + __le16 min_local_timeout; +} __attribute__ ((packed)); + +#define HCI_OP_SET_EVENT_MASK 0x0c01 +struct hci_cp_set_event_mask { + __u8 mask[8]; +} __attribute__ ((packed)); + +#define HCI_OP_RESET 0x0c03 + +#define HCI_OP_SET_EVENT_FLT 0x0c05 struct hci_cp_set_event_flt { __u8 flt_type; __u8 cond_type; @@ -279,29 +438,62 @@ struct hci_cp_set_event_flt { #define HCI_CONN_SETUP_AUTO_OFF 0x01 #define HCI_CONN_SETUP_AUTO_ON 0x02 -#define OCF_READ_CLASS_OF_DEV 0x0023 -struct hci_rp_read_dev_class { +#define HCI_OP_WRITE_LOCAL_NAME 0x0c13 +struct hci_cp_write_local_name { + __u8 name[248]; +} __attribute__ ((packed)); + +#define HCI_OP_READ_LOCAL_NAME 0x0c14 +struct hci_rp_read_local_name { + __u8 status; + __u8 name[248]; +} __attribute__ ((packed)); + +#define HCI_OP_WRITE_CA_TIMEOUT 0x0c16 + +#define HCI_OP_WRITE_PG_TIMEOUT 0x0c18 + +#define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a + #define SCAN_DISABLED 0x00 + #define SCAN_INQUIRY 0x01 + #define SCAN_PAGE 0x02 + +#define HCI_OP_READ_AUTH_ENABLE 0x0c1f + +#define HCI_OP_WRITE_AUTH_ENABLE 0x0c20 + #define AUTH_DISABLED 0x00 + #define AUTH_ENABLED 0x01 + +#define HCI_OP_READ_ENCRYPT_MODE 0x0c21 + +#define HCI_OP_WRITE_ENCRYPT_MODE 0x0c22 + #define ENCRYPT_DISABLED 0x00 + #define ENCRYPT_P2P 0x01 + #define ENCRYPT_BOTH 0x02 + +#define HCI_OP_READ_CLASS_OF_DEV 0x0c23 +struct hci_rp_read_class_of_dev { __u8 status; __u8 dev_class[3]; } __attribute__ ((packed)); -#define OCF_WRITE_CLASS_OF_DEV 0x0024 -struct hci_cp_write_dev_class { +#define HCI_OP_WRITE_CLASS_OF_DEV 0x0c24 +struct hci_cp_write_class_of_dev { __u8 dev_class[3]; } __attribute__ ((packed)); -#define OCF_READ_VOICE_SETTING 0x0025 +#define HCI_OP_READ_VOICE_SETTING 0x0c25 struct hci_rp_read_voice_setting { __u8 status; __le16 voice_setting; } __attribute__ ((packed)); -#define OCF_WRITE_VOICE_SETTING 0x0026 +#define HCI_OP_WRITE_VOICE_SETTING 0x0c26 struct hci_cp_write_voice_setting { __le16 voice_setting; } __attribute__ ((packed)); -#define OCF_HOST_BUFFER_SIZE 0x0033 +#define HCI_OP_HOST_BUFFER_SIZE 0x0c33 struct hci_cp_host_buffer_size { __le16 acl_mtu; __u8 sco_mtu; @@ -309,188 +501,55 @@ struct hci_cp_host_buffer_size { __le16 sco_max_pkt; } __attribute__ ((packed)); -/* Link Control */ -#define OGF_LINK_CTL 0x01 - -#define OCF_CREATE_CONN 0x0005 -struct hci_cp_create_conn { - bdaddr_t bdaddr; - __le16 pkt_type; - __u8 pscan_rep_mode; - __u8 pscan_mode; - __le16 clock_offset; - __u8 role_switch; -} __attribute__ ((packed)); - -#define OCF_CREATE_CONN_CANCEL 0x0008 -struct hci_cp_create_conn_cancel { - bdaddr_t bdaddr; -} __attribute__ ((packed)); - -#define OCF_ACCEPT_CONN_REQ 0x0009 -struct hci_cp_accept_conn_req { - bdaddr_t bdaddr; - __u8 role; -} __attribute__ ((packed)); - -#define OCF_REJECT_CONN_REQ 0x000a -struct hci_cp_reject_conn_req { - bdaddr_t bdaddr; - __u8 reason; -} __attribute__ ((packed)); - -#define OCF_DISCONNECT 0x0006 -struct hci_cp_disconnect { - __le16 handle; - __u8 reason; -} __attribute__ ((packed)); - -#define OCF_ADD_SCO 0x0007 -struct hci_cp_add_sco { - __le16 handle; - __le16 pkt_type; -} __attribute__ ((packed)); - -#define OCF_INQUIRY 0x0001 -struct hci_cp_inquiry { - __u8 lap[3]; - __u8 length; - __u8 num_rsp; -} __attribute__ ((packed)); - -#define OCF_INQUIRY_CANCEL 0x0002 - -#define OCF_EXIT_PERIODIC_INQ 0x0004 - -#define OCF_LINK_KEY_REPLY 0x000B -struct hci_cp_link_key_reply { - bdaddr_t bdaddr; - __u8 link_key[16]; -} __attribute__ ((packed)); - -#define OCF_LINK_KEY_NEG_REPLY 0x000C -struct hci_cp_link_key_neg_reply { - bdaddr_t bdaddr; -} __attribute__ ((packed)); - -#define OCF_PIN_CODE_REPLY 0x000D -struct hci_cp_pin_code_reply { - bdaddr_t bdaddr; - __u8 pin_len; - __u8 pin_code[16]; -} __attribute__ ((packed)); - -#define OCF_PIN_CODE_NEG_REPLY 0x000E -struct hci_cp_pin_code_neg_reply { - bdaddr_t bdaddr; -} __attribute__ ((packed)); - -#define OCF_CHANGE_CONN_PTYPE 0x000F -struct hci_cp_change_conn_ptype { - __le16 handle; - __le16 pkt_type; -} __attribute__ ((packed)); - -#define OCF_AUTH_REQUESTED 0x0011 -struct hci_cp_auth_requested { - __le16 handle; -} __attribute__ ((packed)); - -#define OCF_SET_CONN_ENCRYPT 0x0013 -struct hci_cp_set_conn_encrypt { - __le16 handle; - __u8 encrypt; -} __attribute__ ((packed)); - -#define OCF_CHANGE_CONN_LINK_KEY 0x0015 -struct hci_cp_change_conn_link_key { - __le16 handle; -} __attribute__ ((packed)); - -#define OCF_READ_REMOTE_FEATURES 0x001B -struct hci_cp_read_remote_features { - __le16 handle; -} __attribute__ ((packed)); - -#define OCF_READ_REMOTE_VERSION 0x001D -struct hci_cp_read_remote_version { - __le16 handle; -} __attribute__ ((packed)); - -/* Link Policy */ -#define OGF_LINK_POLICY 0x02 - -#define OCF_SNIFF_MODE 0x0003 -struct hci_cp_sniff_mode { - __le16 handle; - __le16 max_interval; - __le16 min_interval; - __le16 attempt; - __le16 timeout; -} __attribute__ ((packed)); - -#define OCF_EXIT_SNIFF_MODE 0x0004 -struct hci_cp_exit_sniff_mode { - __le16 handle; -} __attribute__ ((packed)); - -#define OCF_ROLE_DISCOVERY 0x0009 -struct hci_cp_role_discovery { - __le16 handle; -} __attribute__ ((packed)); -struct hci_rp_role_discovery { +#define HCI_OP_READ_LOCAL_VERSION 0x1001 +struct hci_rp_read_local_version { __u8 status; - __le16 handle; - __u8 role; + __u8 hci_ver; + __le16 hci_rev; + __u8 lmp_ver; + __le16 manufacturer; + __le16 lmp_subver; } __attribute__ ((packed)); -#define OCF_READ_LINK_POLICY 0x000C -struct hci_cp_read_link_policy { - __le16 handle; -} __attribute__ ((packed)); -struct hci_rp_read_link_policy { +#define HCI_OP_READ_LOCAL_COMMANDS 0x1002 +struct hci_rp_read_local_commands { __u8 status; - __le16 handle; - __le16 policy; + __u8 commands[64]; } __attribute__ ((packed)); -#define OCF_SWITCH_ROLE 0x000B -struct hci_cp_switch_role { +#define HCI_OP_READ_LOCAL_FEATURES 0x1003 +struct hci_rp_read_local_features { + __u8 status; + __u8 features[8]; +} __attribute__ ((packed)); + +#define HCI_OP_READ_LOCAL_EXT_FEATURES 0x1004 +struct hci_rp_read_local_ext_features { + __u8 status; + __u8 page; + __u8 max_page; + __u8 features[8]; +} __attribute__ ((packed)); + +#define HCI_OP_READ_BUFFER_SIZE 0x1005 +struct hci_rp_read_buffer_size { + __u8 status; + __le16 acl_mtu; + __u8 sco_mtu; + __le16 acl_max_pkt; + __le16 sco_max_pkt; +} __attribute__ ((packed)); + +#define HCI_OP_READ_BD_ADDR 0x1009 +struct hci_rp_read_bd_addr { + __u8 status; bdaddr_t bdaddr; - __u8 role; } __attribute__ ((packed)); -#define OCF_WRITE_LINK_POLICY 0x000D -struct hci_cp_write_link_policy { - __le16 handle; - __le16 policy; -} __attribute__ ((packed)); -struct hci_rp_write_link_policy { - __u8 status; - __le16 handle; -} __attribute__ ((packed)); - -#define OCF_SNIFF_SUBRATE 0x0011 -struct hci_cp_sniff_subrate { - __le16 handle; - __le16 max_latency; - __le16 min_remote_timeout; - __le16 min_local_timeout; -} __attribute__ ((packed)); - -/* Status params */ -#define OGF_STATUS_PARAM 0x05 - -/* Testing commands */ -#define OGF_TESTING_CMD 0x3E - -/* Vendor specific commands */ -#define OGF_VENDOR_CMD 0x3F - /* ---- HCI Events ---- */ -#define HCI_EV_INQUIRY_COMPLETE 0x01 +#define HCI_EV_INQUIRY_COMPLETE 0x01 -#define HCI_EV_INQUIRY_RESULT 0x02 +#define HCI_EV_INQUIRY_RESULT 0x02 struct inquiry_info { bdaddr_t bdaddr; __u8 pscan_rep_mode; @@ -500,6 +559,149 @@ struct inquiry_info { __le16 clock_offset; } __attribute__ ((packed)); +#define HCI_EV_CONN_COMPLETE 0x03 +struct hci_ev_conn_complete { + __u8 status; + __le16 handle; + bdaddr_t bdaddr; + __u8 link_type; + __u8 encr_mode; +} __attribute__ ((packed)); + +#define HCI_EV_CONN_REQUEST 0x04 +struct hci_ev_conn_request { + bdaddr_t bdaddr; + __u8 dev_class[3]; + __u8 link_type; +} __attribute__ ((packed)); + +#define HCI_EV_DISCONN_COMPLETE 0x05 +struct hci_ev_disconn_complete { + __u8 status; + __le16 handle; + __u8 reason; +} __attribute__ ((packed)); + +#define HCI_EV_AUTH_COMPLETE 0x06 +struct hci_ev_auth_complete { + __u8 status; + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_EV_REMOTE_NAME 0x07 +struct hci_ev_remote_name { + __u8 status; + bdaddr_t bdaddr; + __u8 name[248]; +} __attribute__ ((packed)); + +#define HCI_EV_ENCRYPT_CHANGE 0x08 +struct hci_ev_encrypt_change { + __u8 status; + __le16 handle; + __u8 encrypt; +} __attribute__ ((packed)); + +#define HCI_EV_CHANGE_LINK_KEY_COMPLETE 0x09 +struct hci_ev_change_link_key_complete { + __u8 status; + __le16 handle; +} __attribute__ ((packed)); + +#define HCI_EV_REMOTE_FEATURES 0x0b +struct hci_ev_remote_features { + __u8 status; + __le16 handle; + __u8 features[8]; +} __attribute__ ((packed)); + +#define HCI_EV_REMOTE_VERSION 0x0c +struct hci_ev_remote_version { + __u8 status; + __le16 handle; + __u8 lmp_ver; + __le16 manufacturer; + __le16 lmp_subver; +} __attribute__ ((packed)); + +#define HCI_EV_QOS_SETUP_COMPLETE 0x0d +struct hci_qos { + __u8 service_type; + __u32 token_rate; + __u32 peak_bandwidth; + __u32 latency; + __u32 delay_variation; +} __attribute__ ((packed)); +struct hci_ev_qos_setup_complete { + __u8 status; + __le16 handle; + struct hci_qos qos; +} __attribute__ ((packed)); + +#define HCI_EV_CMD_COMPLETE 0x0e +struct hci_ev_cmd_complete { + __u8 ncmd; + __le16 opcode; +} __attribute__ ((packed)); + +#define HCI_EV_CMD_STATUS 0x0f +struct hci_ev_cmd_status { + __u8 status; + __u8 ncmd; + __le16 opcode; +} __attribute__ ((packed)); + +#define HCI_EV_ROLE_CHANGE 0x12 +struct hci_ev_role_change { + __u8 status; + bdaddr_t bdaddr; + __u8 role; +} __attribute__ ((packed)); + +#define HCI_EV_NUM_COMP_PKTS 0x13 +struct hci_ev_num_comp_pkts { + __u8 num_hndl; + /* variable length part */ +} __attribute__ ((packed)); + +#define HCI_EV_MODE_CHANGE 0x14 +struct hci_ev_mode_change { + __u8 status; + __le16 handle; + __u8 mode; + __le16 interval; +} __attribute__ ((packed)); + +#define HCI_EV_PIN_CODE_REQ 0x16 +struct hci_ev_pin_code_req { + bdaddr_t bdaddr; +} __attribute__ ((packed)); + +#define HCI_EV_LINK_KEY_REQ 0x17 +struct hci_ev_link_key_req { + bdaddr_t bdaddr; +} __attribute__ ((packed)); + +#define HCI_EV_LINK_KEY_NOTIFY 0x18 +struct hci_ev_link_key_notify { + bdaddr_t bdaddr; + __u8 link_key[16]; + __u8 key_type; +} __attribute__ ((packed)); + +#define HCI_EV_CLOCK_OFFSET 0x1c +struct hci_ev_clock_offset { + __u8 status; + __le16 handle; + __le16 clock_offset; +} __attribute__ ((packed)); + +#define HCI_EV_PSCAN_REP_MODE 0x20 +struct hci_ev_pscan_rep_mode { + bdaddr_t bdaddr; + __u8 pscan_rep_mode; +} __attribute__ ((packed)); + #define HCI_EV_INQUIRY_RESULT_WITH_RSSI 0x22 struct inquiry_info_with_rssi { bdaddr_t bdaddr; @@ -519,7 +721,49 @@ struct inquiry_info_with_rssi_and_pscan_mode { __s8 rssi; } __attribute__ ((packed)); -#define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2F +#define HCI_EV_REMOTE_EXT_FEATURES 0x23 +struct hci_ev_remote_ext_features { + __u8 status; + __le16 handle; + __u8 page; + __u8 max_page; + __u8 features[8]; +} __attribute__ ((packed)); + +#define HCI_EV_SYNC_CONN_COMPLETE 0x2c +struct hci_ev_sync_conn_complete { + __u8 status; + __le16 handle; + bdaddr_t bdaddr; + __u8 link_type; + __u8 tx_interval; + __u8 retrans_window; + __le16 rx_pkt_len; + __le16 tx_pkt_len; + __u8 air_mode; +} __attribute__ ((packed)); + +#define HCI_EV_SYNC_CONN_CHANGED 0x2d +struct hci_ev_sync_conn_changed { + __u8 status; + __le16 handle; + __u8 tx_interval; + __u8 retrans_window; + __le16 rx_pkt_len; + __le16 tx_pkt_len; +} __attribute__ ((packed)); + +#define HCI_EV_SNIFF_SUBRATE 0x2e +struct hci_ev_sniff_subrate { + __u8 status; + __le16 handle; + __le16 max_tx_latency; + __le16 max_rx_latency; + __le16 max_remote_timeout; + __le16 max_local_timeout; +} __attribute__ ((packed)); + +#define HCI_EV_EXTENDED_INQUIRY_RESULT 0x2f struct extended_inquiry_info { bdaddr_t bdaddr; __u8 pscan_rep_mode; @@ -530,160 +774,14 @@ struct extended_inquiry_info { __u8 data[240]; } __attribute__ ((packed)); -#define HCI_EV_CONN_COMPLETE 0x03 -struct hci_ev_conn_complete { - __u8 status; - __le16 handle; - bdaddr_t bdaddr; - __u8 link_type; - __u8 encr_mode; -} __attribute__ ((packed)); - -#define HCI_EV_CONN_REQUEST 0x04 -struct hci_ev_conn_request { - bdaddr_t bdaddr; - __u8 dev_class[3]; - __u8 link_type; -} __attribute__ ((packed)); - -#define HCI_EV_DISCONN_COMPLETE 0x05 -struct hci_ev_disconn_complete { - __u8 status; - __le16 handle; - __u8 reason; -} __attribute__ ((packed)); - -#define HCI_EV_AUTH_COMPLETE 0x06 -struct hci_ev_auth_complete { - __u8 status; - __le16 handle; -} __attribute__ ((packed)); - -#define HCI_EV_ENCRYPT_CHANGE 0x08 -struct hci_ev_encrypt_change { - __u8 status; - __le16 handle; - __u8 encrypt; -} __attribute__ ((packed)); - -#define HCI_EV_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 -struct hci_ev_change_conn_link_key_complete { - __u8 status; - __le16 handle; -} __attribute__ ((packed)); - -#define HCI_EV_QOS_SETUP_COMPLETE 0x0D -struct hci_qos { - __u8 service_type; - __u32 token_rate; - __u32 peak_bandwidth; - __u32 latency; - __u32 delay_variation; -} __attribute__ ((packed)); -struct hci_ev_qos_setup_complete { - __u8 status; - __le16 handle; - struct hci_qos qos; -} __attribute__ ((packed)); - -#define HCI_EV_CMD_COMPLETE 0x0E -struct hci_ev_cmd_complete { - __u8 ncmd; - __le16 opcode; -} __attribute__ ((packed)); - -#define HCI_EV_CMD_STATUS 0x0F -struct hci_ev_cmd_status { - __u8 status; - __u8 ncmd; - __le16 opcode; -} __attribute__ ((packed)); - -#define HCI_EV_NUM_COMP_PKTS 0x13 -struct hci_ev_num_comp_pkts { - __u8 num_hndl; - /* variable length part */ -} __attribute__ ((packed)); - -#define HCI_EV_ROLE_CHANGE 0x12 -struct hci_ev_role_change { - __u8 status; - bdaddr_t bdaddr; - __u8 role; -} __attribute__ ((packed)); - -#define HCI_EV_MODE_CHANGE 0x14 -struct hci_ev_mode_change { - __u8 status; - __le16 handle; - __u8 mode; - __le16 interval; -} __attribute__ ((packed)); - -#define HCI_EV_PIN_CODE_REQ 0x16 -struct hci_ev_pin_code_req { - bdaddr_t bdaddr; -} __attribute__ ((packed)); - -#define HCI_EV_LINK_KEY_REQ 0x17 -struct hci_ev_link_key_req { - bdaddr_t bdaddr; -} __attribute__ ((packed)); - -#define HCI_EV_LINK_KEY_NOTIFY 0x18 -struct hci_ev_link_key_notify { - bdaddr_t bdaddr; - __u8 link_key[16]; - __u8 key_type; -} __attribute__ ((packed)); - -#define HCI_EV_REMOTE_FEATURES 0x0B -struct hci_ev_remote_features { - __u8 status; - __le16 handle; - __u8 features[8]; -} __attribute__ ((packed)); - -#define HCI_EV_REMOTE_VERSION 0x0C -struct hci_ev_remote_version { - __u8 status; - __le16 handle; - __u8 lmp_ver; - __le16 manufacturer; - __le16 lmp_subver; -} __attribute__ ((packed)); - -#define HCI_EV_CLOCK_OFFSET 0x01C -struct hci_ev_clock_offset { - __u8 status; - __le16 handle; - __le16 clock_offset; -} __attribute__ ((packed)); - -#define HCI_EV_PSCAN_REP_MODE 0x20 -struct hci_ev_pscan_rep_mode { - bdaddr_t bdaddr; - __u8 pscan_rep_mode; -} __attribute__ ((packed)); - -#define HCI_EV_SNIFF_SUBRATE 0x2E -struct hci_ev_sniff_subrate { - __u8 status; - __le16 handle; - __le16 max_tx_latency; - __le16 max_rx_latency; - __le16 max_remote_timeout; - __le16 max_local_timeout; -} __attribute__ ((packed)); - /* Internal events generated by Bluetooth stack */ -#define HCI_EV_STACK_INTERNAL 0xFD +#define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { __u16 type; __u8 data[0]; } __attribute__ ((packed)); -#define HCI_EV_SI_DEVICE 0x01 +#define HCI_EV_SI_DEVICE 0x01 struct hci_ev_si_device { __u16 event; __u16 dev_id; @@ -704,40 +802,40 @@ struct hci_ev_si_security { #define HCI_SCO_HDR_SIZE 3 struct hci_command_hdr { - __le16 opcode; /* OCF & OGF */ + __le16 opcode; /* OCF & OGF */ __u8 plen; } __attribute__ ((packed)); struct hci_event_hdr { - __u8 evt; - __u8 plen; + __u8 evt; + __u8 plen; } __attribute__ ((packed)); struct hci_acl_hdr { - __le16 handle; /* Handle & Flags(PB, BC) */ - __le16 dlen; + __le16 handle; /* Handle & Flags(PB, BC) */ + __le16 dlen; } __attribute__ ((packed)); struct hci_sco_hdr { - __le16 handle; - __u8 dlen; + __le16 handle; + __u8 dlen; } __attribute__ ((packed)); #ifdef __KERNEL__ #include <linux/skbuff.h> static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb) { - return (struct hci_event_hdr *)skb->data; + return (struct hci_event_hdr *) skb->data; } static inline struct hci_acl_hdr *hci_acl_hdr(const struct sk_buff *skb) { - return (struct hci_acl_hdr *)skb->data; + return (struct hci_acl_hdr *) skb->data; } static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) { - return (struct hci_sco_hdr *)skb->data; + return (struct hci_sco_hdr *) skb->data; } #endif @@ -771,13 +869,13 @@ struct sockaddr_hci { struct hci_filter { unsigned long type_mask; unsigned long event_mask[2]; - __le16 opcode; + __le16 opcode; }; struct hci_ufilter { - __u32 type_mask; - __u32 event_mask[2]; - __le16 opcode; + __u32 type_mask; + __u32 event_mask[2]; + __le16 opcode; }; #define HCI_FLT_TYPE_BITS 31 @@ -825,15 +923,15 @@ struct hci_dev_info { struct hci_conn_info { __u16 handle; bdaddr_t bdaddr; - __u8 type; - __u8 out; - __u16 state; - __u32 link_mode; + __u8 type; + __u8 out; + __u16 state; + __u32 link_mode; }; struct hci_dev_req { - __u16 dev_id; - __u32 dev_opt; + __u16 dev_id; + __u32 dev_opt; }; struct hci_dev_list_req { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8f67c8a7169b..0db89ed6b00c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -71,7 +71,10 @@ struct hci_dev { __u16 id; __u8 type; bdaddr_t bdaddr; + __u8 dev_name[248]; + __u8 dev_class[3]; __u8 features[8]; + __u8 commands[64]; __u8 hci_ver; __u16 hci_rev; __u16 manufacturer; @@ -312,8 +315,9 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_add_sco(struct hci_conn *conn, __u16 handle); struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); -int hci_conn_del(struct hci_conn *conn); -void hci_conn_hash_flush(struct hci_dev *hdev); +int hci_conn_del(struct hci_conn *conn); +void hci_conn_hash_flush(struct hci_dev *hdev); +void hci_conn_check_pending(struct hci_dev *hdev); struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src); int hci_conn_auth(struct hci_conn *conn); @@ -617,11 +621,11 @@ int hci_unregister_cb(struct hci_cb *hcb); int hci_register_notifier(struct notifier_block *nb); int hci_unregister_notifier(struct notifier_block *nb); -int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param); +int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); -void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf); +void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 5fdfc9a67d39..797a30bec6fd 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -82,7 +82,7 @@ void hci_acl_connect(struct hci_conn *conn) else cp.role_switch = 0x00; - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp); } static void hci_acl_connect_cancel(struct hci_conn *conn) @@ -95,8 +95,7 @@ static void hci_acl_connect_cancel(struct hci_conn *conn) return; bacpy(&cp.bdaddr, &conn->dst); - hci_send_cmd(conn->hdev, OGF_LINK_CTL, - OCF_CREATE_CONN_CANCEL, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp); } void hci_acl_disconn(struct hci_conn *conn, __u8 reason) @@ -109,8 +108,7 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason) cp.handle = cpu_to_le16(conn->handle); cp.reason = reason; - hci_send_cmd(conn->hdev, OGF_LINK_CTL, - OCF_DISCONNECT, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } void hci_add_sco(struct hci_conn *conn, __u16 handle) @@ -126,7 +124,7 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) cp.handle = cpu_to_le16(handle); cp.pkt_type = cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp); } static void hci_conn_timeout(unsigned long arg) @@ -348,7 +346,7 @@ int hci_conn_auth(struct hci_conn *conn) if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_auth_requested cp; cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); } return 0; } @@ -369,7 +367,7 @@ int hci_conn_encrypt(struct hci_conn *conn) struct hci_cp_set_conn_encrypt cp; cp.handle = cpu_to_le16(conn->handle); cp.encrypt = 1; - hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); } return 0; } @@ -383,7 +381,7 @@ int hci_conn_change_link_key(struct hci_conn *conn) if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) { struct hci_cp_change_conn_link_key cp; cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY, sizeof(cp), &cp); } return 0; } @@ -401,7 +399,7 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role) struct hci_cp_switch_role cp; bacpy(&cp.bdaddr, &conn->dst); cp.role = role; - hci_send_cmd(conn->hdev, OGF_LINK_POLICY, OCF_SWITCH_ROLE, sizeof(cp), &cp); + hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp); } return 0; } @@ -423,8 +421,7 @@ void hci_conn_enter_active_mode(struct hci_conn *conn) if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { struct hci_cp_exit_sniff_mode cp; cp.handle = cpu_to_le16(conn->handle); - hci_send_cmd(hdev, OGF_LINK_POLICY, - OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_EXIT_SNIFF_MODE, sizeof(cp), &cp); } timer: @@ -455,8 +452,7 @@ void hci_conn_enter_sniff_mode(struct hci_conn *conn) cp.max_latency = cpu_to_le16(0); cp.min_remote_timeout = cpu_to_le16(0); cp.min_local_timeout = cpu_to_le16(0); - hci_send_cmd(hdev, OGF_LINK_POLICY, - OCF_SNIFF_SUBRATE, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_SNIFF_SUBRATE, sizeof(cp), &cp); } if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { @@ -466,8 +462,7 @@ void hci_conn_enter_sniff_mode(struct hci_conn *conn) cp.min_interval = cpu_to_le16(hdev->sniff_min_interval); cp.attempt = cpu_to_le16(4); cp.timeout = cpu_to_le16(1); - hci_send_cmd(hdev, OGF_LINK_POLICY, - OCF_SNIFF_MODE, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_SNIFF_MODE, sizeof(cp), &cp); } } @@ -493,6 +488,22 @@ void hci_conn_hash_flush(struct hci_dev *hdev) } } +/* Check pending connect attempts */ +void hci_conn_check_pending(struct hci_dev *hdev) +{ + struct hci_conn *conn; + + BT_DBG("hdev %s", hdev->name); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); + if (conn) + hci_acl_connect(conn); + + hci_dev_unlock(hdev); +} + int hci_get_conn_list(void __user *arg) { struct hci_conn_list_req req, *cl; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 18e3afc964df..2894382dd26d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -176,7 +176,7 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) BT_DBG("%s %ld", hdev->name, opt); /* Reset device */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL); + hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); } static void hci_init_req(struct hci_dev *hdev, unsigned long opt) @@ -202,16 +202,16 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Reset */ if (test_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks)) - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL); + hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); /* Read Local Supported Features */ - hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_FEATURES, 0, NULL); + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); /* Read Local Version */ - hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_LOCAL_VERSION, 0, NULL); + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL); + hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); #if 0 /* Host buffer size */ @@ -221,29 +221,35 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) cp.sco_mtu = HCI_MAX_SCO_SIZE; cp.acl_max_pkt = cpu_to_le16(0xffff); cp.sco_max_pkt = cpu_to_le16(0xffff); - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp); } #endif /* Read BD Address */ - hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL); + hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); + + /* Read Class of Device */ + hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); + + /* Read Local Name */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); /* Read Voice Setting */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_READ_VOICE_SETTING, 0, NULL); + hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); /* Optional initialization */ /* Clear Event Filters */ flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &flt_type); + hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); /* Page timeout ~20 secs */ param = cpu_to_le16(0x8000); - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_PG_TIMEOUT, 2, ¶m); + hci_send_cmd(hdev, HCI_OP_WRITE_PG_TIMEOUT, 2, ¶m); /* Connection accept timeout ~20 secs */ param = cpu_to_le16(0x7d00); - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_CA_TIMEOUT, 2, ¶m); + hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); } static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) @@ -253,7 +259,7 @@ static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) BT_DBG("%s %x", hdev->name, scan); /* Inquiry and Page scans */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE, 1, &scan); + hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) @@ -263,7 +269,7 @@ static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) BT_DBG("%s %x", hdev->name, auth); /* Authentication */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE, 1, &auth); + hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); } static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) @@ -273,7 +279,7 @@ static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) BT_DBG("%s %x", hdev->name, encrypt); /* Authentication */ - hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE, 1, &encrypt); + hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); } /* Get HCI device by index. @@ -384,7 +390,7 @@ static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) memcpy(&cp.lap, &ir->lap, 3); cp.length = ir->length; cp.num_rsp = ir->num_rsp; - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); } int hci_inquiry(void __user *arg) @@ -1111,13 +1117,13 @@ static int hci_send_frame(struct sk_buff *skb) } /* Send HCI command */ -int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param) +int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) { int len = HCI_COMMAND_HDR_SIZE + plen; struct hci_command_hdr *hdr; struct sk_buff *skb; - BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen); + BT_DBG("%s opcode 0x%x plen %d", hdev->name, opcode, plen); skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) { @@ -1126,7 +1132,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *p } hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); - hdr->opcode = cpu_to_le16(hci_opcode_pack(ogf, ocf)); + hdr->opcode = cpu_to_le16(opcode); hdr->plen = plen; if (plen) @@ -1143,7 +1149,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *p } /* Get data from the previously sent command */ -void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf) +void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) { struct hci_command_hdr *hdr; @@ -1152,10 +1158,10 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf) hdr = (void *) hdev->sent_cmd->data; - if (hdr->opcode != cpu_to_le16(hci_opcode_pack(ogf, ocf))) + if (hdr->opcode != cpu_to_le16(opcode)) return NULL; - BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf); + BT_DBG("%s opcode 0x%x", hdev->name, opcode); return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 4baea1e38652..e2cfeea5ee72 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -52,213 +52,273 @@ /* Handle HCI Event packets */ -/* Command Complete OGF LINK_CTL */ -static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) +static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) { - __u8 status; - struct hci_conn *pend; + __u8 status = *((__u8 *) skb->data); - BT_DBG("%s ocf 0x%x", hdev->name, ocf); + BT_DBG("%s status 0x%x", hdev->name, status); - switch (ocf) { - case OCF_INQUIRY_CANCEL: - case OCF_EXIT_PERIODIC_INQ: - status = *((__u8 *) skb->data); + if (status) + return; - if (status) { - BT_DBG("%s Inquiry cancel error: status 0x%x", hdev->name, status); - } else { - clear_bit(HCI_INQUIRY, &hdev->flags); - hci_req_complete(hdev, status); - } + clear_bit(HCI_INQUIRY, &hdev->flags); - hci_dev_lock(hdev); + hci_req_complete(hdev, status); - pend = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); - if (pend) - hci_acl_connect(pend); - - hci_dev_unlock(hdev); - - break; - - default: - BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf); - break; - } + hci_conn_check_pending(hdev); } -/* Command Complete OGF LINK_POLICY */ -static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) +static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) { + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + if (status) + return; + + clear_bit(HCI_INQUIRY, &hdev->flags); + + hci_conn_check_pending(hdev); +} + +static void hci_cc_remote_name_req_cancel(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); +} + +static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_role_discovery *rp = (void *) skb->data; struct hci_conn *conn; - struct hci_rp_role_discovery *rd; - struct hci_rp_write_link_policy *lp; - void *sent; - BT_DBG("%s ocf 0x%x", hdev->name, ocf); + BT_DBG("%s status 0x%x", hdev->name, rp->status); - switch (ocf) { - case OCF_ROLE_DISCOVERY: - rd = (void *) skb->data; + if (rp->status) + return; - if (rd->status) - break; + hci_dev_lock(hdev); - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rd->handle)); - if (conn) { - if (rd->role) - conn->link_mode &= ~HCI_LM_MASTER; - else - conn->link_mode |= HCI_LM_MASTER; - } - - hci_dev_unlock(hdev); - break; - - case OCF_WRITE_LINK_POLICY: - sent = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY); - if (!sent) - break; - - lp = (struct hci_rp_write_link_policy *) skb->data; - - if (lp->status) - break; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(lp->handle)); - if (conn) { - __le16 policy = get_unaligned((__le16 *) (sent + 2)); - conn->link_policy = __le16_to_cpu(policy); - } - - hci_dev_unlock(hdev); - break; - - default: - BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", - hdev->name, ocf); - break; + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) { + if (rp->role) + conn->link_mode &= ~HCI_LM_MASTER; + else + conn->link_mode |= HCI_LM_MASTER; } + + hci_dev_unlock(hdev); } -/* Command Complete OGF HOST_CTL */ -static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) +static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb) { - __u8 status, param; + struct hci_rp_write_link_policy *rp = (void *) skb->data; + struct hci_conn *conn; + void *sent; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_POLICY); + if (!sent) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) { + __le16 policy = get_unaligned((__le16 *) (sent + 2)); + conn->link_policy = __le16_to_cpu(policy); + } + + hci_dev_unlock(hdev); +} + +static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); + + hci_req_complete(hdev, status); +} + +static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + void *sent; + + BT_DBG("%s status 0x%x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME); + if (!sent) + return; + + if (!status) + memcpy(hdev->dev_name, sent, 248); +} + +static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_local_name *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + memcpy(hdev->dev_name, rp->name, 248); +} + +static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + void *sent; + + BT_DBG("%s status 0x%x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_ENABLE); + if (!sent) + return; + + if (!status) { + __u8 param = *((__u8 *) sent); + + if (param == AUTH_ENABLED) + set_bit(HCI_AUTH, &hdev->flags); + else + clear_bit(HCI_AUTH, &hdev->flags); + } + + hci_req_complete(hdev, status); +} + +static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + void *sent; + + BT_DBG("%s status 0x%x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE); + if (!sent) + return; + + if (!status) { + __u8 param = *((__u8 *) sent); + + if (param) + set_bit(HCI_ENCRYPT, &hdev->flags); + else + clear_bit(HCI_ENCRYPT, &hdev->flags); + } + + hci_req_complete(hdev, status); +} + +static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + void *sent; + + BT_DBG("%s status 0x%x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE); + if (!sent) + return; + + if (!status) { + __u8 param = *((__u8 *) sent); + + clear_bit(HCI_PSCAN, &hdev->flags); + clear_bit(HCI_ISCAN, &hdev->flags); + + if (param & SCAN_INQUIRY) + set_bit(HCI_ISCAN, &hdev->flags); + + if (param & SCAN_PAGE) + set_bit(HCI_PSCAN, &hdev->flags); + } + + hci_req_complete(hdev, status); +} + +static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_class_of_dev *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + memcpy(hdev->dev_class, rp->dev_class, 3); + + BT_DBG("%s class 0x%.2x%.2x%.2x", hdev->name, + hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); +} + +static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + void *sent; + + BT_DBG("%s status 0x%x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV); + if (!sent) + return; + + if (!status) + memcpy(hdev->dev_class, sent, 3); +} + +static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_voice_setting *rp = (void *) skb->data; __u16 setting; - struct hci_rp_read_voice_setting *vs; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + setting = __le16_to_cpu(rp->voice_setting); + + if (hdev->voice_setting == setting ) + return; + + hdev->voice_setting = setting; + + BT_DBG("%s voice setting 0x%04x", hdev->name, setting); + + if (hdev->notify) { + tasklet_disable(&hdev->tx_task); + hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); + tasklet_enable(&hdev->tx_task); + } +} + +static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); void *sent; - BT_DBG("%s ocf 0x%x", hdev->name, ocf); + BT_DBG("%s status 0x%x", hdev->name, status); - switch (ocf) { - case OCF_RESET: - status = *((__u8 *) skb->data); - hci_req_complete(hdev, status); - break; + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_VOICE_SETTING); + if (!sent) + return; - case OCF_SET_EVENT_FLT: - status = *((__u8 *) skb->data); - if (status) { - BT_DBG("%s SET_EVENT_FLT failed %d", hdev->name, status); - } else { - BT_DBG("%s SET_EVENT_FLT succeseful", hdev->name); - } - break; + if (!status) { + __u16 setting = __le16_to_cpu(get_unaligned((__le16 *) sent)); - case OCF_WRITE_AUTH_ENABLE: - sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE); - if (!sent) - break; - - status = *((__u8 *) skb->data); - param = *((__u8 *) sent); - - if (!status) { - if (param == AUTH_ENABLED) - set_bit(HCI_AUTH, &hdev->flags); - else - clear_bit(HCI_AUTH, &hdev->flags); - } - hci_req_complete(hdev, status); - break; - - case OCF_WRITE_ENCRYPT_MODE: - sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE); - if (!sent) - break; - - status = *((__u8 *) skb->data); - param = *((__u8 *) sent); - - if (!status) { - if (param) - set_bit(HCI_ENCRYPT, &hdev->flags); - else - clear_bit(HCI_ENCRYPT, &hdev->flags); - } - hci_req_complete(hdev, status); - break; - - case OCF_WRITE_CA_TIMEOUT: - status = *((__u8 *) skb->data); - if (status) { - BT_DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status); - } else { - BT_DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name); - } - break; - - case OCF_WRITE_PG_TIMEOUT: - status = *((__u8 *) skb->data); - if (status) { - BT_DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status); - } else { - BT_DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name); - } - break; - - case OCF_WRITE_SCAN_ENABLE: - sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE); - if (!sent) - break; - - status = *((__u8 *) skb->data); - param = *((__u8 *) sent); - - BT_DBG("param 0x%x", param); - - if (!status) { - clear_bit(HCI_PSCAN, &hdev->flags); - clear_bit(HCI_ISCAN, &hdev->flags); - if (param & SCAN_INQUIRY) - set_bit(HCI_ISCAN, &hdev->flags); - - if (param & SCAN_PAGE) - set_bit(HCI_PSCAN, &hdev->flags); - } - hci_req_complete(hdev, status); - break; - - case OCF_READ_VOICE_SETTING: - vs = (struct hci_rp_read_voice_setting *) skb->data; - - if (vs->status) { - BT_DBG("%s READ_VOICE_SETTING failed %d", hdev->name, vs->status); - break; - } - - setting = __le16_to_cpu(vs->voice_setting); - - if (hdev->voice_setting != setting ) { + if (hdev->voice_setting != setting) { hdev->voice_setting = setting; - BT_DBG("%s: voice setting 0x%04x", hdev->name, setting); + BT_DBG("%s voice setting 0x%04x", hdev->name, setting); if (hdev->notify) { tasklet_disable(&hdev->tx_task); @@ -266,164 +326,153 @@ static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb tasklet_enable(&hdev->tx_task); } } - break; - - case OCF_WRITE_VOICE_SETTING: - sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_VOICE_SETTING); - if (!sent) - break; - - status = *((__u8 *) skb->data); - setting = __le16_to_cpu(get_unaligned((__le16 *) sent)); - - if (!status && hdev->voice_setting != setting) { - hdev->voice_setting = setting; - - BT_DBG("%s: voice setting 0x%04x", hdev->name, setting); - - if (hdev->notify) { - tasklet_disable(&hdev->tx_task); - hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); - tasklet_enable(&hdev->tx_task); - } - } - hci_req_complete(hdev, status); - break; - - case OCF_HOST_BUFFER_SIZE: - status = *((__u8 *) skb->data); - if (status) { - BT_DBG("%s OCF_BUFFER_SIZE failed %d", hdev->name, status); - hci_req_complete(hdev, status); - } - break; - - default: - BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf); - break; } } -/* Command Complete OGF INFO_PARAM */ -static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) +static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_rp_read_loc_version *lv; - struct hci_rp_read_local_features *lf; - struct hci_rp_read_buffer_size *bs; - struct hci_rp_read_bd_addr *ba; + __u8 status = *((__u8 *) skb->data); - BT_DBG("%s ocf 0x%x", hdev->name, ocf); + BT_DBG("%s status 0x%x", hdev->name, status); - switch (ocf) { - case OCF_READ_LOCAL_VERSION: - lv = (struct hci_rp_read_loc_version *) skb->data; - - if (lv->status) { - BT_DBG("%s READ_LOCAL_VERSION failed %d", hdev->name, lf->status); - break; - } - - hdev->hci_ver = lv->hci_ver; - hdev->hci_rev = btohs(lv->hci_rev); - hdev->manufacturer = btohs(lv->manufacturer); - - BT_DBG("%s: manufacturer %d hci_ver %d hci_rev %d", hdev->name, - hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); - - break; - - case OCF_READ_LOCAL_FEATURES: - lf = (struct hci_rp_read_local_features *) skb->data; - - if (lf->status) { - BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status); - break; - } - - memcpy(hdev->features, lf->features, sizeof(hdev->features)); - - /* Adjust default settings according to features - * supported by device. */ - if (hdev->features[0] & LMP_3SLOT) - hdev->pkt_type |= (HCI_DM3 | HCI_DH3); - - if (hdev->features[0] & LMP_5SLOT) - hdev->pkt_type |= (HCI_DM5 | HCI_DH5); - - if (hdev->features[1] & LMP_HV2) { - hdev->pkt_type |= (HCI_HV2); - hdev->esco_type |= (ESCO_HV2); - } - - if (hdev->features[1] & LMP_HV3) { - hdev->pkt_type |= (HCI_HV3); - hdev->esco_type |= (ESCO_HV3); - } - - if (hdev->features[3] & LMP_ESCO) - hdev->esco_type |= (ESCO_EV3); - - if (hdev->features[4] & LMP_EV4) - hdev->esco_type |= (ESCO_EV4); - - if (hdev->features[4] & LMP_EV5) - hdev->esco_type |= (ESCO_EV5); - - BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, - lf->features[0], lf->features[1], lf->features[2]); - - break; - - case OCF_READ_BUFFER_SIZE: - bs = (struct hci_rp_read_buffer_size *) skb->data; - - if (bs->status) { - BT_DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status); - hci_req_complete(hdev, bs->status); - break; - } - - hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu); - hdev->sco_mtu = bs->sco_mtu; - hdev->acl_pkts = __le16_to_cpu(bs->acl_max_pkt); - hdev->sco_pkts = __le16_to_cpu(bs->sco_max_pkt); - - if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) { - hdev->sco_mtu = 64; - hdev->sco_pkts = 8; - } - - hdev->acl_cnt = hdev->acl_pkts; - hdev->sco_cnt = hdev->sco_pkts; - - BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name, - hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts); - break; - - case OCF_READ_BD_ADDR: - ba = (struct hci_rp_read_bd_addr *) skb->data; - - if (!ba->status) { - bacpy(&hdev->bdaddr, &ba->bdaddr); - } else { - BT_DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status); - } - - hci_req_complete(hdev, ba->status); - break; - - default: - BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf); - break; - } + hci_req_complete(hdev, status); +} + +static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_local_version *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->hci_ver = rp->hci_ver; + hdev->hci_rev = btohs(rp->hci_rev); + hdev->manufacturer = btohs(rp->manufacturer); + + BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name, + hdev->manufacturer, + hdev->hci_ver, hdev->hci_rev); +} + +static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_local_commands *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); +} + +static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_local_features *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + memcpy(hdev->features, rp->features, 8); + + /* Adjust default settings according to features + * supported by device. */ + + if (hdev->features[0] & LMP_3SLOT) + hdev->pkt_type |= (HCI_DM3 | HCI_DH3); + + if (hdev->features[0] & LMP_5SLOT) + hdev->pkt_type |= (HCI_DM5 | HCI_DH5); + + if (hdev->features[1] & LMP_HV2) { + hdev->pkt_type |= (HCI_HV2); + hdev->esco_type |= (ESCO_HV2); + } + + if (hdev->features[1] & LMP_HV3) { + hdev->pkt_type |= (HCI_HV3); + hdev->esco_type |= (ESCO_HV3); + } + + if (hdev->features[3] & LMP_ESCO) + hdev->esco_type |= (ESCO_EV3); + + if (hdev->features[4] & LMP_EV4) + hdev->esco_type |= (ESCO_EV4); + + if (hdev->features[4] & LMP_EV5) + hdev->esco_type |= (ESCO_EV5); + + BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name, + hdev->features[0], hdev->features[1], + hdev->features[2], hdev->features[3], + hdev->features[4], hdev->features[5], + hdev->features[6], hdev->features[7]); +} + +static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_buffer_size *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->acl_mtu = __le16_to_cpu(rp->acl_mtu); + hdev->sco_mtu = rp->sco_mtu; + hdev->acl_pkts = __le16_to_cpu(rp->acl_max_pkt); + hdev->sco_pkts = __le16_to_cpu(rp->sco_max_pkt); + + if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) { + hdev->sco_mtu = 64; + hdev->sco_pkts = 8; + } + + hdev->acl_cnt = hdev->acl_pkts; + hdev->sco_cnt = hdev->sco_pkts; + + BT_DBG("%s acl mtu %d:%d sco mtu %d:%d", hdev->name, + hdev->acl_mtu, hdev->acl_pkts, + hdev->sco_mtu, hdev->sco_pkts); +} + +static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_bd_addr *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (!rp->status) + bacpy(&hdev->bdaddr, &rp->bdaddr); + + hci_req_complete(hdev, rp->status); +} + +static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) +{ + BT_DBG("%s status 0x%x", hdev->name, status); + + if (status) { + hci_req_complete(hdev, status); + + hci_conn_check_pending(hdev); + } else + set_bit(HCI_INQUIRY, &hdev->flags); } -/* Command Status OGF LINK_CTL */ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) { + struct hci_cp_create_conn *cp; struct hci_conn *conn; - struct hci_cp_create_conn *cp = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN); + BT_DBG("%s status 0x%x", hdev->name, status); + + cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_CONN); if (!cp) return; @@ -431,8 +480,7 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - BT_DBG("%s status 0x%x bdaddr %s conn %p", hdev->name, - status, batostr(&cp->bdaddr), conn); + BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->bdaddr), conn); if (status) { if (conn && conn->state == BT_CONNECT) { @@ -457,157 +505,111 @@ static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) hci_dev_unlock(hdev); } -static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) +static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) { - BT_DBG("%s ocf 0x%x", hdev->name, ocf); + struct hci_cp_add_sco *cp; + struct hci_conn *acl, *sco; + __u16 handle; - switch (ocf) { - case OCF_CREATE_CONN: - hci_cs_create_conn(hdev, status); - break; + if (!status) + return; - case OCF_ADD_SCO: - if (status) { - struct hci_conn *acl, *sco; - struct hci_cp_add_sco *cp = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_ADD_SCO); - __u16 handle; + BT_DBG("%s status 0x%x", hdev->name, status); - if (!cp) - break; + cp = hci_sent_cmd_data(hdev, HCI_OP_ADD_SCO); + if (!cp) + return; - handle = __le16_to_cpu(cp->handle); + handle = __le16_to_cpu(cp->handle); - BT_DBG("%s Add SCO error: handle %d status 0x%x", hdev->name, handle, status); - - hci_dev_lock(hdev); - - acl = hci_conn_hash_lookup_handle(hdev, handle); - if (acl && (sco = acl->link)) { - sco->state = BT_CLOSED; - - hci_proto_connect_cfm(sco, status); - hci_conn_del(sco); - } - - hci_dev_unlock(hdev); - } - break; - - case OCF_INQUIRY: - if (status) { - BT_DBG("%s Inquiry error: status 0x%x", hdev->name, status); - hci_req_complete(hdev, status); - } else { - set_bit(HCI_INQUIRY, &hdev->flags); - } - break; - - default: - BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d", - hdev->name, ocf, status); - break; - } -} - -/* Command Status OGF LINK_POLICY */ -static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status) -{ - BT_DBG("%s ocf 0x%x", hdev->name, ocf); - - switch (ocf) { - case OCF_SNIFF_MODE: - if (status) { - struct hci_conn *conn; - struct hci_cp_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_SNIFF_MODE); - - if (!cp) - break; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); - } - - hci_dev_unlock(hdev); - } - break; - - case OCF_EXIT_SNIFF_MODE: - if (status) { - struct hci_conn *conn; - struct hci_cp_exit_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_EXIT_SNIFF_MODE); - - if (!cp) - break; - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); - if (conn) { - clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); - } - - hci_dev_unlock(hdev); - } - break; - - default: - BT_DBG("%s Command status: ogf LINK_POLICY ocf %x", hdev->name, ocf); - break; - } -} - -/* Command Status OGF HOST_CTL */ -static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status) -{ - BT_DBG("%s ocf 0x%x", hdev->name, ocf); - - switch (ocf) { - default: - BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf); - break; - } -} - -/* Command Status OGF INFO_PARAM */ -static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status) -{ - BT_DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf); - - switch (ocf) { - default: - BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf); - break; - } -} - -/* Inquiry Complete */ -static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - struct hci_conn *pend; - - BT_DBG("%s status %d", hdev->name, status); - - clear_bit(HCI_INQUIRY, &hdev->flags); - hci_req_complete(hdev, status); + BT_DBG("%s handle %d", hdev->name, handle); hci_dev_lock(hdev); - pend = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); - if (pend) - hci_acl_connect(pend); + acl = hci_conn_hash_lookup_handle(hdev, handle); + if (acl && (sco = acl->link)) { + sco->state = BT_CLOSED; + + hci_proto_connect_cfm(sco, status); + hci_conn_del(sco); + } hci_dev_unlock(hdev); } -/* Inquiry Result */ +static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) +{ + BT_DBG("%s status 0x%x", hdev->name, status); +} + +static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) +{ + BT_DBG("%s status 0x%x", hdev->name, status); +} + +static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) +{ + struct hci_cp_sniff_mode *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%x", hdev->name, status); + + if (!status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_SNIFF_MODE); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) + clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + + hci_dev_unlock(hdev); +} + +static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) +{ + struct hci_cp_exit_sniff_mode *cp; + struct hci_conn *conn; + + BT_DBG("%s status 0x%x", hdev->name, status); + + if (!status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_EXIT_SNIFF_MODE); + if (!cp) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) + clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + + hci_dev_unlock(hdev); +} + +static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status %d", hdev->name, status); + + clear_bit(HCI_INQUIRY, &hdev->flags); + + hci_req_complete(hdev, status); + + hci_conn_check_pending(hdev); +} + static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct inquiry_data data; - struct inquiry_info *info = (struct inquiry_info *) (skb->data + 1); + struct inquiry_info *info = (void *) (skb->data + 1); int num_rsp = *((__u8 *) skb->data); BT_DBG("%s num_rsp %d", hdev->name, num_rsp); @@ -632,91 +634,90 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_unlock(hdev); } -/* Inquiry Result With RSSI */ -static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb) +static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct inquiry_data data; - int num_rsp = *((__u8 *) skb->data); + struct hci_ev_conn_complete *ev = (void *) skb->data; + struct hci_conn *conn; - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; + BT_DBG("%s", hdev->name); 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 = - (struct inquiry_info_with_rssi_and_pscan_mode *) (skb->data + 1); + conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); + if (!conn) + goto unlock; - 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 = info->pscan_mode; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - info++; - hci_inquiry_cache_update(hdev, &data); + if (!ev->status) { + conn->handle = __le16_to_cpu(ev->handle); + conn->state = BT_CONNECTED; + + if (test_bit(HCI_AUTH, &hdev->flags)) + conn->link_mode |= HCI_LM_AUTH; + + if (test_bit(HCI_ENCRYPT, &hdev->flags)) + conn->link_mode |= HCI_LM_ENCRYPT; + + /* Get remote features */ + if (conn->type == ACL_LINK) { + struct hci_cp_read_remote_features cp; + cp.handle = ev->handle; + hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES, sizeof(cp), &cp); } - } else { - struct inquiry_info_with_rssi *info = - (struct inquiry_info_with_rssi *) (skb->data + 1); - 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; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - info++; - hci_inquiry_cache_update(hdev, &data); + /* Set link policy */ + if (conn->type == ACL_LINK && hdev->link_policy) { + struct hci_cp_write_link_policy cp; + cp.handle = ev->handle; + cp.policy = cpu_to_le16(hdev->link_policy); + hci_send_cmd(hdev, HCI_OP_WRITE_LINK_POLICY, sizeof(cp), &cp); + } + + /* Set packet type for incoming connection */ + if (!conn->out) { + struct hci_cp_change_conn_ptype cp; + cp.handle = ev->handle; + cp.pkt_type = (conn->type == ACL_LINK) ? + cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): + cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); + + hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE, sizeof(cp), &cp); + } else { + /* Update disconnect timer */ + hci_conn_hold(conn); + hci_conn_put(conn); + } + } else + conn->state = BT_CLOSED; + + if (conn->type == ACL_LINK) { + struct hci_conn *sco = conn->link; + if (sco) { + if (!ev->status) + hci_add_sco(sco, conn->handle); + else { + hci_proto_connect_cfm(sco, ev->status); + hci_conn_del(sco); + } } } + hci_proto_connect_cfm(conn, ev->status); + if (ev->status) + hci_conn_del(conn); + +unlock: hci_dev_unlock(hdev); + + hci_conn_check_pending(hdev); } -/* Extended Inquiry Result */ -static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct inquiry_data data; - struct extended_inquiry_info *info = (struct extended_inquiry_info *) (skb->data + 1); - int num_rsp = *((__u8 *) skb->data); - - BT_DBG("%s num_rsp %d", hdev->name, num_rsp); - - if (!num_rsp) - return; - - hci_dev_lock(hdev); - - for (; num_rsp; num_rsp--) { - bacpy(&data.bdaddr, &info->bdaddr); - data.pscan_rep_mode = info->pscan_rep_mode; - data.pscan_period_mode = info->pscan_period_mode; - data.pscan_mode = 0x00; - memcpy(data.dev_class, info->dev_class, 3); - data.clock_offset = info->clock_offset; - data.rssi = info->rssi; - info++; - hci_inquiry_cache_update(hdev, &data); - } - - hci_dev_unlock(hdev); -} - -/* Connect Request */ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_conn_request *ev = (struct hci_ev_conn_request *) skb->data; + struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; - BT_DBG("%s Connection request: %s type 0x%x", hdev->name, - batostr(&ev->bdaddr), ev->link_type); + BT_DBG("%s bdaddr %s type 0x%x", hdev->name, + batostr(&ev->bdaddr), ev->link_type); mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); @@ -745,107 +746,20 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk else cp.role = 0x01; /* Remain slave */ - hci_send_cmd(hdev, OGF_LINK_CTL, - OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); } else { /* Connection rejected */ struct hci_cp_reject_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.reason = 0x0f; - hci_send_cmd(hdev, OGF_LINK_CTL, - OCF_REJECT_CONN_REQ, sizeof(cp), &cp); + hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); } } -/* Connect Complete */ -static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_conn_complete *ev = (struct hci_ev_conn_complete *) skb->data; - struct hci_conn *conn, *pend; - - BT_DBG("%s", hdev->name); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - hci_dev_unlock(hdev); - return; - } - - if (!ev->status) { - conn->handle = __le16_to_cpu(ev->handle); - conn->state = BT_CONNECTED; - - if (test_bit(HCI_AUTH, &hdev->flags)) - conn->link_mode |= HCI_LM_AUTH; - - if (test_bit(HCI_ENCRYPT, &hdev->flags)) - conn->link_mode |= HCI_LM_ENCRYPT; - - /* Get remote features */ - if (conn->type == ACL_LINK) { - struct hci_cp_read_remote_features cp; - cp.handle = ev->handle; - hci_send_cmd(hdev, OGF_LINK_CTL, - OCF_READ_REMOTE_FEATURES, sizeof(cp), &cp); - } - - /* Set link policy */ - if (conn->type == ACL_LINK && hdev->link_policy) { - struct hci_cp_write_link_policy cp; - cp.handle = ev->handle; - cp.policy = cpu_to_le16(hdev->link_policy); - hci_send_cmd(hdev, OGF_LINK_POLICY, - OCF_WRITE_LINK_POLICY, sizeof(cp), &cp); - } - - /* Set packet type for incoming connection */ - if (!conn->out) { - struct hci_cp_change_conn_ptype cp; - cp.handle = ev->handle; - cp.pkt_type = (conn->type == ACL_LINK) ? - cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): - cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); - - hci_send_cmd(hdev, OGF_LINK_CTL, - OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp); - } else { - /* Update disconnect timer */ - hci_conn_hold(conn); - hci_conn_put(conn); - } - } else - conn->state = BT_CLOSED; - - if (conn->type == ACL_LINK) { - struct hci_conn *sco = conn->link; - if (sco) { - if (!ev->status) - hci_add_sco(sco, conn->handle); - else { - hci_proto_connect_cfm(sco, ev->status); - hci_conn_del(sco); - } - } - } - - hci_proto_connect_cfm(conn, ev->status); - if (ev->status) - hci_conn_del(conn); - - pend = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2); - if (pend) - hci_acl_connect(pend); - - hci_dev_unlock(hdev); -} - -/* Disconnect Complete */ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_disconn_complete *ev = (struct hci_ev_disconn_complete *) skb->data; + struct hci_ev_disconn_complete *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); @@ -865,10 +779,310 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff hci_dev_unlock(hdev); } -/* Number of completed packets */ +static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_auth_complete *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + if (!ev->status) + conn->link_mode |= HCI_LM_AUTH; + + clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); + + hci_auth_cfm(conn, ev->status); + + if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { + if (!ev->status) { + struct hci_cp_set_conn_encrypt cp; + cp.handle = cpu_to_le16(conn->handle); + cp.encrypt = 1; + hci_send_cmd(conn->hdev, + HCI_OP_SET_CONN_ENCRYPT, sizeof(cp), &cp); + } else { + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); + hci_encrypt_cfm(conn, ev->status, 0x00); + } + } + } + + hci_dev_unlock(hdev); +} + +static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); + + hci_conn_check_pending(hdev); +} + +static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_encrypt_change *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + if (!ev->status) { + if (ev->encrypt) + conn->link_mode |= HCI_LM_ENCRYPT; + else + conn->link_mode &= ~HCI_LM_ENCRYPT; + } + + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); + + hci_encrypt_cfm(conn, ev->status, ev->encrypt); + } + + hci_dev_unlock(hdev); +} + +static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_change_link_key_complete *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + if (!ev->status) + conn->link_mode |= HCI_LM_SECURE; + + clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); + + hci_key_change_cfm(conn, ev->status); + } + + hci_dev_unlock(hdev); +} + +static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_remote_features *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) + memcpy(conn->features, ev->features, 8); + + hci_dev_unlock(hdev); +} + +static inline void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); +} + +static inline void hci_qos_setup_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); +} + +static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_cmd_complete *ev = (void *) skb->data; + __u16 opcode; + + skb_pull(skb, sizeof(*ev)); + + opcode = __le16_to_cpu(ev->opcode); + + switch (opcode) { + case HCI_OP_INQUIRY_CANCEL: + hci_cc_inquiry_cancel(hdev, skb); + break; + + case HCI_OP_EXIT_PERIODIC_INQ: + hci_cc_exit_periodic_inq(hdev, skb); + break; + + case HCI_OP_REMOTE_NAME_REQ_CANCEL: + hci_cc_remote_name_req_cancel(hdev, skb); + break; + + case HCI_OP_ROLE_DISCOVERY: + hci_cc_role_discovery(hdev, skb); + break; + + case HCI_OP_WRITE_LINK_POLICY: + hci_cc_write_link_policy(hdev, skb); + break; + + case HCI_OP_RESET: + hci_cc_reset(hdev, skb); + break; + + case HCI_OP_WRITE_LOCAL_NAME: + hci_cc_write_local_name(hdev, skb); + break; + + case HCI_OP_READ_LOCAL_NAME: + hci_cc_read_local_name(hdev, skb); + break; + + case HCI_OP_WRITE_AUTH_ENABLE: + hci_cc_write_auth_enable(hdev, skb); + break; + + case HCI_OP_WRITE_ENCRYPT_MODE: + hci_cc_write_encrypt_mode(hdev, skb); + break; + + case HCI_OP_WRITE_SCAN_ENABLE: + hci_cc_write_scan_enable(hdev, skb); + break; + + case HCI_OP_READ_CLASS_OF_DEV: + hci_cc_read_class_of_dev(hdev, skb); + break; + + case HCI_OP_WRITE_CLASS_OF_DEV: + hci_cc_write_class_of_dev(hdev, skb); + break; + + case HCI_OP_READ_VOICE_SETTING: + hci_cc_read_voice_setting(hdev, skb); + break; + + case HCI_OP_WRITE_VOICE_SETTING: + hci_cc_write_voice_setting(hdev, skb); + break; + + case HCI_OP_HOST_BUFFER_SIZE: + hci_cc_host_buffer_size(hdev, skb); + break; + + case HCI_OP_READ_LOCAL_VERSION: + hci_cc_read_local_version(hdev, skb); + break; + + case HCI_OP_READ_LOCAL_COMMANDS: + hci_cc_read_local_commands(hdev, skb); + break; + + case HCI_OP_READ_LOCAL_FEATURES: + hci_cc_read_local_features(hdev, skb); + break; + + case HCI_OP_READ_BUFFER_SIZE: + hci_cc_read_buffer_size(hdev, skb); + break; + + case HCI_OP_READ_BD_ADDR: + hci_cc_read_bd_addr(hdev, skb); + break; + + default: + BT_DBG("%s opcode 0x%x", hdev->name, opcode); + break; + } + + if (ev->ncmd) { + atomic_set(&hdev->cmd_cnt, 1); + if (!skb_queue_empty(&hdev->cmd_q)) + hci_sched_cmd(hdev); + } +} + +static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_cmd_status *ev = (void *) skb->data; + __u16 opcode; + + skb_pull(skb, sizeof(*ev)); + + opcode = __le16_to_cpu(ev->opcode); + + switch (opcode) { + case HCI_OP_INQUIRY: + hci_cs_inquiry(hdev, ev->status); + break; + + case HCI_OP_CREATE_CONN: + hci_cs_create_conn(hdev, ev->status); + break; + + case HCI_OP_ADD_SCO: + hci_cs_add_sco(hdev, ev->status); + break; + + case HCI_OP_REMOTE_NAME_REQ: + hci_cs_remote_name_req(hdev, ev->status); + break; + + case HCI_OP_SETUP_SYNC_CONN: + hci_cs_setup_sync_conn(hdev, ev->status); + break; + + case HCI_OP_SNIFF_MODE: + hci_cs_sniff_mode(hdev, ev->status); + break; + + case HCI_OP_EXIT_SNIFF_MODE: + hci_cs_exit_sniff_mode(hdev, ev->status); + break; + + default: + BT_DBG("%s opcode 0x%x", hdev->name, opcode); + break; + } + + if (ev->ncmd) { + atomic_set(&hdev->cmd_cnt, 1); + if (!skb_queue_empty(&hdev->cmd_q)) + hci_sched_cmd(hdev); + } +} + +static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_role_change *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (conn) { + if (!ev->status) { + if (ev->role) + conn->link_mode &= ~HCI_LM_MASTER; + else + conn->link_mode |= HCI_LM_MASTER; + } + + clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend); + + hci_role_switch_cfm(conn, ev->status, ev->role); + } + + hci_dev_unlock(hdev); +} + static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_num_comp_pkts *ev = (struct hci_ev_num_comp_pkts *) skb->data; + struct hci_ev_num_comp_pkts *ev = (void *) skb->data; __le16 *ptr; int i; @@ -903,42 +1117,15 @@ static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *s } } } + hci_sched_tx(hdev); tasklet_enable(&hdev->tx_task); } -/* Role Change */ -static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_role_change *ev = (struct hci_ev_role_change *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn) { - if (!ev->status) { - if (ev->role) - conn->link_mode &= ~HCI_LM_MASTER; - else - conn->link_mode |= HCI_LM_MASTER; - } - - clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend); - - hci_role_switch_cfm(conn, ev->status, ev->role); - } - - hci_dev_unlock(hdev); -} - -/* Mode Change */ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_mode_change *ev = (struct hci_ev_mode_change *) skb->data; + struct hci_ev_mode_change *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); @@ -961,129 +1148,24 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb hci_dev_unlock(hdev); } -/* Authentication Complete */ -static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_auth_complete *ev = (struct hci_ev_auth_complete *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) - conn->link_mode |= HCI_LM_AUTH; - - clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); - - hci_auth_cfm(conn, ev->status); - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) { - if (!ev->status) { - struct hci_cp_set_conn_encrypt cp; - cp.handle = cpu_to_le16(conn->handle); - cp.encrypt = 1; - hci_send_cmd(conn->hdev, OGF_LINK_CTL, - OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp); - } else { - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); - hci_encrypt_cfm(conn, ev->status, 0x00); - } - } - } - - hci_dev_unlock(hdev); -} - -/* Encryption Change */ -static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_encrypt_change *ev = (struct hci_ev_encrypt_change *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) { - if (ev->encrypt) - conn->link_mode |= HCI_LM_ENCRYPT; - else - conn->link_mode &= ~HCI_LM_ENCRYPT; - } - - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); - - hci_encrypt_cfm(conn, ev->status, ev->encrypt); - } - - hci_dev_unlock(hdev); -} - -/* Change Connection Link Key Complete */ -static inline void hci_change_conn_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_change_conn_link_key_complete *ev = (struct hci_ev_change_conn_link_key_complete *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn) { - if (!ev->status) - conn->link_mode |= HCI_LM_SECURE; - - clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); - - hci_key_change_cfm(conn, ev->status); - } - - hci_dev_unlock(hdev); -} - -/* Pin Code Request*/ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { + BT_DBG("%s", hdev->name); } -/* Link Key Request */ static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { + BT_DBG("%s", hdev->name); } -/* Link Key Notification */ static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) { + BT_DBG("%s", hdev->name); } -/* Remote Features */ -static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_remote_features *ev = (struct hci_ev_remote_features *) skb->data; - struct hci_conn *conn; - - BT_DBG("%s status %d", hdev->name, ev->status); - - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); - if (conn && !ev->status) { - memcpy(conn->features, ev->features, sizeof(conn->features)); - } - - hci_dev_unlock(hdev); -} - -/* Clock Offset */ static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_clock_offset *ev = (struct hci_ev_clock_offset *) skb->data; + struct hci_ev_clock_offset *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); @@ -1103,10 +1185,9 @@ static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *sk hci_dev_unlock(hdev); } -/* Page Scan Repetition Mode */ static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_pscan_rep_mode *ev = (struct hci_ev_pscan_rep_mode *) skb->data; + struct hci_ev_pscan_rep_mode *ev = (void *) skb->data; struct inquiry_entry *ie; BT_DBG("%s", hdev->name); @@ -1121,10 +1202,69 @@ static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_unlock(hdev); } -/* Sniff Subrate */ +static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct inquiry_data data; + int num_rsp = *((__u8 *) skb->data); + + BT_DBG("%s num_rsp %d", hdev->name, num_rsp); + + if (!num_rsp) + return; + + hci_dev_lock(hdev); + + if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { + struct inquiry_info_with_rssi_and_pscan_mode *info = (void *) (skb->data + 1); + + 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 = info->pscan_mode; + memcpy(data.dev_class, info->dev_class, 3); + data.clock_offset = info->clock_offset; + data.rssi = info->rssi; + info++; + hci_inquiry_cache_update(hdev, &data); + } + } else { + struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); + + 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; + memcpy(data.dev_class, info->dev_class, 3); + data.clock_offset = info->clock_offset; + data.rssi = info->rssi; + info++; + hci_inquiry_cache_update(hdev, &data); + } + } + + hci_dev_unlock(hdev); +} + +static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); +} + +static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); +} + +static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + BT_DBG("%s", hdev->name); +} + static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_sniff_subrate *ev = (struct hci_ev_sniff_subrate *) skb->data; + struct hci_ev_sniff_subrate *ev = (void *) skb->data; struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); @@ -1138,22 +1278,42 @@ static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *s hci_dev_unlock(hdev); } +static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct inquiry_data data; + struct extended_inquiry_info *info = (void *) (skb->data + 1); + int num_rsp = *((__u8 *) skb->data); + + BT_DBG("%s num_rsp %d", hdev->name, num_rsp); + + if (!num_rsp) + return; + + hci_dev_lock(hdev); + + 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; + memcpy(data.dev_class, info->dev_class, 3); + data.clock_offset = info->clock_offset; + data.rssi = info->rssi; + info++; + hci_inquiry_cache_update(hdev, &data); + } + + hci_dev_unlock(hdev); +} + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_event_hdr *hdr = (struct hci_event_hdr *) skb->data; - struct hci_ev_cmd_complete *ec; - struct hci_ev_cmd_status *cs; - u16 opcode, ocf, ogf; + struct hci_event_hdr *hdr = (void *) skb->data; + __u8 event = hdr->evt; skb_pull(skb, HCI_EVENT_HDR_SIZE); - BT_DBG("%s evt 0x%x", hdev->name, hdr->evt); - - switch (hdr->evt) { - case HCI_EV_NUM_COMP_PKTS: - hci_num_comp_pkts_evt(hdev, skb); - break; - + switch (event) { case HCI_EV_INQUIRY_COMPLETE: hci_inquiry_complete_evt(hdev, skb); break; @@ -1162,44 +1322,64 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_inquiry_result_evt(hdev, skb); break; - case HCI_EV_INQUIRY_RESULT_WITH_RSSI: - hci_inquiry_result_with_rssi_evt(hdev, skb); - break; - - case HCI_EV_EXTENDED_INQUIRY_RESULT: - hci_extended_inquiry_result_evt(hdev, skb); + case HCI_EV_CONN_COMPLETE: + hci_conn_complete_evt(hdev, skb); break; case HCI_EV_CONN_REQUEST: hci_conn_request_evt(hdev, skb); break; - case HCI_EV_CONN_COMPLETE: - hci_conn_complete_evt(hdev, skb); - break; - case HCI_EV_DISCONN_COMPLETE: hci_disconn_complete_evt(hdev, skb); break; - case HCI_EV_ROLE_CHANGE: - hci_role_change_evt(hdev, skb); - break; - - case HCI_EV_MODE_CHANGE: - hci_mode_change_evt(hdev, skb); - break; - case HCI_EV_AUTH_COMPLETE: hci_auth_complete_evt(hdev, skb); break; + case HCI_EV_REMOTE_NAME: + hci_remote_name_evt(hdev, skb); + break; + case HCI_EV_ENCRYPT_CHANGE: hci_encrypt_change_evt(hdev, skb); break; - case HCI_EV_CHANGE_CONN_LINK_KEY_COMPLETE: - hci_change_conn_link_key_complete_evt(hdev, skb); + case HCI_EV_CHANGE_LINK_KEY_COMPLETE: + hci_change_link_key_complete_evt(hdev, skb); + break; + + case HCI_EV_REMOTE_FEATURES: + hci_remote_features_evt(hdev, skb); + break; + + case HCI_EV_REMOTE_VERSION: + hci_remote_version_evt(hdev, skb); + break; + + case HCI_EV_QOS_SETUP_COMPLETE: + hci_qos_setup_complete_evt(hdev, skb); + break; + + case HCI_EV_CMD_COMPLETE: + hci_cmd_complete_evt(hdev, skb); + break; + + case HCI_EV_CMD_STATUS: + hci_cmd_status_evt(hdev, skb); + break; + + case HCI_EV_ROLE_CHANGE: + hci_role_change_evt(hdev, skb); + break; + + case HCI_EV_NUM_COMP_PKTS: + hci_num_comp_pkts_evt(hdev, skb); + break; + + case HCI_EV_MODE_CHANGE: + hci_mode_change_evt(hdev, skb); break; case HCI_EV_PIN_CODE_REQ: @@ -1214,10 +1394,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_link_key_notify_evt(hdev, skb); break; - case HCI_EV_REMOTE_FEATURES: - hci_remote_features_evt(hdev, skb); - break; - case HCI_EV_CLOCK_OFFSET: hci_clock_offset_evt(hdev, skb); break; @@ -1226,82 +1402,32 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_pscan_rep_mode_evt(hdev, skb); break; + case HCI_EV_INQUIRY_RESULT_WITH_RSSI: + hci_inquiry_result_with_rssi_evt(hdev, skb); + break; + + case HCI_EV_REMOTE_EXT_FEATURES: + hci_remote_ext_features_evt(hdev, skb); + break; + + case HCI_EV_SYNC_CONN_COMPLETE: + hci_sync_conn_complete_evt(hdev, skb); + break; + + case HCI_EV_SYNC_CONN_CHANGED: + hci_sync_conn_changed_evt(hdev, skb); + break; + case HCI_EV_SNIFF_SUBRATE: hci_sniff_subrate_evt(hdev, skb); break; - case HCI_EV_CMD_STATUS: - cs = (struct hci_ev_cmd_status *) skb->data; - skb_pull(skb, sizeof(cs)); - - opcode = __le16_to_cpu(cs->opcode); - ogf = hci_opcode_ogf(opcode); - ocf = hci_opcode_ocf(opcode); - - switch (ogf) { - case OGF_INFO_PARAM: - hci_cs_info_param(hdev, ocf, cs->status); - break; - - case OGF_HOST_CTL: - hci_cs_host_ctl(hdev, ocf, cs->status); - break; - - case OGF_LINK_CTL: - hci_cs_link_ctl(hdev, ocf, cs->status); - break; - - case OGF_LINK_POLICY: - hci_cs_link_policy(hdev, ocf, cs->status); - break; - - default: - BT_DBG("%s Command Status OGF %x", hdev->name, ogf); - break; - } - - if (cs->ncmd) { - atomic_set(&hdev->cmd_cnt, 1); - if (!skb_queue_empty(&hdev->cmd_q)) - hci_sched_cmd(hdev); - } + case HCI_EV_EXTENDED_INQUIRY_RESULT: + hci_extended_inquiry_result_evt(hdev, skb); break; - case HCI_EV_CMD_COMPLETE: - ec = (struct hci_ev_cmd_complete *) skb->data; - skb_pull(skb, sizeof(*ec)); - - opcode = __le16_to_cpu(ec->opcode); - ogf = hci_opcode_ogf(opcode); - ocf = hci_opcode_ocf(opcode); - - switch (ogf) { - case OGF_INFO_PARAM: - hci_cc_info_param(hdev, ocf, skb); - break; - - case OGF_HOST_CTL: - hci_cc_host_ctl(hdev, ocf, skb); - break; - - case OGF_LINK_CTL: - hci_cc_link_ctl(hdev, ocf, skb); - break; - - case OGF_LINK_POLICY: - hci_cc_link_policy(hdev, ocf, skb); - break; - - default: - BT_DBG("%s Command Completed OGF %x", hdev->name, ogf); - break; - } - - if (ec->ncmd) { - atomic_set(&hdev->cmd_cnt, 1); - if (!skb_queue_empty(&hdev->cmd_q)) - hci_sched_cmd(hdev); - } + default: + BT_DBG("%s event 0x%x", hdev->name, event); break; } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 43dd6373bff9..8825102c517c 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -451,7 +451,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, goto drop; } - if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) { + if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) { skb_queue_tail(&hdev->raw_q, skb); hci_sched_tx(hdev); } else { diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 25835403d659..cef1e3e1881c 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -41,6 +41,26 @@ static ssize_t show_type(struct device *dev, struct device_attribute *attr, char return sprintf(buf, "%s\n", typetostr(hdev->type)); } +static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + char name[249]; + int i; + + for (i = 0; i < 248; i++) + name[i] = hdev->dev_name[i]; + + name[248] = '\0'; + return sprintf(buf, "%s\n", name); +} + +static ssize_t show_class(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + return sprintf(buf, "0x%.2x%.2x%.2x\n", + hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]); +} + static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = dev_get_drvdata(dev); @@ -49,6 +69,17 @@ static ssize_t show_address(struct device *dev, struct device_attribute *attr, c return sprintf(buf, "%s\n", batostr(&bdaddr)); } +static ssize_t show_features(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + + return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + hdev->features[0], hdev->features[1], + hdev->features[2], hdev->features[3], + hdev->features[4], hdev->features[5], + hdev->features[6], hdev->features[7]); +} + static ssize_t show_manufacturer(struct device *dev, struct device_attribute *attr, char *buf) { struct hci_dev *hdev = dev_get_drvdata(dev); @@ -170,7 +201,10 @@ static ssize_t store_sniff_min_interval(struct device *dev, struct device_attrib } static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static DEVICE_ATTR(class, S_IRUGO, show_class, NULL); static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); +static DEVICE_ATTR(features, S_IRUGO, show_features, NULL); static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL); static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL); static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL); @@ -185,7 +219,10 @@ static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, static struct device_attribute *bt_attrs[] = { &dev_attr_type, + &dev_attr_name, + &dev_attr_class, &dev_attr_address, + &dev_attr_features, &dev_attr_manufacturer, &dev_attr_hci_version, &dev_attr_hci_revision, From 876d9484edf77d228adb42aecd4debd58d7739d6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:35:42 +0200 Subject: [PATCH 02/30] [Bluetooth] Finish L2CAP configuration only with acceptable settings The parameters of the L2CAP output configuration might not be accepted after the first configuration round. So only indicate a finished output configuration when acceptable settings are provided. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- net/bluetooth/l2cap.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 36ef27b625db..6ce693d2e5b9 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1370,8 +1370,10 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) if (pi->conf_mtu < pi->omtu) result = L2CAP_CONF_UNACCEPT; - else + else { pi->omtu = pi->conf_mtu; + pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; + } l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); } @@ -1577,16 +1579,19 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); - /* Output config done. */ - l2cap_pi(sk)->conf_state |= L2CAP_CONF_OUTPUT_DONE; - /* Reset config buffer. */ l2cap_pi(sk)->conf_len = 0; + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE)) + goto unlock; + if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) { sk->sk_state = BT_CONNECTED; l2cap_chan_ready(sk); - } else if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) { + goto unlock; + } + + if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_REQ_SENT)) { u8 req[64]; l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req); @@ -1646,7 +1651,6 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr if (flags & 0x01) goto done; - /* Input config done */ l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE; if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) { From 861d6882b3dfe1710b35dbddf1b395b962061413 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:37:06 +0200 Subject: [PATCH 03/30] [Bluetooth] Remove global conf_mtu variable from L2CAP After the change to the L2CAP configuration parameter handling the global conf_mtu variable is no longer needed and so remove it. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- include/net/bluetooth/l2cap.h | 10 +++++----- net/bluetooth/l2cap.c | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 70e70f5d3dd6..f7bcd1f782cb 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -221,7 +221,6 @@ struct l2cap_pinfo { __u8 conf_len; __u8 conf_state; __u8 conf_retry; - __u16 conf_mtu; __u8 ident; @@ -232,10 +231,11 @@ struct l2cap_pinfo { struct sock *prev_c; }; -#define L2CAP_CONF_REQ_SENT 0x01 -#define L2CAP_CONF_INPUT_DONE 0x02 -#define L2CAP_CONF_OUTPUT_DONE 0x04 -#define L2CAP_CONF_MAX_RETRIES 2 +#define L2CAP_CONF_REQ_SENT 0x01 +#define L2CAP_CONF_INPUT_DONE 0x02 +#define L2CAP_CONF_OUTPUT_DONE 0x04 + +#define L2CAP_CONF_MAX_RETRIES 2 void l2cap_load(void); diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 6ce693d2e5b9..fde1606a4f31 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -508,7 +508,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) /* Default config options */ pi->conf_len = 0; - pi->conf_mtu = L2CAP_DEFAULT_MTU; pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; } @@ -1256,11 +1255,11 @@ static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned break; case 2: - *val = __le16_to_cpu(*((__le16 *)opt->val)); + *val = __le16_to_cpu(*((__le16 *) opt->val)); break; case 4: - *val = __le32_to_cpu(*((__le32 *)opt->val)); + *val = __le32_to_cpu(*((__le32 *) opt->val)); break; default: @@ -1332,6 +1331,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) int len = pi->conf_len; int type, hint, olen; unsigned long val; + u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; BT_DBG("sk %p", sk); @@ -1344,7 +1344,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) switch (type) { case L2CAP_CONF_MTU: - pi->conf_mtu = val; + mtu = val; break; case L2CAP_CONF_FLUSH_TO: @@ -1368,10 +1368,10 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) /* Configure output options and let the other side know * which ones we don't like. */ - if (pi->conf_mtu < pi->omtu) + if (mtu < pi->omtu) result = L2CAP_CONF_UNACCEPT; else { - pi->omtu = pi->conf_mtu; + pi->omtu = mtu; pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; } From 4e8402a3f884427f9233ba436459c158d1f2e114 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:37:56 +0200 Subject: [PATCH 04/30] [Bluetooth] Retrieve L2CAP features mask on connection setup The Bluetooth 1.2 specification introduced a specific features mask value to interoperate with newer versions of the specification. So far this piece of information was never needed, but future extensions will rely on it. This patch adds a generic way to retrieve this information only once per connection setup. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- include/net/bluetooth/l2cap.h | 14 +- net/bluetooth/l2cap.c | 233 ++++++++++++++++++++++------------ 2 files changed, 163 insertions(+), 84 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f7bcd1f782cb..e1ea64085c4f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -29,7 +29,8 @@ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_FLUSH_TO 0xFFFF -#define L2CAP_CONN_TIMEOUT (HZ * 40) +#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ +#define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ /* L2CAP socket address */ struct sockaddr_l2 { @@ -160,7 +161,6 @@ struct l2cap_disconn_rsp { struct l2cap_info_req { __le16 type; - __u8 data[0]; } __attribute__ ((packed)); struct l2cap_info_rsp { @@ -192,6 +192,13 @@ struct l2cap_conn { unsigned int mtu; + __u32 feat_mask; + + __u8 info_state; + __u8 info_ident; + + struct timer_list info_timer; + spinlock_t lock; struct sk_buff *rx_skb; @@ -202,6 +209,9 @@ struct l2cap_conn { struct l2cap_chan_list chan_list; }; +#define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 +#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02 + /* ----- L2CAP channel and socket info ----- */ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index fde1606a4f31..896ae5a9c07a 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -258,7 +258,119 @@ static void l2cap_chan_del(struct sock *sk, int err) sk->sk_state_change(sk); } +static inline u8 l2cap_get_ident(struct l2cap_conn *conn) +{ + u8 id; + + /* Get next available identificator. + * 1 - 128 are used by kernel. + * 129 - 199 are reserved. + * 200 - 254 are used by utilities like l2ping, etc. + */ + + spin_lock_bh(&conn->lock); + + if (++conn->tx_ident > 128) + conn->tx_ident = 1; + + id = conn->tx_ident; + + spin_unlock_bh(&conn->lock); + + return id; +} + +static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) +{ + struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); + + BT_DBG("code 0x%2.2x", code); + + if (!skb) + return -ENOMEM; + + return hci_send_acl(conn->hcon, skb, 0); +} + /* ---- L2CAP connections ---- */ +static void l2cap_conn_start(struct l2cap_conn *conn) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + bh_lock_sock(sk); + + if (sk->sk_type != SOCK_SEQPACKET) { + l2cap_sock_clear_timer(sk); + sk->sk_state = BT_CONNECTED; + sk->sk_state_change(sk); + } else if (sk->sk_state == BT_CONNECT) { + struct l2cap_conn_req req; + l2cap_pi(sk)->ident = l2cap_get_ident(conn); + req.scid = cpu_to_le16(l2cap_pi(sk)->scid); + req.psm = l2cap_pi(sk)->psm; + l2cap_send_cmd(conn, l2cap_pi(sk)->ident, + L2CAP_CONN_REQ, sizeof(req), &req); + } + + bh_unlock_sock(sk); + } + + read_unlock(&l->lock); +} + +static void l2cap_conn_ready(struct l2cap_conn *conn) +{ + BT_DBG("conn %p", conn); + + if (conn->chan_list.head || !hlist_empty(&l2cap_sk_list.head)) { + struct l2cap_info_req req; + + req.type = cpu_to_le16(L2CAP_IT_FEAT_MASK); + + conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT; + conn->info_ident = l2cap_get_ident(conn); + + mod_timer(&conn->info_timer, + jiffies + msecs_to_jiffies(L2CAP_INFO_TIMEOUT)); + + l2cap_send_cmd(conn, conn->info_ident, + L2CAP_INFO_REQ, sizeof(req), &req); + } +} + +/* Notify sockets that we cannot guaranty reliability anymore */ +static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) +{ + struct l2cap_chan_list *l = &conn->chan_list; + struct sock *sk; + + BT_DBG("conn %p", conn); + + read_lock(&l->lock); + + for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { + if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) + sk->sk_err = err; + } + + read_unlock(&l->lock); +} + +static void l2cap_info_timeout(unsigned long arg) +{ + struct l2cap_conn *conn = (void *) arg; + + conn->info_ident = 0; + + l2cap_conn_start(conn); +} + static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn = hcon->l2cap_data; @@ -279,6 +391,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; + conn->feat_mask = 0; + + init_timer(&conn->info_timer); + conn->info_timer.function = l2cap_info_timeout; + conn->info_timer.data = (unsigned long) conn; + spin_lock_init(&conn->lock); rwlock_init(&conn->chan_list.lock); @@ -318,40 +436,6 @@ static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, stru write_unlock_bh(&l->lock); } -static inline u8 l2cap_get_ident(struct l2cap_conn *conn) -{ - u8 id; - - /* Get next available identificator. - * 1 - 128 are used by kernel. - * 129 - 199 are reserved. - * 200 - 254 are used by utilities like l2ping, etc. - */ - - spin_lock_bh(&conn->lock); - - if (++conn->tx_ident > 128) - conn->tx_ident = 1; - - id = conn->tx_ident; - - spin_unlock_bh(&conn->lock); - - return id; -} - -static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) -{ - struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); - - BT_DBG("code 0x%2.2x", code); - - if (!skb) - return -ENOMEM; - - return hci_send_acl(conn->hcon, skb, 0); -} - /* ---- Socket interface ---- */ static struct sock *__l2cap_get_sock_by_addr(__le16 psm, bdaddr_t *src) { @@ -529,7 +613,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; + sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); sock_reset_flag(sk, SOCK_ZAPPED); @@ -649,6 +733,11 @@ static int l2cap_do_connect(struct sock *sk) l2cap_sock_set_timer(sk, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { + if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)) { + l2cap_conn_ready(conn); + goto done; + } + if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn_req req; l2cap_pi(sk)->ident = l2cap_get_ident(conn); @@ -1083,52 +1172,6 @@ static int l2cap_sock_release(struct socket *sock) return err; } -static void l2cap_conn_ready(struct l2cap_conn *conn) -{ - struct l2cap_chan_list *l = &conn->chan_list; - struct sock *sk; - - BT_DBG("conn %p", conn); - - read_lock(&l->lock); - - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - bh_lock_sock(sk); - - if (sk->sk_type != SOCK_SEQPACKET) { - l2cap_sock_clear_timer(sk); - sk->sk_state = BT_CONNECTED; - sk->sk_state_change(sk); - } else if (sk->sk_state == BT_CONNECT) { - struct l2cap_conn_req req; - l2cap_pi(sk)->ident = l2cap_get_ident(conn); - req.scid = cpu_to_le16(l2cap_pi(sk)->scid); - req.psm = l2cap_pi(sk)->psm; - l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); - } - - bh_unlock_sock(sk); - } - - read_unlock(&l->lock); -} - -/* Notify sockets that we cannot guaranty reliability anymore */ -static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) -{ - struct l2cap_chan_list *l = &conn->chan_list; - struct sock *sk; - - BT_DBG("conn %p", conn); - - read_lock(&l->lock); - for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { - if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) - sk->sk_err = err; - } - read_unlock(&l->lock); -} - static void l2cap_chan_ready(struct sock *sk) { struct sock *parent = bt_sk(sk)->parent; @@ -1399,6 +1442,23 @@ static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 fla return ptr - data; } +static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) +{ + struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data; + + if (rej->reason != 0x0000) + return 0; + + if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) && + cmd->ident == conn->info_ident) { + conn->info_ident = 0; + del_timer(&conn->info_timer); + l2cap_conn_start(conn); + } + + return 0; +} + static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_chan_list *list = &conn->chan_list; @@ -1739,6 +1799,15 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm BT_DBG("type 0x%4.4x result 0x%2.2x", type, result); + conn->info_ident = 0; + + del_timer(&conn->info_timer); + + if (type == L2CAP_IT_FEAT_MASK) + conn->feat_mask = __le32_to_cpu(get_unaligned((__le32 *) rsp->data)); + + l2cap_conn_start(conn); + return 0; } @@ -1768,7 +1837,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk switch (cmd.code) { case L2CAP_COMMAND_REJ: - /* FIXME: We should process this */ + l2cap_command_rej(conn, &cmd, data); break; case L2CAP_CONN_REQ: From f0709e03ac3552b1b048ee171cb96ecaacc6813c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:38:51 +0200 Subject: [PATCH 05/30] [Bluetooth] Advertise L2CAP features mask support Indicate the support for the L2CAP features mask value when the remote entity tries to negotiate Bluetooth 1.2 specific features. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- net/bluetooth/l2cap.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 896ae5a9c07a..733c95dbd07e 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -55,7 +55,9 @@ #define BT_DBG(D...) #endif -#define VERSION "2.8" +#define VERSION "2.9" + +static u32 l2cap_feat_mask = 0x0000; static const struct proto_ops l2cap_sock_ops; @@ -1775,16 +1777,27 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_info_req *req = (struct l2cap_info_req *) data; - struct l2cap_info_rsp rsp; u16 type; type = __le16_to_cpu(req->type); BT_DBG("type 0x%4.4x", type); - rsp.type = cpu_to_le16(type); - rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP); - l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp), &rsp); + if (type == L2CAP_IT_FEAT_MASK) { + u8 buf[8]; + struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK); + rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); + put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(buf), buf); + } else { + struct l2cap_info_rsp rsp; + rsp.type = cpu_to_le16(type); + rsp.result = cpu_to_le16(L2CAP_IR_NOTSUPP); + l2cap_send_cmd(conn, cmd->ident, + L2CAP_INFO_RSP, sizeof(rsp), &rsp); + } return 0; } From 6464f35f3771f69cd8d107fff166dc29ab392f97 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:39:51 +0200 Subject: [PATCH 06/30] [Bluetooth] Fall back to L2CAP in basic mode In case the remote entity tries to negogiate retransmission or flow control mode, reject it and fall back to basic mode. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- include/net/bluetooth/l2cap.h | 13 +++++++++++++ net/bluetooth/l2cap.c | 34 +++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e1ea64085c4f..73e115bc12dd 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -149,6 +149,19 @@ struct l2cap_conf_opt { #define L2CAP_CONF_MAX_SIZE 22 +struct l2cap_conf_rfc { + __u8 mode; + __u8 txwin_size; + __u8 max_transmit; + __le16 retrans_timeout; + __le16 monitor_timeout; + __le16 max_pdu_size; +} __attribute__ ((packed)); + +#define L2CAP_MODE_BASIC 0x00 +#define L2CAP_MODE_RETRANS 0x01 +#define L2CAP_MODE_FLOWCTL 0x02 + struct l2cap_disconn_req { __le16 dcid; __le16 scid; diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index 733c95dbd07e..6fbbae78b304 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -1048,7 +1048,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = 0x00; + opts.mode = L2CAP_MODE_BASIC; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -1097,7 +1097,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; - opts.mode = 0x00; + opts.mode = L2CAP_MODE_BASIC; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) @@ -1376,6 +1376,7 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) int len = pi->conf_len; int type, hint, olen; unsigned long val; + struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; @@ -1399,6 +1400,11 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) case L2CAP_CONF_QOS: break; + case L2CAP_CONF_RFC: + if (olen == sizeof(rfc)) + memcpy(&rfc, (void *) val, olen); + break; + default: if (hint) break; @@ -1413,14 +1419,24 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data) /* Configure output options and let the other side know * which ones we don't like. */ - if (mtu < pi->omtu) - result = L2CAP_CONF_UNACCEPT; - else { - pi->omtu = mtu; - pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; - } + if (rfc.mode == L2CAP_MODE_BASIC) { + if (mtu < pi->omtu) + result = L2CAP_CONF_UNACCEPT; + else { + pi->omtu = mtu; + pi->conf_state |= L2CAP_CONF_OUTPUT_DONE; + } - l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu); + } else { + result = L2CAP_CONF_UNACCEPT; + + memset(&rfc, 0, sizeof(rfc)); + rfc.mode = L2CAP_MODE_BASIC; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, + sizeof(rfc), (unsigned long) &rfc); + } } rsp->scid = cpu_to_le16(pi->dcid); From e24b21ec85afda6f51b6bc403e971ff2aa7eacee Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 13:41:33 +0200 Subject: [PATCH 07/30] [Bluetooth] Change BPA 100/105 driver to use USB anchors With the new support for USB anchors the driver can become more simpler and also cleaner. This patch switches to the usage of USB anchors for all URBs. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- drivers/bluetooth/bpa10x.c | 730 ++++++++++++++++--------------------- 1 file changed, 315 insertions(+), 415 deletions(-) diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index e8ebd5d3de86..1375b5345a0a 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -2,7 +2,7 @@ * * Digianswer Bluetooth USB driver * - * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org> * * * This program is free software; you can redistribute it and/or modify @@ -21,13 +21,14 @@ * */ -#include <linux/module.h> - #include <linux/kernel.h> +#include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/types.h> +#include <linux/sched.h> #include <linux/errno.h> +#include <linux/skbuff.h> #include <linux/usb.h> @@ -39,7 +40,7 @@ #define BT_DBG(D...) #endif -#define VERSION "0.8" +#define VERSION "0.9" static int ignore = 0; @@ -52,393 +53,285 @@ static struct usb_device_id bpa10x_table[] = { MODULE_DEVICE_TABLE(usb, bpa10x_table); -#define BPA10X_CMD_EP 0x00 -#define BPA10X_EVT_EP 0x81 -#define BPA10X_TX_EP 0x02 -#define BPA10X_RX_EP 0x82 - -#define BPA10X_CMD_BUF_SIZE 252 -#define BPA10X_EVT_BUF_SIZE 16 -#define BPA10X_TX_BUF_SIZE 384 -#define BPA10X_RX_BUF_SIZE 384 - struct bpa10x_data { - struct hci_dev *hdev; - struct usb_device *udev; + struct hci_dev *hdev; + struct usb_device *udev; - rwlock_t lock; + struct usb_anchor tx_anchor; + struct usb_anchor rx_anchor; - struct sk_buff_head cmd_queue; - struct urb *cmd_urb; - struct urb *evt_urb; - struct sk_buff *evt_skb; - unsigned int evt_len; - - struct sk_buff_head tx_queue; - struct urb *tx_urb; - struct urb *rx_urb; + struct sk_buff *rx_skb[2]; }; -#define HCI_VENDOR_HDR_SIZE 5 +#define HCI_VENDOR_HDR_SIZE 5 struct hci_vendor_hdr { - __u8 type; - __le16 snum; - __le16 dlen; + __u8 type; + __le16 snum; + __le16 dlen; } __attribute__ ((packed)); -static void bpa10x_recv_bulk(struct bpa10x_data *data, unsigned char *buf, int count) +static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) { - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - struct hci_vendor_hdr *vh; - struct sk_buff *skb; - int len; + struct bpa10x_data *data = hdev->driver_data; + + BT_DBG("%s queue %d buffer %p count %d", hdev->name, + queue, buf, count); + + if (queue < 0 || queue > 1) + return -EILSEQ; + + hdev->stat.byte_rx += count; while (count) { - switch (*buf++) { - case HCI_ACLDATA_PKT: - ah = (struct hci_acl_hdr *) buf; - len = HCI_ACL_HDR_SIZE + __le16_to_cpu(ah->dlen); - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_recv_frame(skb); - } - break; + struct sk_buff *skb = data->rx_skb[queue]; + struct { __u8 type; int expect; } *scb; + int type, len = 0; - case HCI_SCODATA_PKT: - sh = (struct hci_sco_hdr *) buf; - len = HCI_SCO_HDR_SIZE + sh->dlen; - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; - hci_recv_frame(skb); - } - break; - - case HCI_VENDOR_PKT: - vh = (struct hci_vendor_hdr *) buf; - len = HCI_VENDOR_HDR_SIZE + __le16_to_cpu(vh->dlen); - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (skb) { - memcpy(skb_put(skb, len), buf, len); - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; - hci_recv_frame(skb); - } - break; - - default: - len = count - 1; - break; - } - - buf += len; - count -= (len + 1); - } -} - -static int bpa10x_recv_event(struct bpa10x_data *data, unsigned char *buf, int size) -{ - BT_DBG("data %p buf %p size %d", data, buf, size); - - if (data->evt_skb) { - struct sk_buff *skb = data->evt_skb; - - memcpy(skb_put(skb, size), buf, size); - - if (skb->len == data->evt_len) { - data->evt_skb = NULL; - data->evt_len = 0; - hci_recv_frame(skb); - } - } else { - struct sk_buff *skb; - struct hci_event_hdr *hdr; - unsigned char pkt_type; - int pkt_len = 0; - - if (size < HCI_EVENT_HDR_SIZE + 1) { - BT_ERR("%s event packet block with size %d is too short", - data->hdev->name, size); - return -EILSEQ; - } - - pkt_type = *buf++; - size--; - - if (pkt_type != HCI_EVENT_PKT) { - BT_ERR("%s unexpected event packet start byte 0x%02x", - data->hdev->name, pkt_type); - return -EPROTO; - } - - hdr = (struct hci_event_hdr *) buf; - pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen; - - skb = bt_skb_alloc(pkt_len, GFP_ATOMIC); if (!skb) { - BT_ERR("%s no memory for new event packet", - data->hdev->name); - return -ENOMEM; - } + /* Start of the frame */ - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = pkt_type; + type = *((__u8 *) buf); + count--; buf++; - memcpy(skb_put(skb, size), buf, size); + switch (type) { + case HCI_EVENT_PKT: + if (count >= HCI_EVENT_HDR_SIZE) { + struct hci_event_hdr *h = buf; + len = HCI_EVENT_HDR_SIZE + h->plen; + } else + return -EILSEQ; + break; - if (pkt_len == size) { - hci_recv_frame(skb); + case HCI_ACLDATA_PKT: + if (count >= HCI_ACL_HDR_SIZE) { + struct hci_acl_hdr *h = buf; + len = HCI_ACL_HDR_SIZE + + __le16_to_cpu(h->dlen); + } else + return -EILSEQ; + break; + + case HCI_SCODATA_PKT: + if (count >= HCI_SCO_HDR_SIZE) { + struct hci_sco_hdr *h = buf; + len = HCI_SCO_HDR_SIZE + h->dlen; + } else + return -EILSEQ; + break; + + case HCI_VENDOR_PKT: + if (count >= HCI_VENDOR_HDR_SIZE) { + struct hci_vendor_hdr *h = buf; + len = HCI_VENDOR_HDR_SIZE + + __le16_to_cpu(h->dlen); + } else + return -EILSEQ; + break; + } + + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) { + BT_ERR("%s no memory for packet", hdev->name); + return -ENOMEM; + } + + skb->dev = (void *) hdev; + + data->rx_skb[queue] = skb; + + scb = (void *) skb->cb; + scb->type = type; + scb->expect = len; } else { - data->evt_skb = skb; - data->evt_len = pkt_len; + /* Continuation */ + + scb = (void *) skb->cb; + len = scb->expect; } + + len = min(len, count); + + memcpy(skb_put(skb, len), buf, len); + + scb->expect -= len; + + if (scb->expect == 0) { + /* Complete frame */ + + data->rx_skb[queue] = NULL; + + bt_cb(skb)->pkt_type = scb->type; + hci_recv_frame(skb); + } + + count -= len; buf += len; } return 0; } -static void bpa10x_wakeup(struct bpa10x_data *data) +static void bpa10x_tx_complete(struct urb *urb) { - struct urb *urb; - struct sk_buff *skb; + struct sk_buff *skb = urb->context; + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto done; + + if (!urb->status) + hdev->stat.byte_tx += urb->transfer_buffer_length; + else + hdev->stat.err_tx++; + +done: + kfree(urb->setup_packet); + + kfree_skb(skb); +} + +static void bpa10x_rx_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct bpa10x_data *data = hdev->driver_data; int err; - BT_DBG("data %p", data); + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); - urb = data->cmd_urb; - if (urb->status == -EINPROGRESS) - skb = NULL; - else - skb = skb_dequeue(&data->cmd_queue); - - if (skb) { - struct usb_ctrlrequest *cr; - - if (skb->len > BPA10X_CMD_BUF_SIZE) { - BT_ERR("%s command packet with size %d is too big", - data->hdev->name, skb->len); - kfree_skb(skb); - return; - } - - cr = (struct usb_ctrlrequest *) urb->setup_packet; - cr->wLength = __cpu_to_le16(skb->len); - - skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); - urb->transfer_buffer_length = skb->len; - - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -ENODEV) { - BT_ERR("%s submit failed for command urb %p with error %d", - data->hdev->name, urb, err); - skb_queue_head(&data->cmd_queue, skb); - } else - kfree_skb(skb); - } - - urb = data->tx_urb; - if (urb->status == -EINPROGRESS) - skb = NULL; - else - skb = skb_dequeue(&data->tx_queue); - - if (skb) { - skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); - urb->transfer_buffer_length = skb->len; - - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -ENODEV) { - BT_ERR("%s submit failed for command urb %p with error %d", - data->hdev->name, urb, err); - skb_queue_head(&data->tx_queue, skb); - } else - kfree_skb(skb); - } -} - -static void bpa10x_complete(struct urb *urb) -{ - struct bpa10x_data *data = urb->context; - unsigned char *buf = urb->transfer_buffer; - int err, count = urb->actual_length; - - BT_DBG("data %p urb %p buf %p count %d", data, urb, buf, count); - - read_lock(&data->lock); - - if (!test_bit(HCI_RUNNING, &data->hdev->flags)) - goto unlock; - - if (urb->status < 0 || !count) - goto resubmit; - - if (usb_pipein(urb->pipe)) { - data->hdev->stat.byte_rx += count; - - if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) - bpa10x_recv_event(data, buf, count); - - if (usb_pipetype(urb->pipe) == PIPE_BULK) - bpa10x_recv_bulk(data, buf, count); - } else { - data->hdev->stat.byte_tx += count; - - bpa10x_wakeup(data); - } - -resubmit: - if (usb_pipein(urb->pipe)) { - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -ENODEV) { - BT_ERR("%s urb %p type %d resubmit status %d", - data->hdev->name, urb, usb_pipetype(urb->pipe), err); - } - } - -unlock: - read_unlock(&data->lock); -} - -static inline struct urb *bpa10x_alloc_urb(struct usb_device *udev, unsigned int pipe, - size_t size, gfp_t flags, void *data) -{ - struct urb *urb; - struct usb_ctrlrequest *cr; - unsigned char *buf; - - BT_DBG("udev %p data %p", udev, data); - - urb = usb_alloc_urb(0, flags); - if (!urb) - return NULL; - - buf = kmalloc(size, flags); - if (!buf) { - usb_free_urb(urb); - return NULL; - } - - switch (usb_pipetype(pipe)) { - case PIPE_CONTROL: - cr = kmalloc(sizeof(*cr), flags); - if (!cr) { - kfree(buf); - usb_free_urb(urb); - return NULL; - } - - cr->bRequestType = USB_TYPE_VENDOR; - cr->bRequest = 0; - cr->wIndex = 0; - cr->wValue = 0; - cr->wLength = __cpu_to_le16(0); - - usb_fill_control_urb(urb, udev, pipe, (void *) cr, buf, 0, bpa10x_complete, data); - break; - - case PIPE_INTERRUPT: - usb_fill_int_urb(urb, udev, pipe, buf, size, bpa10x_complete, data, 1); - break; - - case PIPE_BULK: - usb_fill_bulk_urb(urb, udev, pipe, buf, size, bpa10x_complete, data); - break; - - default: - kfree(buf); - usb_free_urb(urb); - return NULL; - } - - return urb; -} - -static inline void bpa10x_free_urb(struct urb *urb) -{ - BT_DBG("urb %p", urb); - - if (!urb) + if (!test_bit(HCI_RUNNING, &hdev->flags)) return; - kfree(urb->setup_packet); - kfree(urb->transfer_buffer); + if (urb->status == 0) { + if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe), + urb->transfer_buffer, + urb->actual_length) < 0) { + BT_ERR("%s corrupted event packet", hdev->name); + hdev->stat.err_rx++; + } + } + + usb_anchor_urb(urb, &data->rx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } +} + +static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) +{ + struct bpa10x_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = 16; + + BT_DBG("%s", hdev->name); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvintpipe(data->udev, 0x81); + + usb_fill_int_urb(urb, data->udev, pipe, buf, size, + bpa10x_rx_complete, hdev, 1); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &data->rx_anchor); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); + } usb_free_urb(urb); + + return err; +} + +static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) +{ + struct bpa10x_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size = 64; + + BT_DBG("%s", hdev->name); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(data->udev, 0x82); + + usb_fill_bulk_urb(urb, data->udev, pipe, + buf, size, bpa10x_rx_complete, hdev); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &data->rx_anchor); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); + } + + usb_free_urb(urb); + + return err; } static int bpa10x_open(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - struct usb_device *udev = data->udev; - unsigned long flags; int err; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) return 0; - data->cmd_urb = bpa10x_alloc_urb(udev, usb_sndctrlpipe(udev, BPA10X_CMD_EP), - BPA10X_CMD_BUF_SIZE, GFP_KERNEL, data); - if (!data->cmd_urb) { - err = -ENOMEM; - goto done; - } - - data->evt_urb = bpa10x_alloc_urb(udev, usb_rcvintpipe(udev, BPA10X_EVT_EP), - BPA10X_EVT_BUF_SIZE, GFP_KERNEL, data); - if (!data->evt_urb) { - bpa10x_free_urb(data->cmd_urb); - err = -ENOMEM; - goto done; - } - - data->rx_urb = bpa10x_alloc_urb(udev, usb_rcvbulkpipe(udev, BPA10X_RX_EP), - BPA10X_RX_BUF_SIZE, GFP_KERNEL, data); - if (!data->rx_urb) { - bpa10x_free_urb(data->evt_urb); - bpa10x_free_urb(data->cmd_urb); - err = -ENOMEM; - goto done; - } - - data->tx_urb = bpa10x_alloc_urb(udev, usb_sndbulkpipe(udev, BPA10X_TX_EP), - BPA10X_TX_BUF_SIZE, GFP_KERNEL, data); - if (!data->rx_urb) { - bpa10x_free_urb(data->rx_urb); - bpa10x_free_urb(data->evt_urb); - bpa10x_free_urb(data->cmd_urb); - err = -ENOMEM; - goto done; - } - - write_lock_irqsave(&data->lock, flags); - - err = usb_submit_urb(data->evt_urb, GFP_ATOMIC); - if (err < 0) { - BT_ERR("%s submit failed for event urb %p with error %d", - data->hdev->name, data->evt_urb, err); - } else { - err = usb_submit_urb(data->rx_urb, GFP_ATOMIC); - if (err < 0) { - BT_ERR("%s submit failed for rx urb %p with error %d", - data->hdev->name, data->evt_urb, err); - usb_kill_urb(data->evt_urb); - } - } - - write_unlock_irqrestore(&data->lock, flags); - -done: + err = bpa10x_submit_intr_urb(hdev); if (err < 0) - clear_bit(HCI_RUNNING, &hdev->flags); + goto error; + + err = bpa10x_submit_bulk_urb(hdev); + if (err < 0) + goto error; + + return 0; + +error: + usb_kill_anchored_urbs(&data->rx_anchor); + + clear_bit(HCI_RUNNING, &hdev->flags); return err; } @@ -446,27 +339,13 @@ done: static int bpa10x_close(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - unsigned long flags; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) return 0; - write_lock_irqsave(&data->lock, flags); - - skb_queue_purge(&data->cmd_queue); - usb_kill_urb(data->cmd_urb); - usb_kill_urb(data->evt_urb); - usb_kill_urb(data->rx_urb); - usb_kill_urb(data->tx_urb); - - write_unlock_irqrestore(&data->lock, flags); - - bpa10x_free_urb(data->cmd_urb); - bpa10x_free_urb(data->evt_urb); - bpa10x_free_urb(data->rx_urb); - bpa10x_free_urb(data->tx_urb); + usb_kill_anchored_urbs(&data->rx_anchor); return 0; } @@ -475,9 +354,9 @@ static int bpa10x_flush(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); - skb_queue_purge(&data->cmd_queue); + usb_kill_anchored_urbs(&data->tx_anchor); return 0; } @@ -485,45 +364,78 @@ static int bpa10x_flush(struct hci_dev *hdev) static int bpa10x_send_frame(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct bpa10x_data *data; + struct bpa10x_data *data = hdev->driver_data; + struct usb_ctrlrequest *dr; + struct urb *urb; + unsigned int pipe; + int err; - BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); - - if (!hdev) { - BT_ERR("Frame for unknown HCI device"); - return -ENODEV; - } + BT_DBG("%s", hdev->name); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - data = hdev->driver_data; + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; /* Prepend skb with frame type */ - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + *skb_push(skb, 1) = bt_cb(skb)->pkt_type; switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: + dr = kmalloc(sizeof(*dr), GFP_ATOMIC); + if (!dr) { + usb_free_urb(urb); + return -ENOMEM; + } + + dr->bRequestType = USB_TYPE_VENDOR; + dr->bRequest = 0; + dr->wIndex = 0; + dr->wValue = 0; + dr->wLength = __cpu_to_le16(skb->len); + + pipe = usb_sndctrlpipe(data->udev, 0x00); + + usb_fill_control_urb(urb, data->udev, pipe, (void *) dr, + skb->data, skb->len, bpa10x_tx_complete, skb); + hdev->stat.cmd_tx++; - skb_queue_tail(&data->cmd_queue, skb); break; case HCI_ACLDATA_PKT: + pipe = usb_sndbulkpipe(data->udev, 0x02); + + usb_fill_bulk_urb(urb, data->udev, pipe, + skb->data, skb->len, bpa10x_tx_complete, skb); + hdev->stat.acl_tx++; - skb_queue_tail(&data->tx_queue, skb); break; case HCI_SCODATA_PKT: + pipe = usb_sndbulkpipe(data->udev, 0x02); + + usb_fill_bulk_urb(urb, data->udev, pipe, + skb->data, skb->len, bpa10x_tx_complete, skb); + hdev->stat.sco_tx++; - skb_queue_tail(&data->tx_queue, skb); break; - }; - read_lock(&data->lock); + default: + return -EILSEQ; + } - bpa10x_wakeup(data); + usb_anchor_urb(urb, &data->tx_anchor); - read_unlock(&data->lock); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p submission failed", hdev->name, urb); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); return 0; } @@ -532,16 +444,17 @@ static void bpa10x_destruct(struct hci_dev *hdev) { struct bpa10x_data *data = hdev->driver_data; - BT_DBG("hdev %p data %p", hdev, data); + BT_DBG("%s", hdev->name); + kfree(data->rx_skb[0]); + kfree(data->rx_skb[1]); kfree(data); } static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct usb_device *udev = interface_to_usbdev(intf); - struct hci_dev *hdev; struct bpa10x_data *data; + struct hci_dev *hdev; int err; BT_DBG("intf %p id %p", intf, id); @@ -549,48 +462,43 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * if (ignore) return -ENODEV; - if (intf->cur_altsetting->desc.bInterfaceNumber > 0) + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; data = kzalloc(sizeof(*data), GFP_KERNEL); - if (!data) { - BT_ERR("Can't allocate data structure"); + if (!data) return -ENOMEM; - } - data->udev = udev; + data->udev = interface_to_usbdev(intf); - rwlock_init(&data->lock); - - skb_queue_head_init(&data->cmd_queue); - skb_queue_head_init(&data->tx_queue); + init_usb_anchor(&data->tx_anchor); + init_usb_anchor(&data->rx_anchor); hdev = hci_alloc_dev(); if (!hdev) { - BT_ERR("Can't allocate HCI device"); kfree(data); return -ENOMEM; } - data->hdev = hdev; - hdev->type = HCI_USB; hdev->driver_data = data; + + data->hdev = hdev; + SET_HCIDEV_DEV(hdev, &intf->dev); - hdev->open = bpa10x_open; - hdev->close = bpa10x_close; - hdev->flush = bpa10x_flush; - hdev->send = bpa10x_send_frame; - hdev->destruct = bpa10x_destruct; + hdev->open = bpa10x_open; + hdev->close = bpa10x_close; + hdev->flush = bpa10x_flush; + hdev->send = bpa10x_send_frame; + hdev->destruct = bpa10x_destruct; hdev->owner = THIS_MODULE; err = hci_register_dev(hdev); if (err < 0) { - BT_ERR("Can't register HCI device"); - kfree(data); hci_free_dev(hdev); + kfree(data); return err; } @@ -602,19 +510,17 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id * static void bpa10x_disconnect(struct usb_interface *intf) { struct bpa10x_data *data = usb_get_intfdata(intf); - struct hci_dev *hdev = data->hdev; BT_DBG("intf %p", intf); - if (!hdev) + if (!data) return; usb_set_intfdata(intf, NULL); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); + hci_unregister_dev(data->hdev); - hci_free_dev(hdev); + hci_free_dev(data->hdev); } static struct usb_driver bpa10x_driver = { @@ -626,15 +532,9 @@ static struct usb_driver bpa10x_driver = { static int __init bpa10x_init(void) { - int err; - BT_INFO("Digianswer Bluetooth USB driver ver %s", VERSION); - err = usb_register(&bpa10x_driver); - if (err < 0) - BT_ERR("Failed to register USB driver"); - - return err; + return usb_register(&bpa10x_driver); } static void __exit bpa10x_exit(void) From 166d2f6a4332aad53cb0750a296f76c06102552d Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen <ohad@bencohen.org> Date: Sat, 20 Oct 2007 13:42:36 +0200 Subject: [PATCH 08/30] [Bluetooth] Add UART driver for Texas Instruments' BRF63xx chips Add support for Texas Instruments' HCI Low Level (HCILL) Bluetooth protocol, which is a power management extension to H4. The HCILL is widely used by TI's BRF63xx Bluetooth chips. Signed-off-by: Ohad Ben-Cohen <ohad@bencohen.org> Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- drivers/bluetooth/Kconfig | 11 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/hci_ldisc.c | 8 +- drivers/bluetooth/hci_ll.c | 531 ++++++++++++++++++++++++++++++++++ drivers/bluetooth/hci_uart.h | 8 +- 5 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 drivers/bluetooth/hci_ll.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index b9fbe6e7f9ae..62b89b95ace2 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -55,6 +55,17 @@ config BT_HCIUART_BCSP Say Y here to compile support for HCI BCSP protocol. +config BT_HCIUART_LL + bool "HCILL protocol support" + depends on BT_HCIUART + help + HCILL (HCI Low Level) is a serial protocol for communication + between Bluetooth device and host. This protocol is required for + serial Bluetooth devices that are based on Texas Instruments' + BRF chips. + + Say Y here to compile support for HCILL protocol. + config BT_HCIBCM203X tristate "HCI BCM203x USB driver" depends on USB diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 08c10e178e02..a543dfc2d6f5 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -16,4 +16,5 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o +hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o hci_uart-objs := $(hci_uart-y) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 6055b9c0ac0f..e68821d074b0 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -549,7 +549,10 @@ static int __init hci_uart_init(void) #ifdef CONFIG_BT_HCIUART_BCSP bcsp_init(); #endif - +#ifdef CONFIG_BT_HCIUART_LL + ll_init(); +#endif + return 0; } @@ -563,6 +566,9 @@ static void __exit hci_uart_exit(void) #ifdef CONFIG_BT_HCIUART_BCSP bcsp_deinit(); #endif +#ifdef CONFIG_BT_HCIUART_LL + ll_deinit(); +#endif /* Release tty registration of line discipline */ if ((err = tty_unregister_ldisc(N_HCI))) diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c new file mode 100644 index 000000000000..8c3e62a17b4a --- /dev/null +++ b/drivers/bluetooth/hci_ll.c @@ -0,0 +1,531 @@ +/* + * Texas Instruments' Bluetooth HCILL UART protocol + * + * HCILL (HCI Low Level) is a Texas Instruments' power management + * protocol extension to H4. + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * Written by Ohad Ben-Cohen <ohad@bencohen.org> + * + * Acknowledgements: + * This file is based on hci_h4.c, which was written + * by Maxim Krasnyansky and Marcel Holtmann. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/poll.h> + +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/ioctl.h> +#include <linux/skbuff.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#include "hci_uart.h" + +/* HCILL commands */ +#define HCILL_GO_TO_SLEEP_IND 0x30 +#define HCILL_GO_TO_SLEEP_ACK 0x31 +#define HCILL_WAKE_UP_IND 0x32 +#define HCILL_WAKE_UP_ACK 0x33 + +/* HCILL receiver States */ +#define HCILL_W4_PACKET_TYPE 0 +#define HCILL_W4_EVENT_HDR 1 +#define HCILL_W4_ACL_HDR 2 +#define HCILL_W4_SCO_HDR 3 +#define HCILL_W4_DATA 4 + +/* HCILL states */ +enum hcill_states_e { + HCILL_ASLEEP, + HCILL_ASLEEP_TO_AWAKE, + HCILL_AWAKE, + HCILL_AWAKE_TO_ASLEEP +}; + +struct hcill_cmd { + u8 cmd; +} __attribute__((packed)); + +struct ll_struct { + unsigned long rx_state; + unsigned long rx_count; + struct sk_buff *rx_skb; + struct sk_buff_head txq; + spinlock_t hcill_lock; /* HCILL state lock */ + unsigned long hcill_state; /* HCILL power state */ + struct sk_buff_head tx_wait_q; /* HCILL wait queue */ +}; + +/* + * Builds and sends an HCILL command packet. + * These are very simple packets with only 1 cmd byte + */ +static int send_hcill_cmd(u8 cmd, struct hci_uart *hu) +{ + int err = 0; + struct sk_buff *skb = NULL; + struct ll_struct *ll = hu->priv; + struct hcill_cmd *hcill_packet; + + BT_DBG("hu %p cmd 0x%x", hu, cmd); + + /* allocate packet */ + skb = bt_skb_alloc(1, GFP_ATOMIC); + if (!skb) { + BT_ERR("cannot allocate memory for HCILL packet"); + err = -ENOMEM; + goto out; + } + + /* prepare packet */ + hcill_packet = (struct hcill_cmd *) skb_put(skb, 1); + hcill_packet->cmd = cmd; + skb->dev = (void *) hu->hdev; + + /* send packet */ + skb_queue_tail(&ll->txq, skb); +out: + return err; +} + +/* Initialize protocol */ +static int ll_open(struct hci_uart *hu) +{ + struct ll_struct *ll; + + BT_DBG("hu %p", hu); + + ll = kzalloc(sizeof(*ll), GFP_ATOMIC); + if (!ll) + return -ENOMEM; + + skb_queue_head_init(&ll->txq); + skb_queue_head_init(&ll->tx_wait_q); + spin_lock_init(&ll->hcill_lock); + + ll->hcill_state = HCILL_AWAKE; + + hu->priv = ll; + + return 0; +} + +/* Flush protocol data */ +static int ll_flush(struct hci_uart *hu) +{ + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ll->tx_wait_q); + skb_queue_purge(&ll->txq); + + return 0; +} + +/* Close protocol */ +static int ll_close(struct hci_uart *hu) +{ + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + skb_queue_purge(&ll->tx_wait_q); + skb_queue_purge(&ll->txq); + + if (ll->rx_skb) + kfree_skb(ll->rx_skb); + + hu->priv = NULL; + + kfree(ll); + + return 0; +} + +/* + * internal function, which does common work of the device wake up process: + * 1. places all pending packets (waiting in tx_wait_q list) in txq list. + * 2. changes internal state to HCILL_AWAKE. + * Note: assumes that hcill_lock spinlock is taken, + * shouldn't be called otherwise! + */ +static void __ll_do_awake(struct ll_struct *ll) +{ + struct sk_buff *skb = NULL; + + while ((skb = skb_dequeue(&ll->tx_wait_q))) + skb_queue_tail(&ll->txq, skb); + + ll->hcill_state = HCILL_AWAKE; +} + +/* + * Called upon a wake-up-indication from the device + */ +static void ll_device_want_to_wakeup(struct hci_uart *hu) +{ + unsigned long flags; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + switch (ll->hcill_state) { + case HCILL_ASLEEP: + /* acknowledge device wake up */ + if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) { + BT_ERR("cannot acknowledge device wake up"); + goto out; + } + break; + case HCILL_ASLEEP_TO_AWAKE: + /* + * this state means that a wake-up-indication + * is already on its way to the device, + * and will serve as the required wake-up-ack + */ + BT_DBG("dual wake-up-indication"); + break; + default: + /* any other state are illegal */ + BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state); + break; + } + + /* send pending packets and change state to HCILL_AWAKE */ + __ll_do_awake(ll); + +out: + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + /* actually send the packets */ + hci_uart_tx_wakeup(hu); +} + +/* + * Called upon a sleep-indication from the device + */ +static void ll_device_want_to_sleep(struct hci_uart *hu) +{ + unsigned long flags; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + /* sanity check */ + if (ll->hcill_state != HCILL_AWAKE) + BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state); + + /* acknowledge device sleep */ + if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) { + BT_ERR("cannot acknowledge device sleep"); + goto out; + } + + /* update state */ + ll->hcill_state = HCILL_ASLEEP; + +out: + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + /* actually send the sleep ack packet */ + hci_uart_tx_wakeup(hu); +} + +/* + * Called upon wake-up-acknowledgement from the device + */ +static void ll_device_woke_up(struct hci_uart *hu) +{ + unsigned long flags; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p", hu); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + /* sanity check */ + if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE) + BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state); + + /* send pending packets and change state to HCILL_AWAKE */ + __ll_do_awake(ll); + + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + /* actually send the packets */ + hci_uart_tx_wakeup(hu); +} + +/* Enqueue frame for transmittion (padding, crc, etc) */ +/* may be called from two simultaneous tasklets */ +static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) +{ + unsigned long flags = 0; + struct ll_struct *ll = hu->priv; + + BT_DBG("hu %p skb %p", hu, skb); + + /* Prepend skb with frame type */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + /* lock hcill state */ + spin_lock_irqsave(&ll->hcill_lock, flags); + + /* act according to current state */ + switch (ll->hcill_state) { + case HCILL_AWAKE: + BT_DBG("device awake, sending normally"); + skb_queue_tail(&ll->txq, skb); + break; + case HCILL_ASLEEP: + BT_DBG("device asleep, waking up and queueing packet"); + /* save packet for later */ + skb_queue_tail(&ll->tx_wait_q, skb); + /* awake device */ + if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) { + BT_ERR("cannot wake up device"); + break; + } + ll->hcill_state = HCILL_ASLEEP_TO_AWAKE; + break; + case HCILL_ASLEEP_TO_AWAKE: + BT_DBG("device waking up, queueing packet"); + /* transient state; just keep packet for later */ + skb_queue_tail(&ll->tx_wait_q, skb); + break; + default: + BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state); + kfree_skb(skb); + break; + } + + spin_unlock_irqrestore(&ll->hcill_lock, flags); + + return 0; +} + +static inline int ll_check_data_len(struct ll_struct *ll, int len) +{ + register int room = skb_tailroom(ll->rx_skb); + + BT_DBG("len %d room %d", len, room); + + if (!len) { + hci_recv_frame(ll->rx_skb); + } else if (len > room) { + BT_ERR("Data length is too large"); + kfree_skb(ll->rx_skb); + } else { + ll->rx_state = HCILL_W4_DATA; + ll->rx_count = len; + return len; + } + + ll->rx_state = HCILL_W4_PACKET_TYPE; + ll->rx_skb = NULL; + ll->rx_count = 0; + + return 0; +} + +/* Recv data */ +static int ll_recv(struct hci_uart *hu, void *data, int count) +{ + struct ll_struct *ll = hu->priv; + register char *ptr; + struct hci_event_hdr *eh; + struct hci_acl_hdr *ah; + struct hci_sco_hdr *sh; + register int len, type, dlen; + + BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count); + + ptr = data; + while (count) { + if (ll->rx_count) { + len = min_t(unsigned int, ll->rx_count, count); + memcpy(skb_put(ll->rx_skb, len), ptr, len); + ll->rx_count -= len; count -= len; ptr += len; + + if (ll->rx_count) + continue; + + switch (ll->rx_state) { + case HCILL_W4_DATA: + BT_DBG("Complete data"); + hci_recv_frame(ll->rx_skb); + + ll->rx_state = HCILL_W4_PACKET_TYPE; + ll->rx_skb = NULL; + continue; + + case HCILL_W4_EVENT_HDR: + eh = (struct hci_event_hdr *) ll->rx_skb->data; + + BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); + + ll_check_data_len(ll, eh->plen); + continue; + + case HCILL_W4_ACL_HDR: + ah = (struct hci_acl_hdr *) ll->rx_skb->data; + dlen = __le16_to_cpu(ah->dlen); + + BT_DBG("ACL header: dlen %d", dlen); + + ll_check_data_len(ll, dlen); + continue; + + case HCILL_W4_SCO_HDR: + sh = (struct hci_sco_hdr *) ll->rx_skb->data; + + BT_DBG("SCO header: dlen %d", sh->dlen); + + ll_check_data_len(ll, sh->dlen); + continue; + } + } + + /* HCILL_W4_PACKET_TYPE */ + switch (*ptr) { + case HCI_EVENT_PKT: + BT_DBG("Event packet"); + ll->rx_state = HCILL_W4_EVENT_HDR; + ll->rx_count = HCI_EVENT_HDR_SIZE; + type = HCI_EVENT_PKT; + break; + + case HCI_ACLDATA_PKT: + BT_DBG("ACL packet"); + ll->rx_state = HCILL_W4_ACL_HDR; + ll->rx_count = HCI_ACL_HDR_SIZE; + type = HCI_ACLDATA_PKT; + break; + + case HCI_SCODATA_PKT: + BT_DBG("SCO packet"); + ll->rx_state = HCILL_W4_SCO_HDR; + ll->rx_count = HCI_SCO_HDR_SIZE; + type = HCI_SCODATA_PKT; + break; + + /* HCILL signals */ + case HCILL_GO_TO_SLEEP_IND: + BT_DBG("HCILL_GO_TO_SLEEP_IND packet"); + ll_device_want_to_sleep(hu); + ptr++; count--; + continue; + + case HCILL_GO_TO_SLEEP_ACK: + /* shouldn't happen */ + BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state); + ptr++; count--; + continue; + + case HCILL_WAKE_UP_IND: + BT_DBG("HCILL_WAKE_UP_IND packet"); + ll_device_want_to_wakeup(hu); + ptr++; count--; + continue; + + case HCILL_WAKE_UP_ACK: + BT_DBG("HCILL_WAKE_UP_ACK packet"); + ll_device_woke_up(hu); + ptr++; count--; + continue; + + default: + BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); + hu->hdev->stat.err_rx++; + ptr++; count--; + continue; + }; + + ptr++; count--; + + /* Allocate packet */ + ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!ll->rx_skb) { + BT_ERR("Can't allocate mem for new packet"); + ll->rx_state = HCILL_W4_PACKET_TYPE; + ll->rx_count = 0; + return 0; + } + + ll->rx_skb->dev = (void *) hu->hdev; + bt_cb(ll->rx_skb)->pkt_type = type; + } + + return count; +} + +static struct sk_buff *ll_dequeue(struct hci_uart *hu) +{ + struct ll_struct *ll = hu->priv; + return skb_dequeue(&ll->txq); +} + +static struct hci_uart_proto llp = { + .id = HCI_UART_LL, + .open = ll_open, + .close = ll_close, + .recv = ll_recv, + .enqueue = ll_enqueue, + .dequeue = ll_dequeue, + .flush = ll_flush, +}; + +int ll_init(void) +{ + int err = hci_uart_register_proto(&llp); + + if (!err) + BT_INFO("HCILL protocol initialized"); + else + BT_ERR("HCILL protocol registration failed"); + + return err; +} + +int ll_deinit(void) +{ + return hci_uart_unregister_proto(&llp); +} diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 1097ce72393f..50113db06b9f 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -33,12 +33,13 @@ #define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 4 +#define HCI_UART_MAX_PROTO 5 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 #define HCI_UART_3WIRE 2 #define HCI_UART_H4DS 3 +#define HCI_UART_LL 4 struct hci_uart; @@ -85,3 +86,8 @@ int h4_deinit(void); int bcsp_init(void); int bcsp_deinit(void); #endif + +#ifdef CONFIG_BT_HCIUART_LL +int ll_init(void); +int ll_deinit(void); +#endif From ac019360fe311dd6aa11b358a02eb3a61675882e Mon Sep 17 00:00:00 2001 From: Jeff Garzik <jeff@garzik.org> Date: Sat, 20 Oct 2007 13:45:57 +0200 Subject: [PATCH 09/30] [Bluetooth] Eliminate checks for impossible conditions in IRQ handler Our info structure and info->hdev is always passed to the IRQ handler, so we don't have to worry about these checks in every interrupt. Leave a BUG_ON() just to help unwary programmers, but these could probably be removed as well. Signed-off-by: Jeff Garzik <jgarzik@redhat.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- drivers/bluetooth/bluecard_cs.c | 5 +---- drivers/bluetooth/bt3c_cs.c | 5 +---- drivers/bluetooth/btuart_cs.c | 5 +---- drivers/bluetooth/dtl1_cs.c | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 851de4d5b7de..bcf57927b7a8 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -503,10 +503,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst) unsigned int iobase; unsigned char reg; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); if (!test_bit(CARD_READY, &(info->hw_state))) return IRQ_HANDLED; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 39516074636b..a18f9b8c9e12 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -344,10 +344,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) unsigned int iobase; int iir; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); iobase = info->p_dev->io.BasePort1; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index d7d2ea0d86a1..08f48d577aba 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -294,10 +294,7 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst) int boguscount = 0; int iir, lsr; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); iobase = info->p_dev->io.BasePort1; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 7f9c54b9964a..dae45cdf02b2 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -298,10 +298,7 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst) int boguscount = 0; int iir, lsr; - if (!info || !info->hdev) { - BT_ERR("Call of irq %d for unknown device", irq); - return IRQ_NONE; - } + BUG_ON(!info->hdev); iobase = info->p_dev->io.BasePort1; From ddbaf13e3609442b64abb931ac21527772d87980 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 14:02:04 +0200 Subject: [PATCH 10/30] [Bluetooth] Add generic driver for Bluetooth SDIO devices This patch adds a generic driver for Bluetooth SDIO devices. It supports Type-A and Type-B devices. Signed-off-by: David Vrabel <david.vrabel@csr.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- drivers/bluetooth/Kconfig | 11 + drivers/bluetooth/Makefile | 2 + drivers/bluetooth/btsdio.c | 406 +++++++++++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 drivers/bluetooth/btsdio.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 62b89b95ace2..77bded441cf1 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -22,6 +22,17 @@ config BT_HCIUSB_SCO Say Y here to compile support for SCO over HCI USB. +config BT_HCIBTSDIO + tristate "HCI SDIO driver" + depends on MMC + help + Bluetooth HCI SDIO driver. + This driver is required if you want to use Bluetooth device with + SDIO interface. + + Say Y here to compile support for Bluetooth SDIO devices into the + kernel or say M to compile it as module (btsdio). + config BT_HCIUART tristate "HCI UART driver" help diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index a543dfc2d6f5..aee12797aa1c 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o +obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o + hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c new file mode 100644 index 000000000000..b786f6187902 --- /dev/null +++ b/drivers/bluetooth/btsdio.c @@ -0,0 +1,406 @@ +/* + * + * Generic Bluetooth SDIO driver + * + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. + * Copyright (C) 2007 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/skbuff.h> + +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/sdio_func.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +#ifndef CONFIG_BT_HCIBTSDIO_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "0.1" + +static const struct sdio_device_id btsdio_table[] = { + /* Generic Bluetooth Type-A SDIO device */ + { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) }, + + /* Generic Bluetooth Type-B SDIO device */ + { SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) }, + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(sdio, btsdio_table); + +struct btsdio_data { + struct hci_dev *hdev; + struct sdio_func *func; + + struct work_struct work; + + struct sk_buff_head txq; +}; + +#define REG_RDAT 0x00 /* Receiver Data */ +#define REG_TDAT 0x00 /* Transmitter Data */ +#define REG_PC_RRT 0x10 /* Read Packet Control */ +#define REG_PC_WRT 0x11 /* Write Packet Control */ +#define REG_RTC_STAT 0x12 /* Retry Control Status */ +#define REG_RTC_SET 0x12 /* Retry Control Set */ +#define REG_INTRD 0x13 /* Interrupt Indication */ +#define REG_CL_INTRD 0x13 /* Interrupt Clear */ +#define REG_EN_INTRD 0x14 /* Interrupt Enable */ +#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */ + +static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb) +{ + int err; + + BT_DBG("%s", data->hdev->name); + + /* Prepend Type-A header */ + skb_push(skb, 4); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + + err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len); + if (err < 0) { + sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL); + return err; + } + + data->hdev->stat.byte_tx += skb->len; + + kfree_skb(skb); + + return 0; +} + +static void btsdio_work(struct work_struct *work) +{ + struct btsdio_data *data = container_of(work, struct btsdio_data, work); + struct sk_buff *skb; + int err; + + BT_DBG("%s", data->hdev->name); + + sdio_claim_host(data->func); + + while ((skb = skb_dequeue(&data->txq))) { + err = btsdio_tx_packet(data, skb); + if (err < 0) { + data->hdev->stat.err_tx++; + skb_queue_head(&data->txq, skb); + break; + } + } + + sdio_release_host(data->func); +} + +static int btsdio_rx_packet(struct btsdio_data *data) +{ + u8 hdr[4] __attribute__ ((aligned(4))); + struct sk_buff *skb; + int err, len; + + BT_DBG("%s", data->hdev->name); + + err = sdio_readsb(data->func, hdr, REG_RDAT, 4); + if (err < 0) + return err; + + len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16); + if (len < 4 || len > 65543) + return -EILSEQ; + + skb = bt_skb_alloc(len - 4, GFP_KERNEL); + if (!skb) { + /* Out of memory. Prepare a read retry and just + * return with the expectation that the next time + * we're called we'll have more memory. */ + return -ENOMEM; + } + + skb_put(skb, len - 4); + + err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4); + if (err < 0) { + kfree(skb); + return err; + } + + data->hdev->stat.byte_rx += len; + + skb->dev = (void *) data->hdev; + bt_cb(skb)->pkt_type = hdr[3]; + + err = hci_recv_frame(skb); + if (err < 0) { + kfree(skb); + return err; + } + + sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL); + + return 0; +} + +static void btsdio_interrupt(struct sdio_func *func) +{ + struct btsdio_data *data = sdio_get_drvdata(func); + int intrd; + + BT_DBG("%s", data->hdev->name); + + intrd = sdio_readb(func, REG_INTRD, NULL); + if (intrd & 0x01) { + sdio_writeb(func, 0x01, REG_CL_INTRD, NULL); + + if (btsdio_rx_packet(data) < 0) { + data->hdev->stat.err_rx++; + sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL); + } + } +} + +static int btsdio_open(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + int err; + + BT_DBG("%s", hdev->name); + + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + sdio_claim_host(data->func); + + err = sdio_enable_func(data->func); + if (err < 0) { + clear_bit(HCI_RUNNING, &hdev->flags); + goto release; + } + + err = sdio_claim_irq(data->func, btsdio_interrupt); + if (err < 0) { + sdio_disable_func(data->func); + clear_bit(HCI_RUNNING, &hdev->flags); + goto release; + } + + if (data->func->class == SDIO_CLASS_BT_B) + sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL); + + sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL); + +release: + sdio_release_host(data->func); + + return err; +} + +static int btsdio_close(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + sdio_claim_host(data->func); + + sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL); + + sdio_release_irq(data->func); + sdio_disable_func(data->func); + + sdio_release_host(data->func); + + return 0; +} + +static int btsdio_flush(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + skb_queue_purge(&data->txq); + + return 0; +} + +static int btsdio_send_frame(struct sk_buff *skb) +{ + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + + default: + return -EILSEQ; + } + + skb_queue_tail(&data->txq, skb); + + schedule_work(&data->work); + + return 0; +} + +static void btsdio_destruct(struct hci_dev *hdev) +{ + struct btsdio_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + kfree(data); +} + +static int btsdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + struct btsdio_data *data; + struct hci_dev *hdev; + struct sdio_func_tuple *tuple = func->tuples; + int err; + + BT_DBG("func %p id %p class 0x%04x", func, id, func->class); + + while (tuple) { + BT_DBG("code 0x%x size %d", tuple->code, tuple->size); + tuple = tuple->next; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->func = func; + + INIT_WORK(&data->work, btsdio_work); + + skb_queue_head_init(&data->txq); + + hdev = hci_alloc_dev(); + if (!hdev) { + kfree(data); + return -ENOMEM; + } + + hdev->type = HCI_SDIO; + hdev->driver_data = data; + + data->hdev = hdev; + + SET_HCIDEV_DEV(hdev, &func->dev); + + hdev->open = btsdio_open; + hdev->close = btsdio_close; + hdev->flush = btsdio_flush; + hdev->send = btsdio_send_frame; + hdev->destruct = btsdio_destruct; + + hdev->owner = THIS_MODULE; + + err = hci_register_dev(hdev); + if (err < 0) { + hci_free_dev(hdev); + kfree(data); + return err; + } + + sdio_set_drvdata(func, data); + + return 0; +} + +static void btsdio_remove(struct sdio_func *func) +{ + struct btsdio_data *data = sdio_get_drvdata(func); + struct hci_dev *hdev; + + BT_DBG("func %p", func); + + if (!data) + return; + + hdev = data->hdev; + + sdio_set_drvdata(func, NULL); + + hci_unregister_dev(hdev); + + hci_free_dev(hdev); +} + +static struct sdio_driver btsdio_driver = { + .name = "btsdio", + .probe = btsdio_probe, + .remove = btsdio_remove, + .id_table = btsdio_table, +}; + +static int __init btsdio_init(void) +{ + BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION); + + return sdio_register_driver(&btsdio_driver); +} + +static void __exit btsdio_exit(void) +{ + sdio_unregister_driver(&btsdio_driver); +} + +module_init(btsdio_init); +module_exit(btsdio_exit); + +MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); +MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); From 5e23b923da03de7e41f00f5664ae22c4f7dcd2a3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 14:12:34 +0200 Subject: [PATCH 11/30] [Bluetooth] Add generic driver for Bluetooth USB devices This patch adds a new generic driver for Bluetooth USB devices. This driver is still experimental at this point, but it is cleaner and easier to maintain than the current Bluetooth USB driver. It is a much better starting point for power management improvements. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- drivers/bluetooth/Kconfig | 13 + drivers/bluetooth/Makefile | 1 + drivers/bluetooth/btusb.c | 564 +++++++++++++++++++++++++++++++++++++ 3 files changed, 578 insertions(+) create mode 100644 drivers/bluetooth/btusb.c diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 77bded441cf1..075598e1c502 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -22,6 +22,19 @@ config BT_HCIUSB_SCO Say Y here to compile support for SCO over HCI USB. +config BT_HCIBTUSB + tristate "HCI USB driver (alternate version)" + depends on USB && EXPERIMENTAL && BT_HCIUSB=n + help + Bluetooth HCI USB driver. + This driver is required if you want to use Bluetooth devices with + USB interface. + + This driver is still experimental and has no SCO support. + + Say Y here to compile support for Bluetooth USB devices into the + kernel or say M to compile it as module (btusb). + config BT_HCIBTSDIO tristate "HCI SDIO driver" depends on MMC diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index aee12797aa1c..77444afbf107 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o +obj-$(CONFIG_BT_HCIBTUSB) += btusb.o obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o hci_uart-y := hci_ldisc.o diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c new file mode 100644 index 000000000000..12e108914f19 --- /dev/null +++ b/drivers/bluetooth/btusb.c @@ -0,0 +1,564 @@ +/* + * + * Generic Bluetooth USB driver + * + * Copyright (C) 2005-2007 Marcel Holtmann <marcel@holtmann.org> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/skbuff.h> + +#include <linux/usb.h> + +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +//#define CONFIG_BT_HCIBTUSB_DEBUG +#ifndef CONFIG_BT_HCIBTUSB_DEBUG +#undef BT_DBG +#define BT_DBG(D...) +#endif + +#define VERSION "0.1" + +static struct usb_device_id btusb_table[] = { + /* Generic Bluetooth USB device */ + { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, + + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, btusb_table); + +static struct usb_device_id blacklist_table[] = { + { } /* Terminating entry */ +}; + +#define BTUSB_INTR_RUNNING 0 +#define BTUSB_BULK_RUNNING 1 + +struct btusb_data { + struct hci_dev *hdev; + struct usb_device *udev; + + spinlock_t lock; + + unsigned long flags; + + struct work_struct work; + + struct usb_anchor tx_anchor; + struct usb_anchor intr_anchor; + struct usb_anchor bulk_anchor; + + struct usb_endpoint_descriptor *intr_ep; + struct usb_endpoint_descriptor *bulk_tx_ep; + struct usb_endpoint_descriptor *bulk_rx_ep; +}; + +static void btusb_intr_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = hdev->driver_data; + int err; + + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; + + if (urb->status == 0) { + if (hci_recv_fragment(hdev, HCI_EVENT_PKT, + urb->transfer_buffer, + urb->actual_length) < 0) { + BT_ERR("%s corrupted event packet", hdev->name); + hdev->stat.err_rx++; + } + } + + if (!test_bit(BTUSB_INTR_RUNNING, &data->flags)) + return; + + usb_anchor_urb(urb, &data->intr_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } +} + +static inline int btusb_submit_intr_urb(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size; + + BT_DBG("%s", hdev->name); + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + size = le16_to_cpu(data->intr_ep->wMaxPacketSize); + + buf = kmalloc(size, GFP_ATOMIC); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvintpipe(data->udev, data->intr_ep->bEndpointAddress); + + usb_fill_int_urb(urb, data->udev, pipe, buf, size, + btusb_intr_complete, hdev, + data->intr_ep->bInterval); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &data->intr_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_bulk_complete(struct urb *urb) +{ + struct hci_dev *hdev = urb->context; + struct btusb_data *data = hdev->driver_data; + int err; + + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return; + + if (urb->status == 0) { + if (hci_recv_fragment(hdev, HCI_ACLDATA_PKT, + urb->transfer_buffer, + urb->actual_length) < 0) { + BT_ERR("%s corrupted ACL packet", hdev->name); + hdev->stat.err_rx++; + } + } + + if (!test_bit(BTUSB_BULK_RUNNING, &data->flags)) + return; + + usb_anchor_urb(urb, &data->bulk_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p failed to resubmit (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + } +} + +static inline int btusb_submit_bulk_urb(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + struct urb *urb; + unsigned char *buf; + unsigned int pipe; + int err, size; + + BT_DBG("%s", hdev->name); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return -ENOMEM; + + size = le16_to_cpu(data->bulk_rx_ep->wMaxPacketSize); + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + usb_free_urb(urb); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(data->udev, data->bulk_rx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, data->udev, pipe, + buf, size, btusb_bulk_complete, hdev); + + urb->transfer_flags |= URB_FREE_BUFFER; + + usb_anchor_urb(urb, &data->bulk_anchor); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err < 0) { + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); + usb_unanchor_urb(urb); + kfree(buf); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_tx_complete(struct urb *urb) +{ + struct sk_buff *skb = urb->context; + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + + BT_DBG("%s urb %p status %d count %d", hdev->name, + urb, urb->status, urb->actual_length); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto done; + + if (!urb->status) + hdev->stat.byte_tx += urb->transfer_buffer_length; + else + hdev->stat.err_tx++; + +done: + kfree(urb->setup_packet); + + kfree_skb(skb); +} + +static int btusb_open(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + int err; + + BT_DBG("%s", hdev->name); + + if (test_and_set_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags)) + return 0; + + err = btusb_submit_intr_urb(hdev); + if (err < 0) { + clear_bit(BTUSB_INTR_RUNNING, &hdev->flags); + clear_bit(HCI_RUNNING, &hdev->flags); + } + + return err; +} + +static int btusb_close(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->bulk_anchor); + + clear_bit(BTUSB_INTR_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->intr_anchor); + + return 0; +} + +static int btusb_flush(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + usb_kill_anchored_urbs(&data->tx_anchor); + + return 0; +} + +static int btusb_send_frame(struct sk_buff *skb) +{ + struct hci_dev *hdev = (struct hci_dev *) skb->dev; + struct btusb_data *data = hdev->driver_data; + struct usb_ctrlrequest *dr; + struct urb *urb; + unsigned int pipe; + int err; + + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + dr = kmalloc(sizeof(*dr), GFP_ATOMIC); + if (!dr) { + usb_free_urb(urb); + return -ENOMEM; + } + + dr->bRequestType = USB_TYPE_CLASS; + dr->bRequest = 0; + dr->wIndex = 0; + dr->wValue = 0; + dr->wLength = __cpu_to_le16(skb->len); + + pipe = usb_sndctrlpipe(data->udev, 0x00); + + usb_fill_control_urb(urb, data->udev, pipe, (void *) dr, + skb->data, skb->len, btusb_tx_complete, skb); + + hdev->stat.cmd_tx++; + break; + + case HCI_ACLDATA_PKT: + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) + return -ENOMEM; + + pipe = usb_sndbulkpipe(data->udev, + data->bulk_tx_ep->bEndpointAddress); + + usb_fill_bulk_urb(urb, data->udev, pipe, + skb->data, skb->len, btusb_tx_complete, skb); + + hdev->stat.acl_tx++; + break; + + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + kfree_skb(skb); + return 0; + + default: + return -EILSEQ; + } + + usb_anchor_urb(urb, &data->tx_anchor); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + BT_ERR("%s urb %p submission failed", hdev->name, urb); + kfree(urb->setup_packet); + usb_unanchor_urb(urb); + } + + usb_free_urb(urb); + + return err; +} + +static void btusb_destruct(struct hci_dev *hdev) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s", hdev->name); + + kfree(data); +} + +static void btusb_notify(struct hci_dev *hdev, unsigned int evt) +{ + struct btusb_data *data = hdev->driver_data; + + BT_DBG("%s evt %d", hdev->name, evt); + + if (evt == HCI_NOTIFY_CONN_ADD || evt == HCI_NOTIFY_CONN_DEL) + schedule_work(&data->work); +} + +static void btusb_work(struct work_struct *work) +{ + struct btusb_data *data = container_of(work, struct btusb_data, work); + struct hci_dev *hdev = data->hdev; + + if (hdev->conn_hash.acl_num == 0) { + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + usb_kill_anchored_urbs(&data->bulk_anchor); + return; + } + + if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) { + if (btusb_submit_bulk_urb(hdev) < 0) + clear_bit(BTUSB_BULK_RUNNING, &data->flags); + else + btusb_submit_bulk_urb(hdev); + } +} + +static int btusb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_endpoint_descriptor *ep_desc; + struct btusb_data *data; + struct hci_dev *hdev; + int i, err; + + BT_DBG("intf %p id %p", intf, id); + + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + + if (!id->driver_info) { + const struct usb_device_id *match; + match = usb_match_id(intf, blacklist_table); + if (match) + id = match; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) { + ep_desc = &intf->cur_altsetting->endpoint[i].desc; + + if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) { + data->intr_ep = ep_desc; + continue; + } + + if (!data->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) { + data->bulk_tx_ep = ep_desc; + continue; + } + + if (!data->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) { + data->bulk_rx_ep = ep_desc; + continue; + } + } + + if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) { + kfree(data); + return -ENODEV; + } + + data->udev = interface_to_usbdev(intf); + + spin_lock_init(&data->lock); + + INIT_WORK(&data->work, btusb_work); + + init_usb_anchor(&data->tx_anchor); + init_usb_anchor(&data->intr_anchor); + init_usb_anchor(&data->bulk_anchor); + + hdev = hci_alloc_dev(); + if (!hdev) { + kfree(data); + return -ENOMEM; + } + + hdev->type = HCI_USB; + hdev->driver_data = data; + + data->hdev = hdev; + + SET_HCIDEV_DEV(hdev, &intf->dev); + + hdev->open = btusb_open; + hdev->close = btusb_close; + hdev->flush = btusb_flush; + hdev->send = btusb_send_frame; + hdev->destruct = btusb_destruct; + hdev->notify = btusb_notify; + + hdev->owner = THIS_MODULE; + + set_bit(HCI_QUIRK_RESET_ON_INIT, &hdev->quirks); + + err = hci_register_dev(hdev); + if (err < 0) { + hci_free_dev(hdev); + kfree(data); + return err; + } + + usb_set_intfdata(intf, data); + + return 0; +} + +static void btusb_disconnect(struct usb_interface *intf) +{ + struct btusb_data *data = usb_get_intfdata(intf); + struct hci_dev *hdev; + + BT_DBG("intf %p", intf); + + if (!data) + return; + + hdev = data->hdev; + + usb_set_intfdata(intf, NULL); + + hci_unregister_dev(hdev); + + hci_free_dev(hdev); +} + +static struct usb_driver btusb_driver = { + .name = "btusb", + .probe = btusb_probe, + .disconnect = btusb_disconnect, + .id_table = btusb_table, +}; + +static int __init btusb_init(void) +{ + BT_INFO("Generic Bluetooth USB driver ver %s", VERSION); + + return usb_register(&btusb_driver); +} + +static void __exit btusb_exit(void) +{ + usb_deregister(&btusb_driver); +} + +module_init(btusb_init); +module_exit(btusb_exit); + +MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); +MODULE_DESCRIPTION("Generic Bluetooth USB driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); From 6792b5ec8d9e723e4689fd5b16cfa52603985f43 Mon Sep 17 00:00:00 2001 From: Dave Young <hidave.darkstar@gmail.com> Date: Sat, 20 Oct 2007 14:15:39 +0200 Subject: [PATCH 12/30] [Bluetooth] Fix wrong argument in debug code of HIDP In the debug code of the hidp_queue_report function, the device variable does not exist, replace it with session->hid. Signed-off-by: Dave Young <hidave.darkstar@gmail.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- net/bluetooth/hidp/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 66c736953cfe..4bbacddeb49d 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -247,7 +247,7 @@ static inline int hidp_queue_report(struct hidp_session *session, unsigned char { struct sk_buff *skb; - BT_DBG("session %p hid %p data %p size %d", session, device, data, size); + BT_DBG("session %p hid %p data %p size %d", session, session->hid, data, size); if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { BT_ERR("Can't allocate memory for new frame"); From dae6a0f6636d05bcb28ece1f3630b23ed2d66e18 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 14:52:38 +0200 Subject: [PATCH 13/30] [Bluetooth] Add address and channel attribute to RFCOMM TTY device Export the remote device address and channel of RFCOMM TTY device via sysfs attributes. This allows udev to create better naming rules for configured RFCOMM devices. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- net/bluetooth/rfcomm/tty.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 22a832098d44..e447651a2dbe 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -189,6 +189,23 @@ static struct device *rfcomm_get_device(struct rfcomm_dev *dev) return conn ? &conn->dev : NULL; } +static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf) +{ + struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); + bdaddr_t bdaddr; + baswap(&bdaddr, &dev->dst); + return sprintf(buf, "%s\n", batostr(&bdaddr)); +} + +static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf) +{ + struct rfcomm_dev *dev = dev_get_drvdata(tty_dev); + return sprintf(buf, "%d\n", dev->channel); +} + +static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); +static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); + static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) { struct rfcomm_dev *dev; @@ -281,6 +298,14 @@ out: return err; } + dev_set_drvdata(dev->tty_dev, dev); + + if (device_create_file(dev->tty_dev, &dev_attr_address) < 0) + BT_ERR("Failed to create address attribute"); + + if (device_create_file(dev->tty_dev, &dev_attr_channel) < 0) + BT_ERR("Failed to create channel attribute"); + return dev->id; } From b6a0dc822497e1c0b9e8c4add270cc27fce48454 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 14:55:10 +0200 Subject: [PATCH 14/30] [Bluetooth] Add support for handling simple eSCO links With the Bluetooth 1.2 specification the Extended SCO feature for better audio connections was introduced. So far the Bluetooth core wasn't able to handle any eSCO connections correctly. This patch adds simple eSCO support while keeping backward compatibility with older devices. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_conn.c | 39 +++++++++++-- net/bluetooth/hci_core.c | 22 +++++++ net/bluetooth/hci_event.c | 99 +++++++++++++++++++++++++++----- net/bluetooth/sco.c | 12 ++-- 5 files changed, 151 insertions(+), 22 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0db89ed6b00c..ea13baa3851b 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -313,6 +313,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, void hci_acl_connect(struct hci_conn *conn); void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_add_sco(struct hci_conn *conn, __u16 handle); +void hci_setup_sync(struct hci_conn *conn, __u16 handle); struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst); int hci_conn_del(struct hci_conn *conn); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 797a30bec6fd..9483320f6dad 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -78,9 +78,9 @@ void hci_acl_connect(struct hci_conn *conn) cp.pkt_type = cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK); if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER)) - cp.role_switch = 0x01; + cp.role_switch = 0x01; else - cp.role_switch = 0x00; + cp.role_switch = 0x00; hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp); } @@ -127,6 +127,28 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle) hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp); } +void hci_setup_sync(struct hci_conn *conn, __u16 handle) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_setup_sync_conn cp; + + BT_DBG("%p", conn); + + conn->state = BT_CONNECT; + conn->out = 1; + + cp.handle = cpu_to_le16(handle); + cp.pkt_type = cpu_to_le16(hdev->esco_type); + + cp.tx_bandwidth = cpu_to_le32(0x00001f40); + cp.rx_bandwidth = cpu_to_le32(0x00001f40); + cp.max_latency = cpu_to_le16(0xffff); + cp.voice_setting = cpu_to_le16(hdev->voice_setting); + cp.retrans_effort = 0xff; + + hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp); +} + static void hci_conn_timeout(unsigned long arg) { struct hci_conn *conn = (void *) arg; @@ -141,7 +163,10 @@ static void hci_conn_timeout(unsigned long arg) switch (conn->state) { case BT_CONNECT: - hci_acl_connect_cancel(conn); + if (conn->type == ACL_LINK) + hci_acl_connect_cancel(conn); + else + hci_acl_disconn(conn, 0x13); break; case BT_CONNECTED: hci_acl_disconn(conn, 0x13); @@ -328,8 +353,12 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst) hci_conn_hold(sco); if (acl->state == BT_CONNECTED && - (sco->state == BT_OPEN || sco->state == BT_CLOSED)) - hci_add_sco(sco, acl->handle); + (sco->state == BT_OPEN || sco->state == BT_CLOSED)) { + if (lmp_esco_capable(hdev)) + hci_setup_sync(sco, acl->handle); + else + hci_add_sco(sco, acl->handle); + } return sco; } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2894382dd26d..372b0d3b75a8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1361,6 +1361,26 @@ static inline void hci_sched_sco(struct hci_dev *hdev) } } +static inline void hci_sched_esco(struct hci_dev *hdev) +{ + struct hci_conn *conn; + struct sk_buff *skb; + int quote; + + BT_DBG("%s", hdev->name); + + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { + while (quote-- && (skb = skb_dequeue(&conn->data_q))) { + BT_DBG("skb %p len %d", skb, skb->len); + hci_send_frame(skb); + + conn->sent++; + if (conn->sent == ~0) + conn->sent = 0; + } + } +} + static void hci_tx_task(unsigned long arg) { struct hci_dev *hdev = (struct hci_dev *) arg; @@ -1376,6 +1396,8 @@ static void hci_tx_task(unsigned long arg) hci_sched_sco(hdev); + hci_sched_esco(hdev); + /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q))) hci_send_frame(skb); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e2cfeea5ee72..46df2e403df8 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -511,11 +511,11 @@ static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status) struct hci_conn *acl, *sco; __u16 handle; + BT_DBG("%s status 0x%x", hdev->name, status); + if (!status) return; - BT_DBG("%s status 0x%x", hdev->name, status); - cp = hci_sent_cmd_data(hdev, HCI_OP_ADD_SCO); if (!cp) return; @@ -544,7 +544,34 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status) { + struct hci_cp_setup_sync_conn *cp; + struct hci_conn *acl, *sco; + __u16 handle; + BT_DBG("%s status 0x%x", hdev->name, status); + + if (!status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN); + if (!cp) + return; + + handle = __le16_to_cpu(cp->handle); + + BT_DBG("%s handle %d", hdev->name, handle); + + hci_dev_lock(hdev); + + acl = hci_conn_hash_lookup_handle(hdev, handle); + if (acl && (sco = acl->link)) { + sco->state = BT_CLOSED; + + hci_proto_connect_cfm(sco, status); + hci_conn_del(sco); + } + + hci_dev_unlock(hdev); } static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) @@ -692,9 +719,12 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; if (sco) { - if (!ev->status) - hci_add_sco(sco, conn->handle); - else { + if (!ev->status) { + if (lmp_esco_capable(hdev)) + hci_setup_sync(sco, conn->handle); + else + hci_add_sco(sco, conn->handle); + } else { hci_proto_connect_cfm(sco, ev->status); hci_conn_del(sco); } @@ -724,9 +754,9 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk if (mask & HCI_LM_ACCEPT) { /* Connection accepted */ struct hci_conn *conn; - struct hci_cp_accept_conn_req cp; hci_dev_lock(hdev); + conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { if (!(conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr))) { @@ -735,18 +765,39 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk return; } } + memcpy(conn->dev_class, ev->dev_class, 3); conn->state = BT_CONNECT; + hci_dev_unlock(hdev); - bacpy(&cp.bdaddr, &ev->bdaddr); + if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) { + struct hci_cp_accept_conn_req cp; - if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) - cp.role = 0x00; /* Become master */ - else - cp.role = 0x01; /* Remain slave */ + bacpy(&cp.bdaddr, &ev->bdaddr); - hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) + cp.role = 0x00; /* Become master */ + else + cp.role = 0x01; /* Remain slave */ + + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, + sizeof(cp), &cp); + } else { + struct hci_cp_accept_sync_conn_req cp; + + bacpy(&cp.bdaddr, &ev->bdaddr); + cp.pkt_type = cpu_to_le16(hdev->esco_type); + + cp.tx_bandwidth = cpu_to_le32(0x00001f40); + cp.rx_bandwidth = cpu_to_le32(0x00001f40); + cp.max_latency = cpu_to_le16(0xffff); + cp.content_format = cpu_to_le16(hdev->voice_setting); + cp.retrans_effort = 0xff; + + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, + sizeof(cp), &cp); + } } else { /* Connection rejected */ struct hci_cp_reject_conn_req cp; @@ -1254,7 +1305,29 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { - BT_DBG("%s", hdev->name); + struct hci_ev_sync_conn_complete *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); + if (!conn) + goto unlock; + + if (!ev->status) { + conn->handle = __le16_to_cpu(ev->handle); + conn->state = BT_CONNECTED; + } else + conn->state = BT_CLOSED; + + hci_proto_connect_cfm(conn, ev->status); + if (ev->status) + hci_conn_del(conn); + +unlock: + hci_dev_unlock(hdev); } static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 65b6fb1c4154..82d0dfdfa7e2 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -189,7 +189,7 @@ static int sco_connect(struct sock *sk) struct sco_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; - int err = 0; + int err, type; BT_DBG("%s -> %s", batostr(src), batostr(dst)); @@ -200,7 +200,9 @@ static int sco_connect(struct sock *sk) err = -ENOMEM; - hcon = hci_connect(hdev, SCO_LINK, dst); + type = lmp_esco_capable(hdev) ? ESCO_LINK : SCO_LINK; + + hcon = hci_connect(hdev, type, dst); if (!hcon) goto done; @@ -224,6 +226,7 @@ static int sco_connect(struct sock *sk) sk->sk_state = BT_CONNECT; sco_sock_set_timer(sk, sk->sk_sndtimeo); } + done: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -846,7 +849,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status) { BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); - if (hcon->type != SCO_LINK) + if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK) return 0; if (!status) { @@ -865,10 +868,11 @@ static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); - if (hcon->type != SCO_LINK) + if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK) return 0; sco_conn_del(hcon, bt_err(reason)); + return 0; } From 2cb3377a295aade1f2e192d4bea948b2196fb162 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 15:28:08 +0200 Subject: [PATCH 15/30] [Bluetooth] Add constant for Bluetooth socket options level Assign the next free socket options level to be used by the Bluetooth protocol and address family. Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- include/linux/socket.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/socket.h b/include/linux/socket.h index f852e1afd65a..c22ef1c1afb8 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -291,6 +291,7 @@ struct ucred { #define SOL_TIPC 271 #define SOL_RXRPC 272 #define SOL_PPPOL2TP 273 +#define SOL_BLUETOOTH 274 /* IPX options */ #define IPX_TYPE 1 From a524eccc7307b1d6e79f03fed79f9f34c016ce56 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann <marcel@holtmann.org> Date: Sat, 20 Oct 2007 21:37:20 +0200 Subject: [PATCH 16/30] [Bluetooth] Convert RFCOMM to use kthread API This patch does the full kthread conversion for the RFCOMM protocol. It makes the code slightly simpler and more maintainable. Based on a patch from Christoph Hellwig <hch@lst.de> Signed-off-by: Marcel Holtmann <marcel@holtmann.org> --- net/bluetooth/rfcomm/core.c | 62 +++++++++++++------------------------ 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index bb7220770f2c..e7ac6ba7ecab 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -33,11 +33,11 @@ #include <linux/sched.h> #include <linux/signal.h> #include <linux/init.h> -#include <linux/freezer.h> #include <linux/wait.h> #include <linux/device.h> #include <linux/net.h> #include <linux/mutex.h> +#include <linux/kthread.h> #include <net/sock.h> #include <asm/uaccess.h> @@ -68,7 +68,6 @@ static DEFINE_MUTEX(rfcomm_mutex); static unsigned long rfcomm_event; static LIST_HEAD(session_list); -static atomic_t terminate, running; static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); @@ -1850,26 +1849,6 @@ static inline void rfcomm_process_sessions(void) rfcomm_unlock(); } -static void rfcomm_worker(void) -{ - BT_DBG(""); - - while (!atomic_read(&terminate)) { - set_current_state(TASK_INTERRUPTIBLE); - if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { - /* No pending events. Let's sleep. - * Incoming connections and data will wake us up. */ - schedule(); - } - set_current_state(TASK_RUNNING); - - /* Process stuff */ - clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); - rfcomm_process_sessions(); - } - return; -} - static int rfcomm_add_listener(bdaddr_t *ba) { struct sockaddr_l2 addr; @@ -1935,22 +1914,28 @@ static void rfcomm_kill_listener(void) static int rfcomm_run(void *unused) { - rfcomm_thread = current; - - atomic_inc(&running); - - daemonize("krfcommd"); - set_user_nice(current, -10); - BT_DBG(""); + set_user_nice(current, -10); + rfcomm_add_listener(BDADDR_ANY); - rfcomm_worker(); + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { + /* No pending events. Let's sleep. + * Incoming connections and data will wake us up. */ + schedule(); + } + set_current_state(TASK_RUNNING); + + /* Process stuff */ + clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); + rfcomm_process_sessions(); + } rfcomm_kill_listener(); - atomic_dec(&running); return 0; } @@ -2059,7 +2044,11 @@ static int __init rfcomm_init(void) hci_register_cb(&rfcomm_cb); - kernel_thread(rfcomm_run, NULL, CLONE_KERNEL); + rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd"); + if (IS_ERR(rfcomm_thread)) { + hci_unregister_cb(&rfcomm_cb); + return PTR_ERR(rfcomm_thread); + } if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0) BT_ERR("Failed to create RFCOMM info file"); @@ -2081,14 +2070,7 @@ static void __exit rfcomm_exit(void) hci_unregister_cb(&rfcomm_cb); - /* Terminate working thread. - * ie. Set terminate flag and wake it up */ - atomic_inc(&terminate); - rfcomm_schedule(RFCOMM_SCHED_STATE); - - /* Wait until thread is running */ - while (atomic_read(&running)) - schedule(); + kthread_stop(rfcomm_thread); #ifdef CONFIG_BT_RFCOMM_TTY rfcomm_cleanup_ttys(); From 6c7af27c8a2e8b85cb235a2409d3b2093b18f77d Mon Sep 17 00:00:00 2001 From: Matt Carlson <mcarlson@broadcom.com> Date: Sun, 21 Oct 2007 16:12:02 -0700 Subject: [PATCH 17/30] [TG3]: Add 5723 support This patch adds support for upcoming 5723 devices. Signed-off-by: Matt Carlson <mcarlson@broadcom.com> Signed-off-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/tg3.c | 1 + include/linux/pci_ids.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 014dc2cfe4d6..253d7613b921 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -200,6 +200,7 @@ static struct pci_device_id tg3_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906M)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5784)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5764)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5723)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5761E)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index df948b44edad..4e10a074ca56 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1943,6 +1943,7 @@ #define PCI_DEVICE_ID_TIGON3_5720 0x1658 #define PCI_DEVICE_ID_TIGON3_5721 0x1659 #define PCI_DEVICE_ID_TIGON3_5722 0x165a +#define PCI_DEVICE_ID_TIGON3_5723 0x165b #define PCI_DEVICE_ID_TIGON3_5705M 0x165d #define PCI_DEVICE_ID_TIGON3_5705M_2 0x165e #define PCI_DEVICE_ID_TIGON3_5714 0x1668 From 9c8a620e7f85fff050a54697da44bbd1a66e8e0b Mon Sep 17 00:00:00 2001 From: Matt Carlson <mcarlson@broadcom.com> Date: Sun, 21 Oct 2007 16:16:08 -0700 Subject: [PATCH 18/30] [TG3]: Add management FW version to ethtool report This patch appends the management firmware version to the bootcode firmware string reported through ethtool. Signed-off-by: Matt Carlson <mcarlson@broadcom.com> Signed-off-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/tg3.c | 93 ++++++++++++++++++++++++++++++++++++++--------- drivers/net/tg3.h | 9 ++++- 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 253d7613b921..98f465828110 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -10821,9 +10821,24 @@ out_not_found: strcpy(tp->board_part_number, "none"); } +static int __devinit tg3_fw_img_is_valid(struct tg3 *tp, u32 offset) +{ + u32 val; + + if (tg3_nvram_read_swab(tp, offset, &val) || + (val & 0xfc000000) != 0x0c000000 || + tg3_nvram_read_swab(tp, offset + 4, &val) || + val != 0) + return 0; + + return 1; +} + static void __devinit tg3_read_fw_ver(struct tg3 *tp) { u32 val, offset, start; + u32 ver_offset; + int i, bcnt; if (tg3_nvram_read_swab(tp, 0, &val)) return; @@ -10836,29 +10851,71 @@ static void __devinit tg3_read_fw_ver(struct tg3 *tp) return; offset = tg3_nvram_logical_addr(tp, offset); - if (tg3_nvram_read_swab(tp, offset, &val)) + + if (!tg3_fw_img_is_valid(tp, offset) || + tg3_nvram_read_swab(tp, offset + 8, &ver_offset)) return; - if ((val & 0xfc000000) == 0x0c000000) { - u32 ver_offset, addr; - int i; - - if (tg3_nvram_read_swab(tp, offset + 4, &val) || - tg3_nvram_read_swab(tp, offset + 8, &ver_offset)) + offset = offset + ver_offset - start; + for (i = 0; i < 16; i += 4) { + if (tg3_nvram_read(tp, offset + i, &val)) return; - if (val != 0) - return; - - addr = offset + ver_offset - start; - for (i = 0; i < 16; i += 4) { - if (tg3_nvram_read(tp, addr + i, &val)) - return; - - val = cpu_to_le32(val); - memcpy(tp->fw_ver + i, &val, 4); - } + val = le32_to_cpu(val); + memcpy(tp->fw_ver + i, &val, 4); } + + if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF) || + (tp->tg3_flags & TG3_FLG3_ENABLE_APE)) + return; + + for (offset = TG3_NVM_DIR_START; + offset < TG3_NVM_DIR_END; + offset += TG3_NVM_DIRENT_SIZE) { + if (tg3_nvram_read_swab(tp, offset, &val)) + return; + + if ((val >> TG3_NVM_DIRTYPE_SHIFT) == TG3_NVM_DIRTYPE_ASFINI) + break; + } + + if (offset == TG3_NVM_DIR_END) + return; + + if (!(tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) + start = 0x08000000; + else if (tg3_nvram_read_swab(tp, offset - 4, &start)) + return; + + if (tg3_nvram_read_swab(tp, offset + 4, &offset) || + !tg3_fw_img_is_valid(tp, offset) || + tg3_nvram_read_swab(tp, offset + 8, &val)) + return; + + offset += val - start; + + bcnt = strlen(tp->fw_ver); + + tp->fw_ver[bcnt++] = ','; + tp->fw_ver[bcnt++] = ' '; + + for (i = 0; i < 4; i++) { + if (tg3_nvram_read(tp, offset, &val)) + return; + + val = le32_to_cpu(val); + offset += sizeof(val); + + if (bcnt > TG3_VER_SIZE - sizeof(val)) { + memcpy(&tp->fw_ver[bcnt], &val, TG3_VER_SIZE - bcnt); + break; + } + + memcpy(&tp->fw_ver[bcnt], &val, sizeof(val)); + bcnt += sizeof(val); + } + + tp->fw_ver[TG3_VER_SIZE - 1] = 0; } static struct pci_dev * __devinit tg3_find_peer(struct tg3 *); diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 6dbdad2b8f88..495a1dfaba5a 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -1540,6 +1540,12 @@ #define TG3_EEPROM_MAGIC_HW 0xabcd #define TG3_EEPROM_MAGIC_HW_MSK 0xffff +#define TG3_NVM_DIR_START 0x18 +#define TG3_NVM_DIR_END 0x78 +#define TG3_NVM_DIRENT_SIZE 0xc +#define TG3_NVM_DIRTYPE_SHIFT 24 +#define TG3_NVM_DIRTYPE_ASFINI 1 + /* 32K Window into NIC internal memory */ #define NIC_SRAM_WIN_BASE 0x00008000 @@ -2418,7 +2424,8 @@ struct tg3 { u32 pci_cmd; char board_part_number[24]; - char fw_ver[16]; +#define TG3_VER_SIZE 32 + char fw_ver[TG3_VER_SIZE]; u32 nic_sram_data_cfg; u32 pci_clock_ctrl; struct pci_dev *pdev_peer; From 8a6eac90e21633e054e17d21454a2c26824aeb18 Mon Sep 17 00:00:00 2001 From: Matt Carlson <mcarlson@broadcom.com> Date: Sun, 21 Oct 2007 16:17:55 -0700 Subject: [PATCH 19/30] [TG3]: PCI command adjustment This patch changes the way the driver works with the PCI command register. It adjusts the access size from dwords to words. This patch is done both as a PCI configuration space cleanup and as preparatory work for PCI error recovery. Signed-off-by: Matt Carlson <mcarlson@broadcom.com> Signed-off-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/tg3.c | 7 ++----- drivers/net/tg3.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 98f465828110..328eb4adc10b 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -5029,10 +5029,7 @@ static int tg3_poll_fw(struct tg3 *tp) /* Save PCI command register before chip reset */ static void tg3_save_pci_state(struct tg3 *tp) { - u32 val; - - pci_read_config_dword(tp->pdev, TG3PCI_COMMAND, &val); - tp->pci_cmd = val; + pci_read_config_word(tp->pdev, PCI_COMMAND, &tp->pci_cmd); } /* Restore PCI state after chip reset */ @@ -5055,7 +5052,7 @@ static void tg3_restore_pci_state(struct tg3 *tp) PCISTATE_ALLOW_APE_SHMEM_WR; pci_write_config_dword(tp->pdev, TG3PCI_PCISTATE, val); - pci_write_config_dword(tp->pdev, TG3PCI_COMMAND, tp->pci_cmd); + pci_write_config_word(tp->pdev, PCI_COMMAND, tp->pci_cmd); if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { pci_write_config_byte(tp->pdev, PCI_CACHE_LINE_SIZE, diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 495a1dfaba5a..1d5b2a3dd29d 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2421,7 +2421,7 @@ struct tg3 { #define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ u32 led_ctrl; - u32 pci_cmd; + u16 pci_cmd; char board_part_number[24]; #define TG3_VER_SIZE 32 From 33b0c4fe6d0dd19fc7c9b801855f55c5260f2858 Mon Sep 17 00:00:00 2001 From: Matt Carlson <mcarlson@broadcom.com> Date: Sun, 21 Oct 2007 16:22:38 -0700 Subject: [PATCH 20/30] [TG3]: Update version to 3.85 This patch updates the version number to 3.85. Signed-off-by: Matt Carlson <mcarlson@broadcom.com> Signed-off-by: Michael Chan <mchan@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/tg3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 328eb4adc10b..09440d783e65 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -64,8 +64,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.84" -#define DRV_MODULE_RELDATE "October 12, 2007" +#define DRV_MODULE_VERSION "3.85" +#define DRV_MODULE_RELDATE "October 18, 2007" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 From bfb85c9f753a7172bd962e8717118191dfd612cc Mon Sep 17 00:00:00 2001 From: Randy Dunlap <randy.dunlap@oracle.com> Date: Sun, 21 Oct 2007 16:24:27 -0700 Subject: [PATCH 21/30] [ATM]: Fix clip module reload crash. net/atm/clip.c crashes the kernel if it (module) is loaded, removed, and then loaded again. Its exit call to neigh_table_clear() should destroy the cache after freeing it. Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net> --- net/core/neighbour.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 67ba9914e52e..05979e356963 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1438,6 +1438,9 @@ int neigh_table_clear(struct neigh_table *tbl) free_percpu(tbl->stats); tbl->stats = NULL; + kmem_cache_destroy(tbl->kmem_cachep); + tbl->kmem_cachep = NULL; + return 0; } From deea84b0ae3d26b41502ae0a39fe7fe134e703d0 Mon Sep 17 00:00:00 2001 From: Herbert Xu <herbert@gondor.apana.org.au> Date: Sun, 21 Oct 2007 16:27:46 -0700 Subject: [PATCH 22/30] [NET]: Fix SKB_WITH_OVERHEAD calculation The calculation in SKB_WITH_OVERHEAD is incorrect in that it can cause an overflow across a page boundary which is what it's meant to prevent. In particular, the header length (X) should not be lumped together with skb_shared_info. The latter needs to be aligned properly while the header has no choice but to sit in front of wherever the payload is. Therefore the correct calculation is to take away the aligned size of skb_shared_info, and then subtract the header length. The resulting quantity L satisfies the following inequality: SKB_DATA_ALIGN(L + X) + sizeof(struct skb_shared_info) <= PAGE_SIZE This is the quantity used by alloc_skb to do the actual allocation. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net> --- include/linux/skbuff.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f93f22b3d2ff..369f60a4797d 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -41,8 +41,7 @@ #define SKB_DATA_ALIGN(X) (((X) + (SMP_CACHE_BYTES - 1)) & \ ~(SMP_CACHE_BYTES - 1)) #define SKB_WITH_OVERHEAD(X) \ - (((X) - sizeof(struct skb_shared_info)) & \ - ~(SMP_CACHE_BYTES - 1)) + ((X) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) #define SKB_MAX_ORDER(X, ORDER) \ SKB_WITH_OVERHEAD((PAGE_SIZE << (ORDER)) - (X)) #define SKB_MAX_HEAD(X) (SKB_MAX_ORDER((X), 0)) From 81429973cfff7745792c877dd083eec29724ec97 Mon Sep 17 00:00:00 2001 From: Olof Johansson <olof@lixom.net> Date: Sun, 21 Oct 2007 16:32:58 -0700 Subject: [PATCH 23/30] [NIU]: Cleanup PAGE_SIZE checks a bit I get the following warning from a powerpc allyesconfig of current mainline: drivers/net/niu.c: In function 'niu_size_rbr': drivers/net/niu.c:3113: warning: large integer implicitly truncated to unsigned type PAGE_SIZE in this case is 64KB, so I don't quite get why gcc can't tell that the line in question will never be reached. I suggest the following instead, but I can unfortunately not do anything but build test it. Also, the driver does some other checks to make sure that PAGE_SIZE is a power of two (BUILD_BUG_ON() in niu_init()), doesn't seem like that could ever be untrue? Or are there really archs with non-power-of-two PAGE_SIZE? Signed-off-by: Olof Johansson <olof@lixom.net> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/niu.c | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/drivers/net/niu.c b/drivers/net/niu.c index ed1f9bbb2a32..112ab079ce7d 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -3103,31 +3103,12 @@ static int niu_alloc_tx_ring_info(struct niu *np, static void niu_size_rbr(struct niu *np, struct rx_ring_info *rp) { - u16 bs; + u16 bss; - switch (PAGE_SIZE) { - case 4 * 1024: - case 8 * 1024: - case 16 * 1024: - case 32 * 1024: - rp->rbr_block_size = PAGE_SIZE; - rp->rbr_blocks_per_page = 1; - break; + bss = min(PAGE_SHIFT, 15); - default: - if (PAGE_SIZE % (32 * 1024) == 0) - bs = 32 * 1024; - else if (PAGE_SIZE % (16 * 1024) == 0) - bs = 16 * 1024; - else if (PAGE_SIZE % (8 * 1024) == 0) - bs = 8 * 1024; - else if (PAGE_SIZE % (4 * 1024) == 0) - bs = 4 * 1024; - else - BUG(); - rp->rbr_block_size = bs; - rp->rbr_blocks_per_page = PAGE_SIZE / bs; - } + rp->rbr_block_size = 1 << bss; + rp->rbr_blocks_per_page = 1 << (PAGE_SHIFT-bss); rp->rbr_sizes[0] = 256; rp->rbr_sizes[1] = 1024; @@ -7902,12 +7883,7 @@ static int __init niu_init(void) { int err = 0; - BUILD_BUG_ON((PAGE_SIZE < 4 * 1024) || - ((PAGE_SIZE > 32 * 1024) && - ((PAGE_SIZE % (32 * 1024)) != 0 && - (PAGE_SIZE % (16 * 1024)) != 0 && - (PAGE_SIZE % (8 * 1024)) != 0 && - (PAGE_SIZE % (4 * 1024)) != 0))); + BUILD_BUG_ON(PAGE_SIZE < 4 * 1024); niu_debug = netif_msg_init(debug, NIU_MSG_DEFAULT); From 305e1e96911417d8cda2699f6a20a6f434616a8c Mon Sep 17 00:00:00 2001 From: Jean Delvare <jdelvare@suse.de> Date: Sun, 21 Oct 2007 16:44:04 -0700 Subject: [PATCH 24/30] [INET]: Let inet_diag and friends autoload By adding module aliases to inet_diag, tcp_diag and dccp_diag, we let them load automatically as needed. This makes tools like "ss" run faster. Signed-off-by: Jean Delvare <jdelvare@suse.de> Signed-off-by: David S. Miller <davem@davemloft.net> --- include/linux/net.h | 4 ++++ net/dccp/diag.c | 1 + net/ipv4/inet_diag.c | 7 +++++++ net/ipv4/tcp_diag.c | 1 + 4 files changed, 13 insertions(+) diff --git a/include/linux/net.h b/include/linux/net.h index c136abce7ef6..dd79cdb8c4cf 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -313,6 +313,10 @@ static const struct proto_ops name##_ops = { \ #define MODULE_ALIAS_NET_PF_PROTO(pf, proto) \ MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto)) +#define MODULE_ALIAS_NET_PF_PROTO_TYPE(pf, proto, type) \ + MODULE_ALIAS("net-pf-" __stringify(pf) "-proto-" __stringify(proto) \ + "-type-" __stringify(type)) + #ifdef CONFIG_SYSCTL #include <linux/sysctl.h> extern ctl_table net_table[]; diff --git a/net/dccp/diag.c b/net/dccp/diag.c index 0f3745585a94..d8a3509b26f6 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -68,3 +68,4 @@ module_exit(dccp_diag_fini); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); MODULE_DESCRIPTION("DCCP inet_diag handler"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_INET_DIAG, DCCPDIAG_GETSOCK); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 7eb83ebed2ec..dc429b6b0ba6 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -815,6 +815,12 @@ static int inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) nlmsg_len(nlh) < hdrlen) return -EINVAL; +#ifdef CONFIG_KMOD + if (inet_diag_table[nlh->nlmsg_type] == NULL) + request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK, + NETLINK_INET_DIAG, nlh->nlmsg_type); +#endif + if (inet_diag_table[nlh->nlmsg_type] == NULL) return -ENOENT; @@ -914,3 +920,4 @@ static void __exit inet_diag_exit(void) module_init(inet_diag_init); module_exit(inet_diag_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_INET_DIAG); diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 3904d2158a92..2fbcc7d1b1a0 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -56,3 +56,4 @@ static void __exit tcp_diag_exit(void) module_init(tcp_diag_init); module_exit(tcp_diag_exit); MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_INET_DIAG, TCPDIAG_GETSOCK); From 7131c6c73656b92aea806c6e688e97aa49ff911e Mon Sep 17 00:00:00 2001 From: Jean Delvare <jdelvare@suse.de> Date: Sun, 21 Oct 2007 16:45:03 -0700 Subject: [PATCH 25/30] [INET]: Use MODULE_ALIAS_NET_PF_PROTO_TYPE where possible. Now that we have this new macro, use it where possible. Signed-off-by: Jean Delvare <jdelvare@suse.de> Signed-off-by: David S. Miller <davem@davemloft.net> --- net/dccp/ipv4.c | 4 ++-- net/dccp/ipv6.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 44f6e17e105f..222549ab274a 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -1037,8 +1037,8 @@ module_exit(dccp_v4_exit); * values directly, Also cover the case where the protocol is not specified, * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP */ -MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6"); -MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 33, 6); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 0, 6); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol"); diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index cac53548c2d8..bbadd6681b83 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -1219,8 +1219,8 @@ module_exit(dccp_v6_exit); * values directly, Also cover the case where the protocol is not specified, * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP */ -MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-33-type-6"); -MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-0-type-6"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 33, 6); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 0, 6); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>"); MODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol"); From dfa4091129019959f4608756f76dc687495287ad Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov <xemul@openvz.org> Date: Sun, 21 Oct 2007 16:57:55 -0700 Subject: [PATCH 26/30] [NET]: Use the skb_set_queue_mapping where appropriate There's already such a helper to initialize this field. Use it. Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- net/core/dev.c | 2 +- net/core/pktgen.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 38b03da5c1ca..1672cc134853 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1661,7 +1661,7 @@ gso: q = dev->qdisc; if (q->enqueue) { /* reset queue_mapping to zero */ - skb->queue_mapping = 0; + skb_set_queue_mapping(skb, 0); rc = q->enqueue(skb, q); qdisc_run(dev); spin_unlock(&dev->queue_lock); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index c4719edb55c0..b78235e5a7f4 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2603,8 +2603,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev, skb->network_header = skb->tail; skb->transport_header = skb->network_header + sizeof(struct iphdr); skb_put(skb, sizeof(struct iphdr) + sizeof(struct udphdr)); - skb->queue_mapping = pkt_dev->cur_queue_map; - + skb_set_queue_mapping(skb, pkt_dev->cur_queue_map); iph = ip_hdr(skb); udph = udp_hdr(skb); @@ -2941,8 +2940,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev, skb->network_header = skb->tail; skb->transport_header = skb->network_header + sizeof(struct ipv6hdr); skb_put(skb, sizeof(struct ipv6hdr) + sizeof(struct udphdr)); - skb->queue_mapping = pkt_dev->cur_queue_map; - + skb_set_queue_mapping(skb, pkt_dev->cur_queue_map); iph = ipv6_hdr(skb); udph = udp_hdr(skb); From 4e3ab47a547616e583c7a5458beced6aa34c8ef3 Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov <xemul@openvz.org> Date: Sun, 21 Oct 2007 17:01:29 -0700 Subject: [PATCH 27/30] [NET]: Make and use skb_get_queue_mapping Make the helper for getting the field, symmetrical to the "set" one. Return 0 if CONFIG_NETDEVICES_MULTIQUEUE=n Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- drivers/net/cpmac.c | 2 +- include/linux/skbuff.h | 9 +++++++++ net/sched/sch_teql.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/cpmac.c b/drivers/net/cpmac.c index ed53aaab4c02..ae419736158e 100644 --- a/drivers/net/cpmac.c +++ b/drivers/net/cpmac.c @@ -471,7 +471,7 @@ static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev) } len = max(skb->len, ETH_ZLEN); - queue = skb->queue_mapping; + queue = skb_get_queue_mapping(skb); #ifdef CONFIG_NETDEVICES_MULTIQUEUE netif_stop_subqueue(dev, queue); #else diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 369f60a4797d..ecb0edef0b39 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1769,6 +1769,15 @@ static inline void skb_set_queue_mapping(struct sk_buff *skb, u16 queue_mapping) #endif } +static inline u16 skb_get_queue_mapping(struct sk_buff *skb) +{ +#ifdef CONFIG_NETDEVICES_MULTIQUEUE + return skb->queue_mapping; +#else + return 0; +#endif +} + static inline void skb_copy_queue_mapping(struct sk_buff *to, const struct sk_buff *from) { #ifdef CONFIG_NETDEVICES_MULTIQUEUE diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index be57cf317a7f..a9fad7162b5d 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -266,7 +266,7 @@ static int teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int busy; int nores; int len = skb->len; - int subq = skb->queue_mapping; + int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; start = master->slaves; From 668f895a85b0c3a62a690425145f13dabebebd7a Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov <xemul@openvz.org> Date: Sun, 21 Oct 2007 17:01:56 -0700 Subject: [PATCH 28/30] [NET]: Hide the queue_mapping field inside netif_subqueue_stopped Many places get the queue_mapping field from skb to pass it to the netif_subqueue_stopped() which will be 0 in any case. Make the helper that works with sk_buff Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- include/linux/netdevice.h | 7 ++++++- net/core/dev.c | 4 ++-- net/core/netpoll.c | 4 ++-- net/core/pktgen.c | 6 +++--- net/sched/sch_teql.c | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6f85db3535e2..4a3f54e358e5 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -996,7 +996,7 @@ static inline void netif_stop_subqueue(struct net_device *dev, u16 queue_index) * * Check individual transmit queue of a device with multiple transmit queues. */ -static inline int netif_subqueue_stopped(const struct net_device *dev, +static inline int __netif_subqueue_stopped(const struct net_device *dev, u16 queue_index) { #ifdef CONFIG_NETDEVICES_MULTIQUEUE @@ -1007,6 +1007,11 @@ static inline int netif_subqueue_stopped(const struct net_device *dev, #endif } +static inline int netif_subqueue_stopped(const struct net_device *dev, + struct sk_buff *skb) +{ + return __netif_subqueue_stopped(dev, skb_get_queue_mapping(skb)); +} /** * netif_wake_subqueue - allow sending packets on subqueue diff --git a/net/core/dev.c b/net/core/dev.c index 1672cc134853..872658927e47 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1553,7 +1553,7 @@ gso: return rc; } if (unlikely((netif_queue_stopped(dev) || - netif_subqueue_stopped(dev, skb->queue_mapping)) && + netif_subqueue_stopped(dev, skb)) && skb->next)) return NETDEV_TX_BUSY; } while (skb->next); @@ -1692,7 +1692,7 @@ gso: HARD_TX_LOCK(dev, cpu); if (!netif_queue_stopped(dev) && - !netif_subqueue_stopped(dev, skb->queue_mapping)) { + !netif_subqueue_stopped(dev, skb)) { rc = 0; if (!dev_hard_start_xmit(skb, dev)) { HARD_TX_UNLOCK(dev); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 95daba624967..bf8d18f1b013 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -67,7 +67,7 @@ static void queue_process(struct work_struct *work) local_irq_save(flags); netif_tx_lock(dev); if ((netif_queue_stopped(dev) || - netif_subqueue_stopped(dev, skb->queue_mapping)) || + netif_subqueue_stopped(dev, skb)) || dev->hard_start_xmit(skb, dev) != NETDEV_TX_OK) { skb_queue_head(&npinfo->txq, skb); netif_tx_unlock(dev); @@ -269,7 +269,7 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb) tries > 0; --tries) { if (netif_tx_trylock(dev)) { if (!netif_queue_stopped(dev) && - !netif_subqueue_stopped(dev, skb->queue_mapping)) + !netif_subqueue_stopped(dev, skb)) status = dev->hard_start_xmit(skb, dev); netif_tx_unlock(dev); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index b78235e5a7f4..de33f36947e9 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3383,7 +3383,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) if ((netif_queue_stopped(odev) || (pkt_dev->skb && - netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping))) || + netif_subqueue_stopped(odev, pkt_dev->skb))) || need_resched()) { idle_start = getCurUs(); @@ -3400,7 +3400,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) pkt_dev->idle_acc += getCurUs() - idle_start; if (netif_queue_stopped(odev) || - netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) { + netif_subqueue_stopped(odev, pkt_dev->skb)) { pkt_dev->next_tx_us = getCurUs(); /* TODO */ pkt_dev->next_tx_ns = 0; goto out; /* Try the next interface */ @@ -3429,7 +3429,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev) netif_tx_lock_bh(odev); if (!netif_queue_stopped(odev) && - !netif_subqueue_stopped(odev, pkt_dev->skb->queue_mapping)) { + !netif_subqueue_stopped(odev, pkt_dev->skb)) { atomic_inc(&(pkt_dev->skb->users)); retry_now: diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index a9fad7162b5d..421281d9dd1d 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -284,7 +284,7 @@ restart: if (slave->qdisc_sleeping != q) continue; if (netif_queue_stopped(slave) || - netif_subqueue_stopped(slave, subq) || + __netif_subqueue_stopped(slave, subq) || !netif_running(slave)) { busy = 1; continue; @@ -294,7 +294,7 @@ restart: case 0: if (netif_tx_trylock(slave)) { if (!netif_queue_stopped(slave) && - !netif_subqueue_stopped(slave, subq) && + !__netif_subqueue_stopped(slave, subq) && slave->hard_start_xmit(skb, slave) == 0) { netif_tx_unlock(slave); master->slaves = NEXT_SLAVE(q); From e3fa259bcbbca25c8e8275c8dcedcf484854465b Mon Sep 17 00:00:00 2001 From: Pavel Emelyanov <xemul@openvz.org> Date: Sun, 21 Oct 2007 17:02:30 -0700 Subject: [PATCH 29/30] [NET]: Cut off the queue_mapping field from sk_buff Just hide it behind the #ifdef, because nobody wants it now. Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- include/linux/skbuff.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ecb0edef0b39..fd4e12f24270 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -300,8 +300,9 @@ struct sk_buff { #endif int iif; +#ifdef CONFIG_NETDEVICES_MULTIQUEUE __u16 queue_mapping; - +#endif #ifdef CONFIG_NET_SCHED __u16 tc_index; /* traffic control index */ #ifdef CONFIG_NET_CLS_ACT From ea2c47b42f12dadbad9d879fb6df102b9003ab82 Mon Sep 17 00:00:00 2001 From: Masahide NAKAMURA <nakam@linux-ipv6.org> Date: Mon, 22 Oct 2007 02:30:15 -0700 Subject: [PATCH 30/30] [IPSEC] IPV6: Fix to add tunnel mode SA correctly. Signed-off-by: Masahide NAKAMURA <nakam@linux-ipv6.org> Signed-off-by: David S. Miller <davem@davemloft.net> --- net/ipv6/ah6.c | 1 + net/ipv6/esp6.c | 1 + 2 files changed, 2 insertions(+) diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 67cd06613a25..66a9139d46e9 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -483,6 +483,7 @@ static int ah6_init_state(struct xfrm_state *x) break; case XFRM_MODE_TUNNEL: x->props.header_len += sizeof(struct ipv6hdr); + break; default: goto error; } diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index b0715432e454..72a659806cad 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -360,6 +360,7 @@ static int esp6_init_state(struct xfrm_state *x) break; case XFRM_MODE_TUNNEL: x->props.header_len += sizeof(struct ipv6hdr); + break; default: goto error; }