11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b3190df6SSuraj Sumangala /*
3b3190df6SSuraj Sumangala * Atheros Communication Bluetooth HCIATH3K UART protocol
4b3190df6SSuraj Sumangala *
5b3190df6SSuraj Sumangala * HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's
6b3190df6SSuraj Sumangala * power management protocol extension to H4 to support AR300x Bluetooth Chip.
7b3190df6SSuraj Sumangala *
8b3190df6SSuraj Sumangala * Copyright (c) 2009-2010 Atheros Communications Inc.
9b3190df6SSuraj Sumangala *
10b3190df6SSuraj Sumangala * Acknowledgements:
11b3190df6SSuraj Sumangala * This file is based on hci_h4.c, which was written
12b3190df6SSuraj Sumangala * by Maxim Krasnyansky and Marcel Holtmann.
13b3190df6SSuraj Sumangala */
14b3190df6SSuraj Sumangala
15b3190df6SSuraj Sumangala #include <linux/module.h>
16b3190df6SSuraj Sumangala #include <linux/kernel.h>
17b3190df6SSuraj Sumangala
18b3190df6SSuraj Sumangala #include <linux/init.h>
19b3190df6SSuraj Sumangala #include <linux/slab.h>
20b3190df6SSuraj Sumangala #include <linux/tty.h>
21b3190df6SSuraj Sumangala #include <linux/errno.h>
22b3190df6SSuraj Sumangala #include <linux/ioctl.h>
23b3190df6SSuraj Sumangala #include <linux/skbuff.h>
24b3190df6SSuraj Sumangala
25b3190df6SSuraj Sumangala #include <net/bluetooth/bluetooth.h>
26b3190df6SSuraj Sumangala #include <net/bluetooth/hci_core.h>
27b3190df6SSuraj Sumangala
28b3190df6SSuraj Sumangala #include "hci_uart.h"
29b3190df6SSuraj Sumangala
30b3190df6SSuraj Sumangala struct ath_struct {
31b3190df6SSuraj Sumangala struct hci_uart *hu;
32b3190df6SSuraj Sumangala unsigned int cur_sleep;
33b3190df6SSuraj Sumangala
34d90aa682SMarcel Holtmann struct sk_buff *rx_skb;
35b3190df6SSuraj Sumangala struct sk_buff_head txq;
36b3190df6SSuraj Sumangala struct work_struct ctxtsw;
37b3190df6SSuraj Sumangala };
38b3190df6SSuraj Sumangala
3913df5000SLoic Poulain #define OP_WRITE_TAG 0x01
4013df5000SLoic Poulain
4113df5000SLoic Poulain #define INDEX_BDADDR 0x01
4213df5000SLoic Poulain
4313df5000SLoic Poulain struct ath_vendor_cmd {
4413df5000SLoic Poulain __u8 opcode;
4513df5000SLoic Poulain __le16 index;
4613df5000SLoic Poulain __u8 len;
4713df5000SLoic Poulain __u8 data[251];
4813df5000SLoic Poulain } __packed;
4913df5000SLoic Poulain
ath_wakeup_ar3k(struct tty_struct * tty)50b3190df6SSuraj Sumangala static int ath_wakeup_ar3k(struct tty_struct *tty)
51b3190df6SSuraj Sumangala {
52afaae084SAlan Cox int status = tty->driver->ops->tiocmget(tty);
53b3190df6SSuraj Sumangala
54b3190df6SSuraj Sumangala if (status & TIOCM_CTS)
55b3190df6SSuraj Sumangala return status;
56b3190df6SSuraj Sumangala
57b3190df6SSuraj Sumangala /* Clear RTS first */
58632f32e2SPeter Hurley tty->driver->ops->tiocmget(tty);
59afaae084SAlan Cox tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
60688d6240SJia-Ju Bai msleep(20);
61b3190df6SSuraj Sumangala
62b3190df6SSuraj Sumangala /* Set RTS, wake up board */
63632f32e2SPeter Hurley tty->driver->ops->tiocmget(tty);
64afaae084SAlan Cox tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
65688d6240SJia-Ju Bai msleep(20);
66b3190df6SSuraj Sumangala
67afaae084SAlan Cox status = tty->driver->ops->tiocmget(tty);
68b3190df6SSuraj Sumangala return status;
69b3190df6SSuraj Sumangala }
70b3190df6SSuraj Sumangala
ath_hci_uart_work(struct work_struct * work)71b3190df6SSuraj Sumangala static void ath_hci_uart_work(struct work_struct *work)
72b3190df6SSuraj Sumangala {
73b3190df6SSuraj Sumangala int status;
74b3190df6SSuraj Sumangala struct ath_struct *ath;
75b3190df6SSuraj Sumangala struct hci_uart *hu;
76b3190df6SSuraj Sumangala struct tty_struct *tty;
77b3190df6SSuraj Sumangala
78b3190df6SSuraj Sumangala ath = container_of(work, struct ath_struct, ctxtsw);
79b3190df6SSuraj Sumangala
80b3190df6SSuraj Sumangala hu = ath->hu;
81b3190df6SSuraj Sumangala tty = hu->tty;
82b3190df6SSuraj Sumangala
83b3190df6SSuraj Sumangala /* verify and wake up controller */
84b3190df6SSuraj Sumangala if (ath->cur_sleep) {
85b3190df6SSuraj Sumangala status = ath_wakeup_ar3k(tty);
86b3190df6SSuraj Sumangala if (!(status & TIOCM_CTS))
87b3190df6SSuraj Sumangala return;
88b3190df6SSuraj Sumangala }
89b3190df6SSuraj Sumangala
90b3190df6SSuraj Sumangala /* Ready to send Data */
91b3190df6SSuraj Sumangala clear_bit(HCI_UART_SENDING, &hu->tx_state);
92b3190df6SSuraj Sumangala hci_uart_tx_wakeup(hu);
93b3190df6SSuraj Sumangala }
94b3190df6SSuraj Sumangala
ath_open(struct hci_uart * hu)95b3190df6SSuraj Sumangala static int ath_open(struct hci_uart *hu)
96b3190df6SSuraj Sumangala {
97b3190df6SSuraj Sumangala struct ath_struct *ath;
98b3190df6SSuraj Sumangala
99b3190df6SSuraj Sumangala BT_DBG("hu %p", hu);
100b3190df6SSuraj Sumangala
101*b36a1552SVladis Dronov if (!hci_uart_has_flow_control(hu))
102*b36a1552SVladis Dronov return -EOPNOTSUPP;
103*b36a1552SVladis Dronov
104f5fd5baeSDavid Herrmann ath = kzalloc(sizeof(*ath), GFP_KERNEL);
105b3190df6SSuraj Sumangala if (!ath)
106b3190df6SSuraj Sumangala return -ENOMEM;
107b3190df6SSuraj Sumangala
108b3190df6SSuraj Sumangala skb_queue_head_init(&ath->txq);
109b3190df6SSuraj Sumangala
110b3190df6SSuraj Sumangala hu->priv = ath;
111b3190df6SSuraj Sumangala ath->hu = hu;
112b3190df6SSuraj Sumangala
113b3190df6SSuraj Sumangala INIT_WORK(&ath->ctxtsw, ath_hci_uart_work);
114b3190df6SSuraj Sumangala
115b3190df6SSuraj Sumangala return 0;
116b3190df6SSuraj Sumangala }
117b3190df6SSuraj Sumangala
ath_close(struct hci_uart * hu)118b3190df6SSuraj Sumangala static int ath_close(struct hci_uart *hu)
119b3190df6SSuraj Sumangala {
120b3190df6SSuraj Sumangala struct ath_struct *ath = hu->priv;
121b3190df6SSuraj Sumangala
122b3190df6SSuraj Sumangala BT_DBG("hu %p", hu);
123b3190df6SSuraj Sumangala
124b3190df6SSuraj Sumangala skb_queue_purge(&ath->txq);
125b3190df6SSuraj Sumangala
126d90aa682SMarcel Holtmann kfree_skb(ath->rx_skb);
127d90aa682SMarcel Holtmann
128b3190df6SSuraj Sumangala cancel_work_sync(&ath->ctxtsw);
129b3190df6SSuraj Sumangala
130b3190df6SSuraj Sumangala hu->priv = NULL;
131b3190df6SSuraj Sumangala kfree(ath);
132b3190df6SSuraj Sumangala
133b3190df6SSuraj Sumangala return 0;
134b3190df6SSuraj Sumangala }
135b3190df6SSuraj Sumangala
ath_flush(struct hci_uart * hu)136c0ba7acdSMarcel Holtmann static int ath_flush(struct hci_uart *hu)
137c0ba7acdSMarcel Holtmann {
138c0ba7acdSMarcel Holtmann struct ath_struct *ath = hu->priv;
139c0ba7acdSMarcel Holtmann
140c0ba7acdSMarcel Holtmann BT_DBG("hu %p", hu);
141c0ba7acdSMarcel Holtmann
142c0ba7acdSMarcel Holtmann skb_queue_purge(&ath->txq);
143c0ba7acdSMarcel Holtmann
144c0ba7acdSMarcel Holtmann return 0;
145c0ba7acdSMarcel Holtmann }
146c0ba7acdSMarcel Holtmann
ath_vendor_cmd(struct hci_dev * hdev,uint8_t opcode,uint16_t index,const void * data,size_t dlen)14713df5000SLoic Poulain static int ath_vendor_cmd(struct hci_dev *hdev, uint8_t opcode, uint16_t index,
14813df5000SLoic Poulain const void *data, size_t dlen)
1494c876c0eSMarcel Holtmann {
1504c876c0eSMarcel Holtmann struct sk_buff *skb;
15113df5000SLoic Poulain struct ath_vendor_cmd cmd;
1524c876c0eSMarcel Holtmann
15313df5000SLoic Poulain if (dlen > sizeof(cmd.data))
15413df5000SLoic Poulain return -EINVAL;
1554c876c0eSMarcel Holtmann
15613df5000SLoic Poulain cmd.opcode = opcode;
15713df5000SLoic Poulain cmd.index = cpu_to_le16(index);
15813df5000SLoic Poulain cmd.len = dlen;
15913df5000SLoic Poulain memcpy(cmd.data, data, dlen);
16013df5000SLoic Poulain
16113df5000SLoic Poulain skb = __hci_cmd_sync(hdev, 0xfc0b, dlen + 4, &cmd, HCI_INIT_TIMEOUT);
16213df5000SLoic Poulain if (IS_ERR(skb))
16313df5000SLoic Poulain return PTR_ERR(skb);
1644c876c0eSMarcel Holtmann kfree_skb(skb);
1654c876c0eSMarcel Holtmann
1664c876c0eSMarcel Holtmann return 0;
1674c876c0eSMarcel Holtmann }
1684c876c0eSMarcel Holtmann
ath_set_bdaddr(struct hci_dev * hdev,const bdaddr_t * bdaddr)16913df5000SLoic Poulain static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
17013df5000SLoic Poulain {
17113df5000SLoic Poulain return ath_vendor_cmd(hdev, OP_WRITE_TAG, INDEX_BDADDR, bdaddr,
17213df5000SLoic Poulain sizeof(*bdaddr));
17313df5000SLoic Poulain }
17413df5000SLoic Poulain
ath_setup(struct hci_uart * hu)1754c876c0eSMarcel Holtmann static int ath_setup(struct hci_uart *hu)
1764c876c0eSMarcel Holtmann {
1774c876c0eSMarcel Holtmann BT_DBG("hu %p", hu);
1784c876c0eSMarcel Holtmann
1794c876c0eSMarcel Holtmann hu->hdev->set_bdaddr = ath_set_bdaddr;
1804c876c0eSMarcel Holtmann
1814c876c0eSMarcel Holtmann return 0;
1824c876c0eSMarcel Holtmann }
1834c876c0eSMarcel Holtmann
184c0ba7acdSMarcel Holtmann static const struct h4_recv_pkt ath_recv_pkts[] = {
185c0ba7acdSMarcel Holtmann { H4_RECV_ACL, .recv = hci_recv_frame },
186c0ba7acdSMarcel Holtmann { H4_RECV_SCO, .recv = hci_recv_frame },
187c0ba7acdSMarcel Holtmann { H4_RECV_EVENT, .recv = hci_recv_frame },
188c0ba7acdSMarcel Holtmann };
189c0ba7acdSMarcel Holtmann
ath_recv(struct hci_uart * hu,const void * data,int count)190c0ba7acdSMarcel Holtmann static int ath_recv(struct hci_uart *hu, const void *data, int count)
191c0ba7acdSMarcel Holtmann {
192c0ba7acdSMarcel Holtmann struct ath_struct *ath = hu->priv;
193c0ba7acdSMarcel Holtmann
194c0ba7acdSMarcel Holtmann ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
195c0ba7acdSMarcel Holtmann ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
196c0ba7acdSMarcel Holtmann if (IS_ERR(ath->rx_skb)) {
197c0ba7acdSMarcel Holtmann int err = PTR_ERR(ath->rx_skb);
1982064ee33SMarcel Holtmann bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
19937134167SChan-yeol Park ath->rx_skb = NULL;
200c0ba7acdSMarcel Holtmann return err;
201c0ba7acdSMarcel Holtmann }
202c0ba7acdSMarcel Holtmann
203c0ba7acdSMarcel Holtmann return count;
204c0ba7acdSMarcel Holtmann }
205c0ba7acdSMarcel Holtmann
206b3190df6SSuraj Sumangala #define HCI_OP_ATH_SLEEP 0xFC04
207b3190df6SSuraj Sumangala
ath_enqueue(struct hci_uart * hu,struct sk_buff * skb)208b3190df6SSuraj Sumangala static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
209b3190df6SSuraj Sumangala {
210b3190df6SSuraj Sumangala struct ath_struct *ath = hu->priv;
211b3190df6SSuraj Sumangala
212618e8bc2SMarcel Holtmann if (hci_skb_pkt_type(skb) == HCI_SCODATA_PKT) {
2134ebaa4edSDan Carpenter kfree_skb(skb);
214b3190df6SSuraj Sumangala return 0;
215b3190df6SSuraj Sumangala }
216b3190df6SSuraj Sumangala
217c0ba7acdSMarcel Holtmann /* Update power management enable flag with parameters of
218b3190df6SSuraj Sumangala * HCI sleep enable vendor specific HCI command.
219b3190df6SSuraj Sumangala */
220618e8bc2SMarcel Holtmann if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
221b3190df6SSuraj Sumangala struct hci_command_hdr *hdr = (void *)skb->data;
222b3190df6SSuraj Sumangala
223b3190df6SSuraj Sumangala if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
224b3190df6SSuraj Sumangala ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE];
225b3190df6SSuraj Sumangala }
226b3190df6SSuraj Sumangala
227b3190df6SSuraj Sumangala BT_DBG("hu %p skb %p", hu, skb);
228b3190df6SSuraj Sumangala
229b3190df6SSuraj Sumangala /* Prepend skb with frame type */
230618e8bc2SMarcel Holtmann memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
231b3190df6SSuraj Sumangala
232b3190df6SSuraj Sumangala skb_queue_tail(&ath->txq, skb);
233b3190df6SSuraj Sumangala set_bit(HCI_UART_SENDING, &hu->tx_state);
234b3190df6SSuraj Sumangala
235b3190df6SSuraj Sumangala schedule_work(&ath->ctxtsw);
236b3190df6SSuraj Sumangala
237b3190df6SSuraj Sumangala return 0;
238b3190df6SSuraj Sumangala }
239b3190df6SSuraj Sumangala
ath_dequeue(struct hci_uart * hu)240b3190df6SSuraj Sumangala static struct sk_buff *ath_dequeue(struct hci_uart *hu)
241b3190df6SSuraj Sumangala {
242b3190df6SSuraj Sumangala struct ath_struct *ath = hu->priv;
243b3190df6SSuraj Sumangala
244b3190df6SSuraj Sumangala return skb_dequeue(&ath->txq);
245b3190df6SSuraj Sumangala }
246b3190df6SSuraj Sumangala
2474ee7ef19SMarcel Holtmann static const struct hci_uart_proto athp = {
248b3190df6SSuraj Sumangala .id = HCI_UART_ATH3K,
2497c40fb8dSMarcel Holtmann .name = "ATH3K",
250aee61f7aSMarcel Holtmann .manufacturer = 69,
251b3190df6SSuraj Sumangala .open = ath_open,
252b3190df6SSuraj Sumangala .close = ath_close,
253c0ba7acdSMarcel Holtmann .flush = ath_flush,
2544c876c0eSMarcel Holtmann .setup = ath_setup,
255b3190df6SSuraj Sumangala .recv = ath_recv,
256b3190df6SSuraj Sumangala .enqueue = ath_enqueue,
257b3190df6SSuraj Sumangala .dequeue = ath_dequeue,
258b3190df6SSuraj Sumangala };
259b3190df6SSuraj Sumangala
ath_init(void)260f2b94bb9SGustavo F. Padovan int __init ath_init(void)
261b3190df6SSuraj Sumangala {
26201009eecSMarcel Holtmann return hci_uart_register_proto(&athp);
263b3190df6SSuraj Sumangala }
264b3190df6SSuraj Sumangala
ath_deinit(void)265f2b94bb9SGustavo F. Padovan int __exit ath_deinit(void)
266b3190df6SSuraj Sumangala {
267b3190df6SSuraj Sumangala return hci_uart_unregister_proto(&athp);
268b3190df6SSuraj Sumangala }
269