static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; struct hci_dev *hdev; struct sk_buff *skb; int err; BT_DBG("sock %p sk %p", sock, sk); if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE)) return -EINVAL; if (len < 4) return -EINVAL; lock_sock(sk); if (!(hdev = hci_pi(sk)->hdev)) { err = -EBADFD; goto done; } if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) goto done; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { err = -EFAULT; goto drop; } skb->pkt_type = *((unsigned char *) skb->data); skb_pull(skb, 1); skb->dev = (void *) hdev; if (skb->pkt_type == HCI_COMMAND_PKT) { u16 opcode = __le16_to_cpu(get_unaligned((u16 *)skb->data)); u16 ogf = cmd_opcode_ogf(opcode); u16 ocf = cmd_opcode_ocf(opcode); if (((ogf > HCI_SFLT_MAX_OGF) || !hci_test_bit(ocf & HCI_FLT_OCF_BITS, &hci_sec_filter.ocf_mask[ogf])) && !capable(CAP_NET_RAW)) { err = -EPERM; goto drop; } if (test_bit(HCI_RAW, &hdev->flags) || (ogf == OGF_VENDOR_CMD)) { skb_queue_tail(&hdev->raw_q, skb); hci_sched_tx(hdev); } else { skb_queue_tail(&hdev->cmd_q, skb); hci_sched_cmd(hdev); } } else { if (!capable(CAP_NET_RAW)) { err = -EPERM; goto drop; } skb_queue_tail(&hdev->raw_q, skb); hci_sched_tx(hdev); } err = len; done: release_sock(sk); return err; drop: kfree_skb(skb); goto done; }
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_AUTH_REQUESTED: hci_cs_auth_requested(hdev, ev->status); break; case HCI_OP_SET_CONN_ENCRYPT: hci_cs_set_conn_encrypt(hdev, ev->status); break; case HCI_OP_REMOTE_NAME_REQ: hci_cs_remote_name_req(hdev, ev->status); break; case HCI_OP_READ_REMOTE_FEATURES: hci_cs_read_remote_features(hdev, ev->status); break; case HCI_OP_READ_REMOTE_EXT_FEATURES: hci_cs_read_remote_ext_features(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); } }
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; 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; case HCI_EV_INQUIRY_COMPLETE: hci_inquiry_complete_evt(hdev, skb); break; case HCI_EV_INQUIRY_RESULT: 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); 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_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); break; case HCI_EV_PIN_CODE_REQ: hci_pin_code_request_evt(hdev, skb); break; case HCI_EV_LINK_KEY_REQ: hci_link_key_request_evt(hdev, skb); break; case HCI_EV_LINK_KEY_NOTIFY: 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; case HCI_EV_PSCAN_REP_MODE: hci_pscan_rep_mode_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); } 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); } break; } kfree_skb(skb); hdev->stat.evt_rx++; }
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_READ_LINK_POLICY: hci_cc_read_link_policy(hdev, skb); break; case HCI_OP_WRITE_LINK_POLICY: hci_cc_write_link_policy(hdev, skb); break; case HCI_OP_READ_DEF_LINK_POLICY: hci_cc_read_def_link_policy(hdev, skb); break; case HCI_OP_WRITE_DEF_LINK_POLICY: hci_cc_write_def_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_SSP_MODE: hci_cc_read_ssp_mode(hdev, skb); break; case HCI_OP_WRITE_SSP_MODE: hci_cc_write_ssp_mode(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); } }
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { hci_event_hdr *he = (hci_event_hdr *) skb->data; evt_cmd_status *cs; evt_cmd_complete *ec; __u16 opcode, ocf, ogf; skb_pull(skb, HCI_EVENT_HDR_SIZE); BT_DBG("%s evt 0x%x", hdev->name, he->evt); switch (he->evt) { case EVT_NUM_COMP_PKTS: hci_num_comp_pkts_evt(hdev, skb); break; case EVT_INQUIRY_COMPLETE: hci_inquiry_complete_evt(hdev, skb); break; case EVT_INQUIRY_RESULT: hci_inquiry_result_evt(hdev, skb); break; case EVT_INQUIRY_RESULT_WITH_RSSI: hci_inquiry_result_with_rssi_evt(hdev, skb); break; case EVT_CONN_REQUEST: hci_conn_request_evt(hdev, skb); break; case EVT_CONN_COMPLETE: hci_conn_complete_evt(hdev, skb); break; case EVT_DISCONN_COMPLETE: hci_disconn_complete_evt(hdev, skb); break; case EVT_ROLE_CHANGE: hci_role_change_evt(hdev, skb); break; case EVT_AUTH_COMPLETE: hci_auth_complete_evt(hdev, skb); break; case EVT_ENCRYPT_CHANGE: hci_encrypt_change_evt(hdev, skb); break; case EVT_CMD_STATUS: cs = (evt_cmd_status *) skb->data; skb_pull(skb, EVT_CMD_STATUS_SIZE); opcode = __le16_to_cpu(cs->opcode); ogf = cmd_opcode_ogf(opcode); ocf = cmd_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); } break; case EVT_CMD_COMPLETE: ec = (evt_cmd_complete *) skb->data; skb_pull(skb, EVT_CMD_COMPLETE_SIZE); opcode = __le16_to_cpu(ec->opcode); ogf = cmd_opcode_ogf(opcode); ocf = cmd_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); } break; }; kfree_skb(skb); hdev->stat.evt_rx++; }