diff options
author | Bing Zhao <bzhao@marvell.com> | 2009-06-02 17:29:35 -0400 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2009-08-22 17:25:32 -0400 |
commit | 132ff4e5fa8dfb71a7d99902f88043113947e972 (patch) | |
tree | 3fb4b236d8bfafd7c0a74f55a2d324189e0ccd91 /drivers/bluetooth | |
parent | edad63886993d18ab800c49f6587a93432ef8b35 (diff) |
Bluetooth: Add btmrvl driver for Marvell Bluetooth devices
This driver provides basic definitions and library functions to
support Marvell Bluetooth enabled devices, such as 88W8688 WLAN/BT
combo chip.
This patch incorporates a lot of comments given by
Nicolas Pitre <nico@marvell.com>. Many thanks to Nicolas Pitre.
Signed-off-by: Rahul Tank <rahult@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth')
-rw-r--r-- | drivers/bluetooth/Kconfig | 12 | ||||
-rw-r--r-- | drivers/bluetooth/Makefile | 3 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_drv.h | 138 | ||||
-rw-r--r-- | drivers/bluetooth/btmrvl_main.c | 714 |
4 files changed, 867 insertions, 0 deletions
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index 1164837bb781..b049a79fcc02 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig | |||
@@ -170,5 +170,17 @@ config BT_HCIVHCI | |||
170 | Say Y here to compile support for virtual HCI devices into the | 170 | Say Y here to compile support for virtual HCI devices into the |
171 | kernel or say M to compile it as module (hci_vhci). | 171 | kernel or say M to compile it as module (hci_vhci). |
172 | 172 | ||
173 | config BT_MRVL | ||
174 | tristate "Marvell Bluetooth driver support" | ||
175 | select FW_LOADER | ||
176 | help | ||
177 | The core driver to support Marvell Bluetooth devices. | ||
178 | |||
179 | This driver is required if you want to support | ||
180 | Marvell Bluetooth devices, such as 8688. | ||
181 | |||
182 | Say Y here to compile Marvell Bluetooth driver | ||
183 | into the kernel or say M to compile it as module. | ||
184 | |||
173 | endmenu | 185 | endmenu |
174 | 186 | ||
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 16930f93d1ca..3eff1231812c 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile | |||
@@ -15,6 +15,9 @@ obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o | |||
15 | obj-$(CONFIG_BT_HCIBTUSB) += btusb.o | 15 | obj-$(CONFIG_BT_HCIBTUSB) += btusb.o |
16 | obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o | 16 | obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o |
17 | 17 | ||
18 | btmrvl-objs := btmrvl_main.o | ||
19 | obj-$(CONFIG_BT_MRVL) += btmrvl.o | ||
20 | |||
18 | hci_uart-y := hci_ldisc.o | 21 | hci_uart-y := hci_ldisc.o |
19 | hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o | 22 | hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o |
20 | hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o | 23 | hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o |
diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h new file mode 100644 index 000000000000..9ad71f48110d --- /dev/null +++ b/drivers/bluetooth/btmrvl_drv.h | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Marvell Bluetooth driver: global definitions & declarations | ||
3 | * | ||
4 | * Copyright (C) 2009, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * | ||
15 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
17 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
18 | * this warranty disclaimer. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifndef _BTMRVL_DRV_H_ | ||
23 | #define _BTMRVL_DRV_H_ | ||
24 | |||
25 | #include <linux/kthread.h> | ||
26 | #include <linux/bitops.h> | ||
27 | #include <net/bluetooth/bluetooth.h> | ||
28 | |||
29 | #define BTM_HEADER_LEN 4 | ||
30 | #define BTM_DEV_NAME_LEN 32 | ||
31 | #define BTM_UPLD_SIZE 2312 | ||
32 | |||
33 | /* Time to wait until Host Sleep state change in millisecond */ | ||
34 | #define WAIT_UNTIL_HS_STATE_CHANGED 5000 | ||
35 | /* Time to wait for command response in millisecond */ | ||
36 | #define WAIT_UNTIL_CMD_RESP 5000 | ||
37 | |||
38 | struct btmrvl_thread { | ||
39 | struct task_struct *task; | ||
40 | wait_queue_head_t wait_q; | ||
41 | void *priv; | ||
42 | }; | ||
43 | |||
44 | struct btmrvl_device { | ||
45 | char name[BTM_DEV_NAME_LEN]; | ||
46 | void *card; | ||
47 | struct hci_dev *hcidev; | ||
48 | |||
49 | u8 tx_dnld_rdy; | ||
50 | |||
51 | u8 psmode; | ||
52 | u8 pscmd; | ||
53 | u8 hsmode; | ||
54 | u8 hscmd; | ||
55 | |||
56 | /* Low byte is gap, high byte is GPIO */ | ||
57 | u16 gpio_gap; | ||
58 | |||
59 | u8 hscfgcmd; | ||
60 | u8 sendcmdflag; | ||
61 | }; | ||
62 | |||
63 | struct btmrvl_adapter { | ||
64 | u32 int_count; | ||
65 | struct sk_buff_head tx_queue; | ||
66 | u8 psmode; | ||
67 | u8 ps_state; | ||
68 | u8 hs_state; | ||
69 | u8 wakeup_tries; | ||
70 | wait_queue_head_t cmd_wait_q; | ||
71 | u8 cmd_complete; | ||
72 | }; | ||
73 | |||
74 | struct btmrvl_private { | ||
75 | struct btmrvl_device btmrvl_dev; | ||
76 | struct btmrvl_adapter *adapter; | ||
77 | struct btmrvl_thread main_thread; | ||
78 | int (*hw_host_to_card) (struct btmrvl_private *priv, | ||
79 | u8 *payload, u16 nb); | ||
80 | int (*hw_wakeup_firmware) (struct btmrvl_private *priv); | ||
81 | spinlock_t driver_lock; /* spinlock used by driver */ | ||
82 | }; | ||
83 | |||
84 | #define MRVL_VENDOR_PKT 0xFE | ||
85 | |||
86 | /* Bluetooth commands */ | ||
87 | #define BT_CMD_AUTO_SLEEP_MODE 0x23 | ||
88 | #define BT_CMD_HOST_SLEEP_CONFIG 0x59 | ||
89 | #define BT_CMD_HOST_SLEEP_ENABLE 0x5A | ||
90 | #define BT_CMD_MODULE_CFG_REQ 0x5B | ||
91 | |||
92 | /* Sub-commands: Module Bringup/Shutdown Request */ | ||
93 | #define MODULE_BRINGUP_REQ 0xF1 | ||
94 | #define MODULE_SHUTDOWN_REQ 0xF2 | ||
95 | |||
96 | #define BT_EVENT_POWER_STATE 0x20 | ||
97 | |||
98 | /* Bluetooth Power States */ | ||
99 | #define BT_PS_ENABLE 0x02 | ||
100 | #define BT_PS_DISABLE 0x03 | ||
101 | #define BT_PS_SLEEP 0x01 | ||
102 | |||
103 | #define OGF 0x3F | ||
104 | |||
105 | /* Host Sleep states */ | ||
106 | #define HS_ACTIVATED 0x01 | ||
107 | #define HS_DEACTIVATED 0x00 | ||
108 | |||
109 | /* Power Save modes */ | ||
110 | #define PS_SLEEP 0x01 | ||
111 | #define PS_AWAKE 0x00 | ||
112 | |||
113 | struct btmrvl_cmd { | ||
114 | __le16 ocf_ogf; | ||
115 | u8 length; | ||
116 | u8 data[4]; | ||
117 | } __attribute__ ((packed)); | ||
118 | |||
119 | struct btmrvl_event { | ||
120 | u8 ec; /* event counter */ | ||
121 | u8 length; | ||
122 | u8 data[4]; | ||
123 | } __attribute__ ((packed)); | ||
124 | |||
125 | /* Prototype of global function */ | ||
126 | |||
127 | struct btmrvl_private *btmrvl_add_card(void *card); | ||
128 | int btmrvl_remove_card(struct btmrvl_private *priv); | ||
129 | |||
130 | void btmrvl_interrupt(struct btmrvl_private *priv); | ||
131 | |||
132 | void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb); | ||
133 | int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb); | ||
134 | |||
135 | int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd); | ||
136 | int btmrvl_prepare_command(struct btmrvl_private *priv); | ||
137 | |||
138 | #endif /* _BTMRVL_DRV_H_ */ | ||
diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c new file mode 100644 index 000000000000..11c2f2cecf18 --- /dev/null +++ b/drivers/bluetooth/btmrvl_main.c | |||
@@ -0,0 +1,714 @@ | |||
1 | /** | ||
2 | * Marvell Bluetooth driver | ||
3 | * | ||
4 | * Copyright (C) 2009, Marvell International Ltd. | ||
5 | * | ||
6 | * This software file (the "File") is distributed by Marvell International | ||
7 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
8 | * (the "License"). You may use, redistribute and/or modify this File in | ||
9 | * accordance with the terms and conditions of the License, a copy of which | ||
10 | * is available by writing to the Free Software Foundation, Inc., | ||
11 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the | ||
12 | * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
13 | * | ||
14 | * | ||
15 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
17 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
18 | * this warranty disclaimer. | ||
19 | **/ | ||
20 | |||
21 | #include <net/bluetooth/bluetooth.h> | ||
22 | #include <net/bluetooth/hci_core.h> | ||
23 | |||
24 | #include "btmrvl_drv.h" | ||
25 | |||
26 | #define VERSION "1.0" | ||
27 | |||
28 | /* | ||
29 | * This function is called by interface specific interrupt handler. | ||
30 | * It updates Power Save & Host Sleep states, and wakes up the main | ||
31 | * thread. | ||
32 | */ | ||
33 | void btmrvl_interrupt(struct btmrvl_private *priv) | ||
34 | { | ||
35 | BT_DBG("Enter"); | ||
36 | |||
37 | priv->adapter->ps_state = PS_AWAKE; | ||
38 | |||
39 | priv->adapter->wakeup_tries = 0; | ||
40 | |||
41 | priv->adapter->int_count++; | ||
42 | |||
43 | wake_up_interruptible(&priv->main_thread.wait_q); | ||
44 | |||
45 | BT_DBG("Leave"); | ||
46 | } | ||
47 | EXPORT_SYMBOL_GPL(btmrvl_interrupt); | ||
48 | |||
49 | void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb) | ||
50 | { | ||
51 | struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data; | ||
52 | struct hci_ev_cmd_complete *ec; | ||
53 | u16 opcode, ocf; | ||
54 | |||
55 | BT_DBG("Enter"); | ||
56 | |||
57 | if (hdr->evt == HCI_EV_CMD_COMPLETE) { | ||
58 | ec = (struct hci_ev_cmd_complete *)(skb->data + | ||
59 | HCI_EVENT_HDR_SIZE); | ||
60 | opcode = __le16_to_cpu(ec->opcode); | ||
61 | ocf = hci_opcode_ocf(opcode); | ||
62 | if ((ocf == BT_CMD_MODULE_CFG_REQ) && | ||
63 | (priv->btmrvl_dev.sendcmdflag)) { | ||
64 | priv->btmrvl_dev.sendcmdflag = false; | ||
65 | priv->adapter->cmd_complete = true; | ||
66 | wake_up_interruptible(&priv->adapter->cmd_wait_q); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | BT_DBG("Leave"); | ||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt); | ||
73 | |||
74 | int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) | ||
75 | { | ||
76 | struct btmrvl_adapter *adapter = priv->adapter; | ||
77 | u8 ret = 0; | ||
78 | struct btmrvl_event *event; | ||
79 | |||
80 | BT_DBG("Enter"); | ||
81 | |||
82 | event = (struct btmrvl_event *) skb->data; | ||
83 | if (event->ec != 0xff) { | ||
84 | BT_DBG("Not Marvell Event=%x", event->ec); | ||
85 | ret = -EINVAL; | ||
86 | goto exit; | ||
87 | } | ||
88 | |||
89 | switch (event->data[0]) { | ||
90 | case BT_CMD_AUTO_SLEEP_MODE: | ||
91 | if (!event->data[2]) { | ||
92 | if (event->data[1] == BT_PS_ENABLE) | ||
93 | adapter->psmode = 1; | ||
94 | else | ||
95 | adapter->psmode = 0; | ||
96 | BT_DBG("PS Mode:%s", | ||
97 | (adapter->psmode) ? "Enable" : "Disable"); | ||
98 | } else { | ||
99 | BT_DBG("PS Mode command failed"); | ||
100 | } | ||
101 | break; | ||
102 | |||
103 | case BT_CMD_HOST_SLEEP_CONFIG: | ||
104 | if (!event->data[3]) | ||
105 | BT_DBG("gpio=%x, gap=%x", event->data[1], | ||
106 | event->data[2]); | ||
107 | else | ||
108 | BT_DBG("HSCFG command failed"); | ||
109 | break; | ||
110 | |||
111 | case BT_CMD_HOST_SLEEP_ENABLE: | ||
112 | if (!event->data[1]) { | ||
113 | adapter->hs_state = HS_ACTIVATED; | ||
114 | if (adapter->psmode) | ||
115 | adapter->ps_state = PS_SLEEP; | ||
116 | wake_up_interruptible(&adapter->cmd_wait_q); | ||
117 | BT_DBG("HS ACTIVATED!"); | ||
118 | } else { | ||
119 | BT_DBG("HS Enable failed"); | ||
120 | } | ||
121 | break; | ||
122 | |||
123 | case BT_CMD_MODULE_CFG_REQ: | ||
124 | if ((priv->btmrvl_dev.sendcmdflag) && | ||
125 | (event->data[1] == MODULE_BRINGUP_REQ)) { | ||
126 | BT_DBG("EVENT:%s", (event->data[2]) ? | ||
127 | "Bring-up failed" : "Bring-up succeed"); | ||
128 | } else if ((priv->btmrvl_dev.sendcmdflag) && | ||
129 | (event->data[1] == MODULE_SHUTDOWN_REQ)) { | ||
130 | BT_DBG("EVENT:%s", (event->data[2]) ? | ||
131 | "Shutdown failed" : "Shutdown succeed"); | ||
132 | } else { | ||
133 | BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP"); | ||
134 | ret = -EINVAL; | ||
135 | } | ||
136 | break; | ||
137 | |||
138 | case BT_EVENT_POWER_STATE: | ||
139 | if (event->data[1] == BT_PS_SLEEP) | ||
140 | adapter->ps_state = PS_SLEEP; | ||
141 | BT_DBG("EVENT:%s", | ||
142 | (adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); | ||
143 | break; | ||
144 | |||
145 | default: | ||
146 | BT_DBG("Unknown Event=%d", event->data[0]); | ||
147 | ret = -EINVAL; | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | exit: | ||
152 | if (!ret) | ||
153 | kfree_skb(skb); | ||
154 | |||
155 | BT_DBG("Leave"); | ||
156 | |||
157 | return ret; | ||
158 | } | ||
159 | EXPORT_SYMBOL_GPL(btmrvl_process_event); | ||
160 | |||
161 | int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) | ||
162 | { | ||
163 | struct sk_buff *skb = NULL; | ||
164 | u8 ret = 0; | ||
165 | struct btmrvl_cmd *cmd; | ||
166 | |||
167 | BT_DBG("Enter"); | ||
168 | |||
169 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
170 | if (skb == NULL) { | ||
171 | BT_ERR("No free skb"); | ||
172 | ret = -ENOMEM; | ||
173 | goto exit; | ||
174 | } | ||
175 | |||
176 | cmd = (struct btmrvl_cmd *) skb->tail; | ||
177 | cmd->ocf_ogf = cpu_to_le16((OGF << 10) | BT_CMD_MODULE_CFG_REQ); | ||
178 | cmd->length = 1; | ||
179 | cmd->data[0] = subcmd; | ||
180 | |||
181 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | ||
182 | |||
183 | skb_put(skb, sizeof(*cmd)); | ||
184 | skb->dev = (void *)priv->btmrvl_dev.hcidev; | ||
185 | skb_queue_head(&priv->adapter->tx_queue, skb); | ||
186 | |||
187 | priv->btmrvl_dev.sendcmdflag = true; | ||
188 | |||
189 | priv->adapter->cmd_complete = false; | ||
190 | |||
191 | BT_DBG("Queue module cfg Command"); | ||
192 | |||
193 | wake_up_interruptible(&priv->main_thread.wait_q); | ||
194 | |||
195 | if (!wait_event_interruptible_timeout( | ||
196 | priv->adapter->cmd_wait_q, | ||
197 | priv->adapter->cmd_complete, | ||
198 | msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) { | ||
199 | ret = -ETIMEDOUT; | ||
200 | BT_ERR("module_cfg_cmd(%x): timeout: %d", | ||
201 | subcmd, priv->btmrvl_dev.sendcmdflag); | ||
202 | } | ||
203 | |||
204 | BT_DBG("module cfg Command done"); | ||
205 | |||
206 | exit: | ||
207 | BT_DBG("Leave"); | ||
208 | |||
209 | return ret; | ||
210 | } | ||
211 | EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); | ||
212 | |||
213 | static int btmrvl_enable_hs(struct btmrvl_private *priv) | ||
214 | { | ||
215 | struct sk_buff *skb = NULL; | ||
216 | u8 ret = 0; | ||
217 | struct btmrvl_cmd *cmd; | ||
218 | |||
219 | BT_DBG("Enter"); | ||
220 | |||
221 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
222 | if (skb == NULL) { | ||
223 | BT_ERR("No free skb"); | ||
224 | ret = -ENOMEM; | ||
225 | goto exit; | ||
226 | } | ||
227 | |||
228 | cmd = (struct btmrvl_cmd *) skb->tail; | ||
229 | cmd->ocf_ogf = cpu_to_le16((OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE); | ||
230 | cmd->length = 0; | ||
231 | |||
232 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | ||
233 | |||
234 | skb_put(skb, sizeof(*cmd)); | ||
235 | skb->dev = (void *)priv->btmrvl_dev.hcidev; | ||
236 | skb_queue_head(&priv->adapter->tx_queue, skb); | ||
237 | |||
238 | BT_DBG("Queue hs enable Command"); | ||
239 | |||
240 | wake_up_interruptible(&priv->main_thread.wait_q); | ||
241 | |||
242 | if (!wait_event_interruptible_timeout( | ||
243 | priv->adapter->cmd_wait_q, | ||
244 | priv->adapter->hs_state, | ||
245 | msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) { | ||
246 | ret = -ETIMEDOUT; | ||
247 | BT_ERR("timeout: %d, %d,%d", | ||
248 | priv->adapter->hs_state, | ||
249 | priv->adapter->ps_state, | ||
250 | priv->adapter->wakeup_tries); | ||
251 | } | ||
252 | |||
253 | exit: | ||
254 | BT_DBG("Leave"); | ||
255 | |||
256 | return ret; | ||
257 | } | ||
258 | |||
259 | int btmrvl_prepare_command(struct btmrvl_private *priv) | ||
260 | { | ||
261 | struct sk_buff *skb = NULL; | ||
262 | u8 ret = 0; | ||
263 | struct btmrvl_cmd *cmd; | ||
264 | |||
265 | BT_DBG("Enter"); | ||
266 | |||
267 | if (priv->btmrvl_dev.hscfgcmd) { | ||
268 | priv->btmrvl_dev.hscfgcmd = 0; | ||
269 | |||
270 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
271 | if (skb == NULL) { | ||
272 | BT_ERR("No free skb"); | ||
273 | ret = -ENOMEM; | ||
274 | goto exit; | ||
275 | } | ||
276 | |||
277 | cmd = (struct btmrvl_cmd *) skb->tail; | ||
278 | cmd->ocf_ogf = cpu_to_le16((OGF << 10) | | ||
279 | BT_CMD_HOST_SLEEP_CONFIG); | ||
280 | cmd->length = 2; | ||
281 | cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; | ||
282 | cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); | ||
283 | |||
284 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | ||
285 | |||
286 | skb_put(skb, sizeof(*cmd)); | ||
287 | skb->dev = (void *)priv->btmrvl_dev.hcidev; | ||
288 | skb_queue_head(&priv->adapter->tx_queue, skb); | ||
289 | |||
290 | BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x", | ||
291 | cmd->data[0], cmd->data[1]); | ||
292 | } | ||
293 | |||
294 | if (priv->btmrvl_dev.pscmd) { | ||
295 | priv->btmrvl_dev.pscmd = 0; | ||
296 | |||
297 | skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); | ||
298 | if (skb == NULL) { | ||
299 | BT_ERR("No free skb"); | ||
300 | ret = -ENOMEM; | ||
301 | goto exit; | ||
302 | } | ||
303 | |||
304 | cmd = (struct btmrvl_cmd *) skb->tail; | ||
305 | cmd->ocf_ogf = cpu_to_le16((OGF << 10) | | ||
306 | BT_CMD_AUTO_SLEEP_MODE); | ||
307 | cmd->length = 1; | ||
308 | |||
309 | if (priv->btmrvl_dev.psmode) | ||
310 | cmd->data[0] = BT_PS_ENABLE; | ||
311 | else | ||
312 | cmd->data[0] = BT_PS_DISABLE; | ||
313 | |||
314 | bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; | ||
315 | |||
316 | skb_put(skb, sizeof(*cmd)); | ||
317 | skb->dev = (void *)priv->btmrvl_dev.hcidev; | ||
318 | skb_queue_head(&priv->adapter->tx_queue, skb); | ||
319 | |||
320 | BT_DBG("Queue PSMODE Command:%d", cmd->data[0]); | ||
321 | } | ||
322 | |||
323 | if (priv->btmrvl_dev.hscmd) { | ||
324 | priv->btmrvl_dev.hscmd = 0; | ||
325 | |||
326 | if (priv->btmrvl_dev.hsmode) { | ||
327 | ret = btmrvl_enable_hs(priv); | ||
328 | } else { | ||
329 | ret = priv->hw_wakeup_firmware(priv); | ||
330 | priv->adapter->hs_state = HS_DEACTIVATED; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | exit: | ||
335 | BT_DBG("Leave"); | ||
336 | |||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) | ||
341 | { | ||
342 | u8 ret = 0; | ||
343 | |||
344 | BT_DBG("Enter"); | ||
345 | |||
346 | if (!skb || !skb->data) { | ||
347 | BT_DBG("Leave"); | ||
348 | return -EINVAL; | ||
349 | } | ||
350 | |||
351 | if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) { | ||
352 | BT_ERR("Tx Error: Bad skb length %d : %d", | ||
353 | skb->len, BTM_UPLD_SIZE); | ||
354 | BT_DBG("Leave"); | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | |||
358 | if (skb_headroom(skb) < BTM_HEADER_LEN) { | ||
359 | struct sk_buff *tmp = skb; | ||
360 | |||
361 | skb = skb_realloc_headroom(skb, BTM_HEADER_LEN); | ||
362 | if (!skb) { | ||
363 | BT_ERR("Tx Error: realloc_headroom failed %d", | ||
364 | BTM_HEADER_LEN); | ||
365 | skb = tmp; | ||
366 | BT_DBG("Leave"); | ||
367 | return -EINVAL; | ||
368 | } | ||
369 | |||
370 | kfree_skb(tmp); | ||
371 | } | ||
372 | |||
373 | skb_push(skb, BTM_HEADER_LEN); | ||
374 | |||
375 | /* header type: byte[3] | ||
376 | * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor | ||
377 | * header length: byte[2][1][0] | ||
378 | */ | ||
379 | |||
380 | skb->data[0] = (skb->len & 0x0000ff); | ||
381 | skb->data[1] = (skb->len & 0x00ff00) >> 8; | ||
382 | skb->data[2] = (skb->len & 0xff0000) >> 16; | ||
383 | skb->data[3] = bt_cb(skb)->pkt_type; | ||
384 | |||
385 | if (priv->hw_host_to_card) | ||
386 | ret = priv->hw_host_to_card(priv, skb->data, skb->len); | ||
387 | |||
388 | BT_DBG("Leave"); | ||
389 | |||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | static void btmrvl_init_adapter(struct btmrvl_private *priv) | ||
394 | { | ||
395 | BT_DBG("Enter"); | ||
396 | |||
397 | skb_queue_head_init(&priv->adapter->tx_queue); | ||
398 | |||
399 | priv->adapter->ps_state = PS_AWAKE; | ||
400 | |||
401 | init_waitqueue_head(&priv->adapter->cmd_wait_q); | ||
402 | |||
403 | BT_DBG("Leave"); | ||
404 | } | ||
405 | |||
406 | static void btmrvl_free_adapter(struct btmrvl_private *priv) | ||
407 | { | ||
408 | BT_DBG("Enter"); | ||
409 | |||
410 | skb_queue_purge(&priv->adapter->tx_queue); | ||
411 | |||
412 | kfree(priv->adapter); | ||
413 | |||
414 | priv->adapter = NULL; | ||
415 | |||
416 | BT_DBG("Leave"); | ||
417 | } | ||
418 | |||
419 | static int | ||
420 | btmrvl_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) | ||
421 | { | ||
422 | BT_DBG("Enter"); | ||
423 | |||
424 | BT_DBG("Leave"); | ||
425 | |||
426 | return -ENOIOCTLCMD; | ||
427 | } | ||
428 | |||
429 | static void btmrvl_destruct(struct hci_dev *hdev) | ||
430 | { | ||
431 | BT_DBG("Enter"); | ||
432 | |||
433 | BT_DBG("Leave"); | ||
434 | } | ||
435 | |||
436 | static int btmrvl_send_frame(struct sk_buff *skb) | ||
437 | { | ||
438 | struct hci_dev *hdev = (struct hci_dev *)skb->dev; | ||
439 | struct btmrvl_private *priv = NULL; | ||
440 | |||
441 | BT_DBG("Enter: type=%d, len=%d", skb->pkt_type, skb->len); | ||
442 | |||
443 | if (!hdev || !hdev->driver_data) { | ||
444 | BT_ERR("Frame for unknown HCI device"); | ||
445 | BT_DBG("Leave"); | ||
446 | return -ENODEV; | ||
447 | } | ||
448 | |||
449 | priv = (struct btmrvl_private *)hdev->driver_data; | ||
450 | if (!test_bit(HCI_RUNNING, &hdev->flags)) { | ||
451 | BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags); | ||
452 | print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET, | ||
453 | skb->data, skb->len); | ||
454 | BT_DBG("Leave"); | ||
455 | return -EBUSY; | ||
456 | } | ||
457 | |||
458 | switch (bt_cb(skb)->pkt_type) { | ||
459 | case HCI_COMMAND_PKT: | ||
460 | hdev->stat.cmd_tx++; | ||
461 | break; | ||
462 | |||
463 | case HCI_ACLDATA_PKT: | ||
464 | hdev->stat.acl_tx++; | ||
465 | break; | ||
466 | |||
467 | case HCI_SCODATA_PKT: | ||
468 | hdev->stat.sco_tx++; | ||
469 | break; | ||
470 | } | ||
471 | |||
472 | skb_queue_tail(&priv->adapter->tx_queue, skb); | ||
473 | |||
474 | wake_up_interruptible(&priv->main_thread.wait_q); | ||
475 | |||
476 | BT_DBG("Leave"); | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static int btmrvl_flush(struct hci_dev *hdev) | ||
482 | { | ||
483 | struct btmrvl_private *priv = | ||
484 | (struct btmrvl_private *) hdev->driver_data; | ||
485 | |||
486 | BT_DBG("Enter"); | ||
487 | |||
488 | skb_queue_purge(&priv->adapter->tx_queue); | ||
489 | |||
490 | BT_DBG("Leave"); | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int btmrvl_close(struct hci_dev *hdev) | ||
496 | { | ||
497 | struct btmrvl_private *priv = | ||
498 | (struct btmrvl_private *) hdev->driver_data; | ||
499 | |||
500 | BT_DBG("Enter"); | ||
501 | |||
502 | if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) { | ||
503 | BT_DBG("Leave"); | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | skb_queue_purge(&priv->adapter->tx_queue); | ||
508 | |||
509 | BT_DBG("Leave"); | ||
510 | |||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | static int btmrvl_open(struct hci_dev *hdev) | ||
515 | { | ||
516 | BT_DBG("Enter"); | ||
517 | |||
518 | set_bit(HCI_RUNNING, &hdev->flags); | ||
519 | |||
520 | BT_DBG("Leave"); | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * This function handles the event generated by firmware, rx data | ||
527 | * received from firmware, and tx data sent from kernel. | ||
528 | */ | ||
529 | static int btmrvl_service_main_thread(void *data) | ||
530 | { | ||
531 | struct btmrvl_thread *thread = data; | ||
532 | struct btmrvl_private *priv = thread->priv; | ||
533 | struct btmrvl_adapter *adapter = priv->adapter; | ||
534 | wait_queue_t wait; | ||
535 | struct sk_buff *skb; | ||
536 | ulong flags; | ||
537 | |||
538 | BT_DBG("Enter"); | ||
539 | |||
540 | init_waitqueue_entry(&wait, current); | ||
541 | |||
542 | current->flags |= PF_NOFREEZE; | ||
543 | |||
544 | for (;;) { | ||
545 | add_wait_queue(&thread->wait_q, &wait); | ||
546 | |||
547 | set_current_state(TASK_INTERRUPTIBLE); | ||
548 | |||
549 | if (adapter->wakeup_tries || | ||
550 | ((!adapter->int_count) && | ||
551 | (!priv->btmrvl_dev.tx_dnld_rdy || | ||
552 | skb_queue_empty(&adapter->tx_queue)))) { | ||
553 | BT_DBG("main_thread is sleeping..."); | ||
554 | schedule(); | ||
555 | } | ||
556 | |||
557 | set_current_state(TASK_RUNNING); | ||
558 | |||
559 | remove_wait_queue(&thread->wait_q, &wait); | ||
560 | |||
561 | BT_DBG("main_thread woke up"); | ||
562 | |||
563 | if (kthread_should_stop()) { | ||
564 | BT_DBG("main_thread: break from main thread"); | ||
565 | break; | ||
566 | } | ||
567 | |||
568 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
569 | if (adapter->int_count) { | ||
570 | adapter->int_count = 0; | ||
571 | } else if ((adapter->ps_state == PS_SLEEP) && | ||
572 | !skb_queue_empty(&adapter->tx_queue)) { | ||
573 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
574 | adapter->wakeup_tries++; | ||
575 | priv->hw_wakeup_firmware(priv); | ||
576 | continue; | ||
577 | } | ||
578 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
579 | |||
580 | if (adapter->ps_state == PS_SLEEP) | ||
581 | continue; | ||
582 | |||
583 | if (!priv->btmrvl_dev.tx_dnld_rdy) | ||
584 | continue; | ||
585 | |||
586 | skb = skb_dequeue(&adapter->tx_queue); | ||
587 | if (skb) { | ||
588 | if (btmrvl_tx_pkt(priv, skb)) | ||
589 | priv->btmrvl_dev.hcidev->stat.err_tx++; | ||
590 | else | ||
591 | priv->btmrvl_dev.hcidev->stat.byte_tx | ||
592 | += skb->len; | ||
593 | |||
594 | kfree_skb(skb); | ||
595 | } | ||
596 | } | ||
597 | |||
598 | BT_DBG("Leave"); | ||
599 | |||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | struct btmrvl_private *btmrvl_add_card(void *card) | ||
604 | { | ||
605 | struct hci_dev *hdev = NULL; | ||
606 | struct btmrvl_private *priv = NULL; | ||
607 | int ret; | ||
608 | |||
609 | BT_DBG("Enter"); | ||
610 | |||
611 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
612 | if (!priv) { | ||
613 | BT_ERR("Can not allocate priv"); | ||
614 | goto err_priv; | ||
615 | } | ||
616 | |||
617 | priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL); | ||
618 | if (!priv->adapter) { | ||
619 | BT_ERR("Allocate buffer for btmrvl_adapter failed!"); | ||
620 | goto err_adapter; | ||
621 | } | ||
622 | |||
623 | btmrvl_init_adapter(priv); | ||
624 | |||
625 | hdev = hci_alloc_dev(); | ||
626 | if (!hdev) { | ||
627 | BT_ERR("Can not allocate HCI device"); | ||
628 | goto err_hdev; | ||
629 | } | ||
630 | |||
631 | BT_DBG("Starting kthread..."); | ||
632 | priv->main_thread.priv = priv; | ||
633 | spin_lock_init(&priv->driver_lock); | ||
634 | |||
635 | init_waitqueue_head(&priv->main_thread.wait_q); | ||
636 | priv->main_thread.task = kthread_run(btmrvl_service_main_thread, | ||
637 | &priv->main_thread, "btmrvl_main_service"); | ||
638 | |||
639 | priv->btmrvl_dev.hcidev = hdev; | ||
640 | priv->btmrvl_dev.card = card; | ||
641 | |||
642 | hdev->driver_data = priv; | ||
643 | |||
644 | priv->btmrvl_dev.tx_dnld_rdy = true; | ||
645 | |||
646 | hdev->type = HCI_SDIO; | ||
647 | hdev->open = btmrvl_open; | ||
648 | hdev->close = btmrvl_close; | ||
649 | hdev->flush = btmrvl_flush; | ||
650 | hdev->send = btmrvl_send_frame; | ||
651 | hdev->destruct = btmrvl_destruct; | ||
652 | hdev->ioctl = btmrvl_ioctl; | ||
653 | hdev->owner = THIS_MODULE; | ||
654 | |||
655 | ret = hci_register_dev(hdev); | ||
656 | if (ret < 0) { | ||
657 | BT_ERR("Can not register HCI device"); | ||
658 | goto err_hci_register_dev; | ||
659 | } | ||
660 | |||
661 | BT_DBG("Leave"); | ||
662 | return priv; | ||
663 | |||
664 | err_hci_register_dev: | ||
665 | /* Stop the thread servicing the interrupts */ | ||
666 | kthread_stop(priv->main_thread.task); | ||
667 | |||
668 | hci_free_dev(hdev); | ||
669 | |||
670 | err_hdev: | ||
671 | btmrvl_free_adapter(priv); | ||
672 | |||
673 | err_adapter: | ||
674 | kfree(priv); | ||
675 | |||
676 | err_priv: | ||
677 | BT_DBG("Leave"); | ||
678 | |||
679 | return NULL; | ||
680 | } | ||
681 | EXPORT_SYMBOL_GPL(btmrvl_add_card); | ||
682 | |||
683 | int btmrvl_remove_card(struct btmrvl_private *priv) | ||
684 | { | ||
685 | struct hci_dev *hdev; | ||
686 | |||
687 | BT_DBG("Enter"); | ||
688 | |||
689 | hdev = priv->btmrvl_dev.hcidev; | ||
690 | |||
691 | wake_up_interruptible(&priv->adapter->cmd_wait_q); | ||
692 | |||
693 | kthread_stop(priv->main_thread.task); | ||
694 | |||
695 | hci_unregister_dev(hdev); | ||
696 | |||
697 | hci_free_dev(hdev); | ||
698 | |||
699 | priv->btmrvl_dev.hcidev = NULL; | ||
700 | |||
701 | btmrvl_free_adapter(priv); | ||
702 | |||
703 | kfree(priv); | ||
704 | |||
705 | BT_DBG("Leave"); | ||
706 | |||
707 | return 0; | ||
708 | } | ||
709 | EXPORT_SYMBOL_GPL(btmrvl_remove_card); | ||
710 | |||
711 | MODULE_AUTHOR("Marvell International Ltd."); | ||
712 | MODULE_DESCRIPTION("Marvell Bluetooth Driver v" VERSION); | ||
713 | MODULE_VERSION(VERSION); | ||
714 | MODULE_LICENSE("GPL v2"); | ||