diff options
Diffstat (limited to 'drivers/bluetooth/hci_ath.c')
-rw-r--r-- | drivers/bluetooth/hci_ath.c | 115 |
1 files changed, 74 insertions, 41 deletions
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 9c4dcf4c62ea..ec8fa0e0f036 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -45,6 +45,7 @@ struct ath_struct { struct hci_uart *hu; unsigned int cur_sleep; + struct sk_buff *rx_skb; struct sk_buff_head txq; struct work_struct ctxtsw; }; @@ -94,7 +95,6 @@ static void ath_hci_uart_work(struct work_struct *work) hci_uart_tx_wakeup(hu); } -/* Initialize protocol */ static int ath_open(struct hci_uart *hu) { struct ath_struct *ath; @@ -115,8 +115,7 @@ static int ath_open(struct hci_uart *hu) return 0; } -/* Flush protocol data */ -static int ath_flush(struct hci_uart *hu) +static int ath_close(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; @@ -124,11 +123,17 @@ static int ath_flush(struct hci_uart *hu) skb_queue_purge(&ath->txq); + kfree_skb(ath->rx_skb); + + cancel_work_sync(&ath->ctxtsw); + + hu->priv = NULL; + kfree(ath); + return 0; } -/* Close protocol */ -static int ath_close(struct hci_uart *hu) +static int ath_flush(struct hci_uart *hu) { struct ath_struct *ath = hu->priv; @@ -136,17 +141,65 @@ static int ath_close(struct hci_uart *hu) skb_queue_purge(&ath->txq); - cancel_work_sync(&ath->ctxtsw); + return 0; +} - hu->priv = NULL; - kfree(ath); +static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + u8 buf[10]; + int err; + + buf[0] = 0x01; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = sizeof(bdaddr_t); + memcpy(buf + 4, bdaddr, sizeof(bdaddr_t)); + + skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + BT_ERR("%s: Change address command failed (%d)", + hdev->name, err); + return err; + } + kfree_skb(skb); return 0; } +static int ath_setup(struct hci_uart *hu) +{ + BT_DBG("hu %p", hu); + + hu->hdev->set_bdaddr = ath_set_bdaddr; + + return 0; +} + +static const struct h4_recv_pkt ath_recv_pkts[] = { + { H4_RECV_ACL, .recv = hci_recv_frame }, + { H4_RECV_SCO, .recv = hci_recv_frame }, + { H4_RECV_EVENT, .recv = hci_recv_frame }, +}; + +static int ath_recv(struct hci_uart *hu, const void *data, int count) +{ + struct ath_struct *ath = hu->priv; + + ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count, + ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); + if (IS_ERR(ath->rx_skb)) { + int err = PTR_ERR(ath->rx_skb); + BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + return err; + } + + return count; +} + #define HCI_OP_ATH_SLEEP 0xFC04 -/* Enqueue frame for transmittion */ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) { struct ath_struct *ath = hu->priv; @@ -156,8 +209,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) return 0; } - /* - * Update power management enable flag with parameters of + /* Update power management enable flag with parameters of * HCI sleep enable vendor specific HCI command. */ if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { @@ -187,40 +239,21 @@ static struct sk_buff *ath_dequeue(struct hci_uart *hu) return skb_dequeue(&ath->txq); } -/* Recv data */ -static int ath_recv(struct hci_uart *hu, void *data, int count) -{ - int ret; - - ret = hci_recv_stream_fragment(hu->hdev, data, count); - if (ret < 0) { - BT_ERR("Frame Reassembly Failed"); - return ret; - } - - return count; -} - -static struct hci_uart_proto athp = { - .id = HCI_UART_ATH3K, - .open = ath_open, - .close = ath_close, - .recv = ath_recv, - .enqueue = ath_enqueue, - .dequeue = ath_dequeue, - .flush = ath_flush, +static const struct hci_uart_proto athp = { + .id = HCI_UART_ATH3K, + .name = "ATH3K", + .open = ath_open, + .close = ath_close, + .flush = ath_flush, + .setup = ath_setup, + .recv = ath_recv, + .enqueue = ath_enqueue, + .dequeue = ath_dequeue, }; int __init ath_init(void) { - int err = hci_uart_register_proto(&athp); - - if (!err) - BT_INFO("HCIATH3K protocol initialized"); - else - BT_ERR("HCIATH3K protocol registration failed"); - - return err; + return hci_uart_register_proto(&athp); } int __exit ath_deinit(void) |