static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) { __u32 mask = hci_pi(sk)->cmsg_mask; if (mask & HCI_CMSG_DIR) put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming); if (mask & HCI_CMSG_TSTAMP) put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp); }
int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; int len, opt; if (get_user(len, optlen)) return -EFAULT; switch (optname) { case HCI_DATA_DIR: if (hci_pi(sk)->cmsg_mask & HCI_CMSG_DIR) opt = 1; else opt = 0; if (put_user(opt, optval)) return -EFAULT; break; case HCI_TIME_STAMP: if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP) opt = 1; else opt = 0; if (put_user(opt, optval)) return -EFAULT; break; case HCI_FILTER: len = MIN(len, sizeof(struct hci_filter)); if (copy_to_user(optval, &hci_pi(sk)->filter, len)) return -EFAULT; break; default: return -ENOPROTOOPT; break; }; return 0; }
/* Send frame to RAW socket */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) { struct sock * sk; DBG("hdev %p len %d", hdev, skb->len); read_lock(&hci_sk_list.lock); for (sk = hci_sk_list.head; sk; sk = sk->next) { struct hci_filter *flt; struct sk_buff *nskb; if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev) continue; /* Don't send frame to the socket it came from */ if (skb->sk == sk) continue; /* Apply filter */ flt = &hci_pi(sk)->filter; if (!test_bit(skb->pkt_type, &flt->type_mask)) continue; if (skb->pkt_type == HCI_EVENT_PKT) { register int evt = (*(__u8 *)skb->data & 63); if (!test_bit(evt, &flt->event_mask)) continue; } if (!(nskb = skb_clone(skb, GFP_ATOMIC))) continue; /* Put type byte before the data */ memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1); skb_queue_tail(&sk->receive_queue, nskb); sk->data_ready(sk, nskb->len); } read_unlock(&hci_sk_list.lock); }
static struct sock *hci_sock_lookup(struct hci_dev *hdev) { struct sock *sk; read_lock(&hci_sk_list.lock); for (sk = hci_sk_list.head; sk; sk = sk->next) { if (hci_pi(sk)->hdev == hdev) break; } read_unlock(&hci_sk_list.lock); return sk; }
static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer) { struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; struct sock *sk = sock->sk; DBG("sock %p sk %p", sock, sk); *addr_len = sizeof(*haddr); haddr->hci_family = AF_BLUETOOTH; haddr->hci_dev = hci_pi(sk)->hdev->id; return 0; }
int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len) { struct sock *sk = sock->sk; struct hci_filter flt; int err = 0, opt = 0; DBG("sk %p, opt %d", sk, optname); lock_sock(sk); switch (optname) { case HCI_DATA_DIR: if (get_user(opt, (int *)optval)) return -EFAULT; if (opt) hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR; else hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR; break; case HCI_FILTER: len = MIN(len, sizeof(struct hci_filter)); if (copy_from_user(&flt, optval, len)) { err = -EFAULT; break; } memcpy(&hci_pi(sk)->filter, &flt, len); break; default: err = -ENOPROTOOPT; break; }; release_sock(sk); return err; }
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 = hci_pi(sk)->hdev; struct sk_buff *skb; int err; 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 (!hdev) return -EBADFD; if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err))) return err; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { kfree_skb(skb); return -EFAULT; } skb->dev = (void *) hdev; skb->pkt_type = *((unsigned char *) skb->data); skb_pull(skb, 1); /* Send frame to HCI core */ hci_send_raw(skb); return len; }
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 int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; struct hci_dev *hdev = hci_pi(sk)->hdev; __u32 mode; DBG("cmd %x arg %lx", cmd, arg); switch (cmd) { case HCIGETINFO: return hci_dev_info(arg); case HCIGETDEVLIST: return hci_dev_list(arg); case HCIDEVUP: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_open(arg); case HCIDEVDOWN: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_close(arg); case HCIDEVRESET: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_reset(arg); case HCIRESETSTAT: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_reset_stat(arg); case HCISETSCAN: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_setscan(arg); case HCISETAUTH: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_setauth(arg); case HCISETRAW: if (!capable(CAP_NET_ADMIN)) return -EACCES; if (!hdev) return -EBADFD; if (arg) mode = HCI_RAW; else mode = HCI_NORMAL; return hci_dev_setmode(hdev, mode); case HCISETPTYPE: if (!capable(CAP_NET_ADMIN)) return -EACCES; return hci_dev_setptype(arg); case HCIINQUIRY: return hci_inquiry(arg); case HCIGETCONNLIST: return hci_conn_list(arg); default: return -EINVAL; }; }
int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len) { struct sock *sk = sock->sk; struct hci_filter flt = { opcode: 0 }; int err = 0, opt = 0; BT_DBG("sk %p, opt %d", sk, optname); lock_sock(sk); switch (optname) { case HCI_DATA_DIR: if (get_user(opt, (int *)optval)) { err = -EFAULT; break; } if (opt) hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR; else hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR; break; case HCI_TIME_STAMP: if (get_user(opt, (int *)optval)) { err = -EFAULT; break; } if (opt) hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP; else hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP; break; case HCI_FILTER: memcpy(&flt, &hci_pi(sk)->filter, len); len = MIN(len, sizeof(struct hci_filter)); if (copy_from_user(&flt, optval, len)) { err = -EFAULT; break; } if (!capable(CAP_NET_RAW)) { flt.type_mask &= hci_sec_filter.type_mask; flt.event_mask[0] &= hci_sec_filter.event_mask[0]; flt.event_mask[1] &= hci_sec_filter.event_mask[1]; } memcpy(&hci_pi(sk)->filter, &flt, len); break; default: err = -ENOPROTOOPT; break; }; release_sock(sk); return err; }
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); if (!capable(CAP_NET_RAW)) { err = -EPERM; if (skb->pkt_type == HCI_COMMAND_PKT) { __u16 opcode = __le16_to_cpu(*(__u16 *)skb->data); __u16 ogf = cmd_opcode_ogf(opcode) - 1; __u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS; if (ogf > HCI_SFLT_MAX_OGF || !hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf])) goto drop; } else goto drop; } /* Send frame to HCI core */ skb->dev = (void *) hdev; hci_send_raw(skb); err = len; done: release_sock(sk); return err; drop: kfree_skb(skb); goto done; }