Commit | Line | Data |
---|---|---|
b3190df6 SS |
1 | /* |
2 | * Atheros Communication Bluetooth HCIATH3K UART protocol | |
3 | * | |
4 | * HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's | |
5 | * power management protocol extension to H4 to support AR300x Bluetooth Chip. | |
6 | * | |
7 | * Copyright (c) 2009-2010 Atheros Communications Inc. | |
8 | * | |
9 | * Acknowledgements: | |
10 | * This file is based on hci_h4.c, which was written | |
11 | * by Maxim Krasnyansky and Marcel Holtmann. | |
12 | * | |
13 | * This program is free software; you can redistribute it and/or modify | |
14 | * it under the terms of the GNU General Public License as published by | |
15 | * the Free Software Foundation; either version 2 of the License, or | |
16 | * (at your option) any later version. | |
17 | * | |
18 | * This program is distributed in the hope that it will be useful, | |
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | * GNU General Public License for more details. | |
22 | * | |
23 | * You should have received a copy of the GNU General Public License | |
24 | * along with this program; if not, write to the Free Software | |
25 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
26 | * | |
27 | */ | |
28 | ||
29 | #include <linux/module.h> | |
30 | #include <linux/kernel.h> | |
31 | ||
32 | #include <linux/init.h> | |
33 | #include <linux/slab.h> | |
34 | #include <linux/tty.h> | |
35 | #include <linux/errno.h> | |
36 | #include <linux/ioctl.h> | |
37 | #include <linux/skbuff.h> | |
38 | ||
39 | #include <net/bluetooth/bluetooth.h> | |
40 | #include <net/bluetooth/hci_core.h> | |
41 | ||
42 | #include "hci_uart.h" | |
43 | ||
44 | struct ath_struct { | |
45 | struct hci_uart *hu; | |
46 | unsigned int cur_sleep; | |
47 | ||
48 | struct sk_buff_head txq; | |
49 | struct work_struct ctxtsw; | |
50 | }; | |
51 | ||
52 | static int ath_wakeup_ar3k(struct tty_struct *tty) | |
53 | { | |
afaae084 | 54 | int status = tty->driver->ops->tiocmget(tty); |
b3190df6 SS |
55 | |
56 | if (status & TIOCM_CTS) | |
57 | return status; | |
58 | ||
b3190df6 | 59 | /* Clear RTS first */ |
632f32e2 | 60 | tty->driver->ops->tiocmget(tty); |
afaae084 | 61 | tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS); |
b3190df6 SS |
62 | mdelay(20); |
63 | ||
64 | /* Set RTS, wake up board */ | |
632f32e2 | 65 | tty->driver->ops->tiocmget(tty); |
afaae084 | 66 | tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00); |
b3190df6 SS |
67 | mdelay(20); |
68 | ||
afaae084 | 69 | status = tty->driver->ops->tiocmget(tty); |
b3190df6 SS |
70 | return status; |
71 | } | |
72 | ||
73 | static void ath_hci_uart_work(struct work_struct *work) | |
74 | { | |
75 | int status; | |
76 | struct ath_struct *ath; | |
77 | struct hci_uart *hu; | |
78 | struct tty_struct *tty; | |
79 | ||
80 | ath = container_of(work, struct ath_struct, ctxtsw); | |
81 | ||
82 | hu = ath->hu; | |
83 | tty = hu->tty; | |
84 | ||
85 | /* verify and wake up controller */ | |
86 | if (ath->cur_sleep) { | |
87 | status = ath_wakeup_ar3k(tty); | |
88 | if (!(status & TIOCM_CTS)) | |
89 | return; | |
90 | } | |
91 | ||
92 | /* Ready to send Data */ | |
93 | clear_bit(HCI_UART_SENDING, &hu->tx_state); | |
94 | hci_uart_tx_wakeup(hu); | |
95 | } | |
96 | ||
97 | /* Initialize protocol */ | |
98 | static int ath_open(struct hci_uart *hu) | |
99 | { | |
100 | struct ath_struct *ath; | |
101 | ||
102 | BT_DBG("hu %p", hu); | |
103 | ||
f5fd5bae | 104 | ath = kzalloc(sizeof(*ath), GFP_KERNEL); |
b3190df6 SS |
105 | if (!ath) |
106 | return -ENOMEM; | |
107 | ||
108 | skb_queue_head_init(&ath->txq); | |
109 | ||
110 | hu->priv = ath; | |
111 | ath->hu = hu; | |
112 | ||
113 | INIT_WORK(&ath->ctxtsw, ath_hci_uart_work); | |
114 | ||
115 | return 0; | |
116 | } | |
117 | ||
118 | /* Flush protocol data */ | |
119 | static int ath_flush(struct hci_uart *hu) | |
120 | { | |
121 | struct ath_struct *ath = hu->priv; | |
122 | ||
123 | BT_DBG("hu %p", hu); | |
124 | ||
125 | skb_queue_purge(&ath->txq); | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | /* Close protocol */ | |
131 | static int ath_close(struct hci_uart *hu) | |
132 | { | |
133 | struct ath_struct *ath = hu->priv; | |
134 | ||
135 | BT_DBG("hu %p", hu); | |
136 | ||
137 | skb_queue_purge(&ath->txq); | |
138 | ||
139 | cancel_work_sync(&ath->ctxtsw); | |
140 | ||
141 | hu->priv = NULL; | |
142 | kfree(ath); | |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | #define HCI_OP_ATH_SLEEP 0xFC04 | |
148 | ||
149 | /* Enqueue frame for transmittion */ | |
150 | static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) | |
151 | { | |
152 | struct ath_struct *ath = hu->priv; | |
153 | ||
154 | if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { | |
4ebaa4ed | 155 | kfree_skb(skb); |
b3190df6 SS |
156 | return 0; |
157 | } | |
158 | ||
159 | /* | |
160 | * Update power management enable flag with parameters of | |
161 | * HCI sleep enable vendor specific HCI command. | |
162 | */ | |
163 | if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { | |
164 | struct hci_command_hdr *hdr = (void *)skb->data; | |
165 | ||
166 | if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP) | |
167 | ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE]; | |
168 | } | |
169 | ||
170 | BT_DBG("hu %p skb %p", hu, skb); | |
171 | ||
172 | /* Prepend skb with frame type */ | |
173 | memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); | |
174 | ||
175 | skb_queue_tail(&ath->txq, skb); | |
176 | set_bit(HCI_UART_SENDING, &hu->tx_state); | |
177 | ||
178 | schedule_work(&ath->ctxtsw); | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | static struct sk_buff *ath_dequeue(struct hci_uart *hu) | |
184 | { | |
185 | struct ath_struct *ath = hu->priv; | |
186 | ||
187 | return skb_dequeue(&ath->txq); | |
188 | } | |
189 | ||
190 | /* Recv data */ | |
191 | static int ath_recv(struct hci_uart *hu, void *data, int count) | |
192 | { | |
78b4a56c JZ |
193 | int ret; |
194 | ||
195 | ret = hci_recv_stream_fragment(hu->hdev, data, count); | |
196 | if (ret < 0) { | |
b3190df6 | 197 | BT_ERR("Frame Reassembly Failed"); |
78b4a56c JZ |
198 | return ret; |
199 | } | |
b3190df6 SS |
200 | |
201 | return count; | |
202 | } | |
203 | ||
204 | static struct hci_uart_proto athp = { | |
205 | .id = HCI_UART_ATH3K, | |
206 | .open = ath_open, | |
207 | .close = ath_close, | |
208 | .recv = ath_recv, | |
209 | .enqueue = ath_enqueue, | |
210 | .dequeue = ath_dequeue, | |
211 | .flush = ath_flush, | |
212 | }; | |
213 | ||
f2b94bb9 | 214 | int __init ath_init(void) |
b3190df6 SS |
215 | { |
216 | int err = hci_uart_register_proto(&athp); | |
217 | ||
218 | if (!err) | |
219 | BT_INFO("HCIATH3K protocol initialized"); | |
220 | else | |
221 | BT_ERR("HCIATH3K protocol registration failed"); | |
222 | ||
223 | return err; | |
224 | } | |
225 | ||
f2b94bb9 | 226 | int __exit ath_deinit(void) |
b3190df6 SS |
227 | { |
228 | return hci_uart_unregister_proto(&athp); | |
229 | } |