/* HCI callback list */
LIST_HEAD(hci_cb_list);
- DEFINE_RWLOCK(hci_cb_list_lock);
+ DEFINE_MUTEX(hci_cb_list_lock);
/* HCI ID Numbering */
static DEFINE_IDA(hci_index_ida);
hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL);
}
- static void amp_init(struct hci_request *req)
+ static void amp_init1(struct hci_request *req)
{
req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED;
/* Read Local Supported Commands */
hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
- /* Read Local Supported Features */
- hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
-
/* Read Local AMP Info */
hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL);
}
+ static void amp_init2(struct hci_request *req)
+ {
+ /* Read Local Supported Features. Not all AMP controllers
+ * support this so it's placed conditionally in the second
+ * stage init.
+ */
+ if (req->hdev->commands[14] & 0x20)
+ hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
+ }
+
static void hci_init1_req(struct hci_request *req, unsigned long opt)
{
struct hci_dev *hdev = req->hdev;
break;
case HCI_AMP:
- amp_init(req);
+ amp_init1(req);
break;
default:
{
struct hci_dev *hdev = req->hdev;
+ if (hdev->dev_type == HCI_AMP)
+ return amp_init2(req);
+
if (lmp_bredr_capable(hdev))
bredr_setup(req);
else
&dut_mode_fops);
}
+ err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
+ if (err < 0)
+ return err;
+
/* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
* BR/EDR/LE type controllers. AMP controllers only need the
- * first stage init.
+ * first two stages of init.
*/
if (hdev->dev_type != HCI_BREDR)
return 0;
- err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
- if (err < 0)
- return err;
-
err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT);
if (err < 0)
return err;
{
BT_DBG("%s %p", hdev->name, hdev);
+ if (!test_bit(HCI_UNREGISTER, &hdev->dev_flags)) {
+ /* Execute vendor specific shutdown routine */
+ if (hdev->shutdown)
+ hdev->shutdown(hdev);
+ }
+
cancel_delayed_work(&hdev->power_off);
hci_req_cancel(hdev, ENODEV);
{
BT_DBG("%p name %s", cb, cb->name);
- write_lock(&hci_cb_list_lock);
- list_add(&cb->list, &hci_cb_list);
- write_unlock(&hci_cb_list_lock);
+ mutex_lock(&hci_cb_list_lock);
+ list_add_tail(&cb->list, &hci_cb_list);
+ mutex_unlock(&hci_cb_list_lock);
return 0;
}
{
BT_DBG("%p name %s", cb, cb->name);
- write_lock(&hci_cb_list_lock);
+ mutex_lock(&hci_cb_list_lock);
list_del(&cb->list);
- write_unlock(&hci_cb_list_lock);
+ mutex_unlock(&hci_cb_list_lock);
return 0;
}
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->req.start = true;
+ bt_cb(skb)->req_start = 1;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
if (!skb)
return true;
- return bt_cb(skb)->req.start;
+ return bt_cb(skb)->req_start;
}
static void hci_resend_last(struct hci_dev *hdev)
* command queue (hdev->cmd_q).
*/
if (hdev->sent_cmd) {
- req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+ req_complete = bt_cb(hdev->sent_cmd)->req_complete;
if (req_complete) {
/* We must set the complete callback to NULL to
* avoid calling the callback more than once if
* this function gets called again.
*/
- bt_cb(hdev->sent_cmd)->req.complete = NULL;
+ bt_cb(hdev->sent_cmd)->req_complete = NULL;
goto call_complete;
}
/* Remove all pending commands belonging to this request */
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
- if (bt_cb(skb)->req.start) {
+ if (bt_cb(skb)->req_start) {
__skb_queue_head(&hdev->cmd_q, skb);
break;
}
- req_complete = bt_cb(skb)->req.complete;
+ req_complete = bt_cb(skb)->req_complete;
kfree_skb(skb);
}
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
if (conn && conn->state == BT_CONNECT) {
if (status != 0x0c || conn->attempt > 2) {
conn->state = BT_CLOSED;
- hci_proto_connect_cfm(conn, status);
+ hci_connect_cfm(conn, status);
hci_conn_del(conn);
} else
conn->state = BT_CONNECT2;
if (sco) {
sco->state = BT_CLOSED;
- hci_proto_connect_cfm(sco, status);
+ hci_connect_cfm(sco, status);
hci_conn_del(sco);
}
}
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
if (conn->state == BT_CONFIG) {
- hci_proto_connect_cfm(conn, status);
+ hci_connect_cfm(conn, status);
hci_conn_drop(conn);
}
}
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
if (conn->state == BT_CONFIG) {
- hci_proto_connect_cfm(conn, status);
+ hci_connect_cfm(conn, status);
hci_conn_drop(conn);
}
}
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
if (conn->state == BT_CONFIG) {
- hci_proto_connect_cfm(conn, status);
+ hci_connect_cfm(conn, status);
hci_conn_drop(conn);
}
}
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
if (conn) {
if (conn->state == BT_CONFIG) {
- hci_proto_connect_cfm(conn, status);
+ hci_connect_cfm(conn, status);
hci_conn_drop(conn);
}
}
if (sco) {
sco->state = BT_CLOSED;
- hci_proto_connect_cfm(sco, status);
+ hci_connect_cfm(sco, status);
hci_conn_del(sco);
}
}
hci_sco_setup(conn, ev->status);
if (ev->status) {
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
hci_conn_del(conn);
} else if (ev->link_type != ACL_LINK)
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
unlock:
hci_dev_unlock(hdev);
&cp);
} else {
conn->state = BT_CONNECT2;
- hci_proto_connect_cfm(conn, 0);
+ hci_connect_cfm(conn, 0);
}
}
type = conn->type;
- hci_proto_disconn_cfm(conn, ev->reason);
+ hci_disconn_cfm(conn, ev->reason);
hci_conn_del(conn);
/* Re-enable advertising if necessary, since it might
&cp);
} else {
conn->state = BT_CONNECTED;
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn);
}
} else {
if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) &&
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
- hci_proto_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
+ hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
hci_conn_drop(conn);
goto unlock;
}
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn);
} else
hci_encrypt_cfm(conn, ev->status, ev->encrypt);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn);
}
cancel_delayed_work(&hdev->cmd_timer);
if (ev->status ||
- (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
+ (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req_event))
hci_req_cmd_complete(hdev, opcode, ev->status);
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn);
}
break;
}
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
if (ev->status)
hci_conn_del(conn);
if (!ev->status)
conn->state = BT_CONNECTED;
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
hci_conn_drop(conn);
} else {
hci_auth_cfm(conn, ev->status);
hci_debugfs_create_conn(conn);
hci_conn_add_sysfs(conn);
- hci_proto_connect_cfm(conn, ev->status);
+ hci_connect_cfm(conn, ev->status);
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
conn->dst_type);
skb_pull(skb, HCI_EVENT_HDR_SIZE);
- if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
+ if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req_event == event) {
struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
unsigned short channel;
};
- static inline int hci_test_bit(int nr, void *addr)
+ static inline int hci_test_bit(int nr, const void *addr)
{
- return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
+ return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
}
/* Security filter */
kfree_skb(skb_copy);
}
- /* Send frame to control socket */
- void hci_send_to_control(struct sk_buff *skb, struct sock *skip_sk)
+ /* Send frame to sockets with specific channel */
+ void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
+ struct sock *skip_sk)
{
struct sock *sk;
- BT_DBG("len %d", skb->len);
+ BT_DBG("channel %u len %d", channel, skb->len);
read_lock(&hci_sk_list.lock);
if (sk->sk_state != BT_BOUND)
continue;
- if (hci_pi(sk)->channel != HCI_CHANNEL_CONTROL)
- continue;
-
- nskb = skb_clone(skb, GFP_ATOMIC);
- if (!nskb)
- continue;
-
- if (sock_queue_rcv_skb(sk, nskb))
- kfree_skb(nskb);
- }
-
- read_unlock(&hci_sk_list.lock);
- }
-
- static void queue_monitor_skb(struct sk_buff *skb)
- {
- struct sock *sk;
-
- BT_DBG("len %d", skb->len);
-
- read_lock(&hci_sk_list.lock);
-
- sk_for_each(sk, &hci_sk_list.head) {
- struct sk_buff *nskb;
-
- if (sk->sk_state != BT_BOUND)
- continue;
-
- if (hci_pi(sk)->channel != HCI_CHANNEL_MONITOR)
+ if (hci_pi(sk)->channel != channel)
continue;
nskb = skb_clone(skb, GFP_ATOMIC);
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len);
- queue_monitor_skb(skb_copy);
+ hci_send_to_channel(HCI_CHANNEL_MONITOR, skb_copy, NULL);
kfree_skb(skb_copy);
}
skb = create_monitor_event(hdev, event);
if (skb) {
- queue_monitor_skb(skb);
+ hci_send_to_channel(HCI_CHANNEL_MONITOR, skb, NULL);
kfree_skb(skb);
}
}
}
}
-static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t len, int flags)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
+ int flags)
{
int noblock = flags & MSG_DONTWAIT;
struct sock *sk = sock->sk;
return err ? : copied;
}
-static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t len)
+static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+ size_t len)
{
struct sock *sk = sock->sk;
struct hci_dev *hdev;
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->req.start = true;
+ bt_cb(skb)->req_start = 1;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
return 0;
}
-static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t len)
+static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+ size_t len)
{
struct sock *sk = sock->sk;
int err;
}
}
-static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
- struct msghdr *msg, size_t len, int flags)
+static int sco_sock_recvmsg(struct socket *sock, struct msghdr *msg,
+ size_t len, int flags)
{
struct sock *sk = sock->sk;
struct sco_pinfo *pi = sco_pi(sk);
release_sock(sk);
- return bt_sock_recvmsg(iocb, sock, msg, len, flags);
+ return bt_sock_recvmsg(sock, msg, len, flags);
}
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
return lm;
}
- void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
+ static void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{
+ if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
+ return;
+
BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
+
if (!status) {
struct sco_conn *conn;
sco_conn_del(hcon, bt_to_errno(status));
}
- void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
+ static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
{
+ if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
+ return;
+
BT_DBG("hcon %p reason %d", hcon, reason);
sco_conn_del(hcon, bt_to_errno(reason));
return 0;
}
+ static struct hci_cb sco_cb = {
+ .name = "SCO",
+ .connect_cfm = sco_connect_cfm,
+ .disconn_cfm = sco_disconn_cfm,
+ };
+
static int sco_debugfs_show(struct seq_file *f, void *p)
{
struct sock *sk;
BT_INFO("SCO socket layer initialized");
+ hci_register_cb(&sco_cb);
+
if (IS_ERR_OR_NULL(bt_debugfs))
return 0;
debugfs_remove(sco_debugfs);
+ hci_unregister_cb(&sco_cb);
+
bt_sock_unregister(BTPROTO_SCO);
proto_unregister(&sco_proto);