blob: a32184946565b8eb0c8fbb74170d5e593e1c4300 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * HCI_SMD (HCI Shared Memory Driver) is Qualcomm's Shared memory driver
3 * for the BT HCI protocol.
4 *
5 * Copyright (c) 2000-2001, 2011 Code Aurora Forum. All rights reserved.
6 * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
7 * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
8 *
9 * This file is based on drivers/bluetooth/hci_vhci.c
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 */
20
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/errno.h>
25#include <linux/string.h>
26#include <linux/skbuff.h>
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -070027#include <linux/wakelock.h>
28#include <linux/uaccess.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029#include <net/bluetooth/bluetooth.h>
30#include <net/bluetooth/hci_core.h>
31#include <net/bluetooth/hci.h>
32#include <mach/msm_smd.h>
33
Bhasker Netid08d9012011-09-05 19:37:54 +053034#define EVENT_CHANNEL "APPS_RIVA_BT_CMD"
35#define DATA_CHANNEL "APPS_RIVA_BT_ACL"
36#define RX_Q_MONITOR (1) /* 1 milli second */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -070038
39static unsigned int driver_state;
40
41static int hcismd_set;
42static DEFINE_MUTEX(hci_smd_enable);
43
44static int hcismd_set_enable(const char *val, struct kernel_param *kp);
45module_param_call(hcismd_set, hcismd_set_enable, NULL, &hcismd_set, 0644);
46
47
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070048struct hci_smd_data {
49 struct hci_dev *hdev;
50
51 struct smd_channel *event_channel;
52 struct smd_channel *data_channel;
Bhasker Netid08d9012011-09-05 19:37:54 +053053 struct wake_lock wake_lock_tx;
54 struct wake_lock wake_lock_rx;
55 struct timer_list rx_q_timer;
Ankur Nandwani034ed892011-10-07 11:15:53 -070056 struct tasklet_struct hci_event_task;
57 struct tasklet_struct hci_data_task;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058};
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -070059static struct hci_smd_data hs;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060
Bhasker Netid08d9012011-09-05 19:37:54 +053061/* Rx queue monitor timer function */
62static int is_rx_q_empty(unsigned long arg)
63{
64 struct hci_dev *hdev = (struct hci_dev *) arg;
65 struct sk_buff_head *list_ = &hdev->rx_q;
66 struct sk_buff *list = ((struct sk_buff *)list_)->next;
67 BT_DBG("%s Rx timer triggered", hdev->name);
68
69 if (list == (struct sk_buff *)list_) {
70 BT_DBG("%s RX queue empty", hdev->name);
71 return 1;
72 } else{
73 BT_DBG("%s RX queue not empty", hdev->name);
74 return 0;
75 }
76}
77
78static void release_lock(void)
79{
80 struct hci_smd_data *hsmd = &hs;
81 BT_DBG("Releasing Rx Lock");
82 if (is_rx_q_empty((unsigned long)hsmd->hdev) &&
83 wake_lock_active(&hs.wake_lock_rx))
84 wake_unlock(&hs.wake_lock_rx);
85}
86
87/* Rx timer callback function */
88static void schedule_timer(unsigned long arg)
89{
90 struct hci_dev *hdev = (struct hci_dev *) arg;
91 struct hci_smd_data *hsmd = &hs;
92 BT_DBG("%s Schedule Rx timer", hdev->name);
93
94 if (is_rx_q_empty(arg) && wake_lock_active(&hs.wake_lock_rx)) {
95 BT_DBG("%s RX queue empty", hdev->name);
96 /*
97 * Since the queue is empty, its ideal
98 * to release the wake lock on Rx
99 */
100 wake_unlock(&hs.wake_lock_rx);
101 } else{
102 BT_DBG("%s RX queue not empty", hdev->name);
103 /*
104 * Restart the timer to monitor whether the Rx queue is
105 * empty for releasing the Rx wake lock
106 */
107 mod_timer(&hsmd->rx_q_timer,
108 jiffies + msecs_to_jiffies(RX_Q_MONITOR));
109 }
110}
111
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112static int hci_smd_open(struct hci_dev *hdev)
113{
114 set_bit(HCI_RUNNING, &hdev->flags);
115 return 0;
116}
117
118
119static int hci_smd_close(struct hci_dev *hdev)
120{
121 if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
122 return 0;
123 else
124 return -EPERM;
125}
126
127
128static void hci_smd_destruct(struct hci_dev *hdev)
129{
130 kfree(hdev->driver_data);
131}
132
133static void hci_smd_recv_data(unsigned long arg)
134{
Bhasker Netid08d9012011-09-05 19:37:54 +0530135 int len = 0;
136 int rc = 0;
137 struct sk_buff *skb = NULL;
138 unsigned char *buf = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139 struct hci_smd_data *hsmd = &hs;
Bhasker Netid08d9012011-09-05 19:37:54 +0530140 wake_lock(&hs.wake_lock_rx);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141
142 len = smd_read_avail(hsmd->data_channel);
Bhasker Neti60734c02011-11-23 15:17:54 -0800143 if (len > HCI_MAX_FRAME_SIZE) {
144 BT_ERR("Frame larger than the allowed size");
Bhasker Netid08d9012011-09-05 19:37:54 +0530145 goto out_data;
146 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 while (len > 0) {
Ankur Nandwanic8150962011-08-24 13:06:23 -0700148 skb = bt_skb_alloc(len, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 if (!skb) {
Bhasker Neti60734c02011-11-23 15:17:54 -0800150 BT_ERR("Error in allocating socket buffer");
Bhasker Netid08d9012011-09-05 19:37:54 +0530151 goto out_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 }
153
Ankur Nandwanic8150962011-08-24 13:06:23 -0700154 buf = kmalloc(len, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155 if (!buf) {
Bhasker Neti60734c02011-11-23 15:17:54 -0800156 BT_ERR("Error in allocating buffer");
Bhasker Netid08d9012011-09-05 19:37:54 +0530157 rc = -ENOMEM;
158 goto out_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 }
160
Ankur Nandwani531333f2011-10-25 15:47:43 -0700161 rc = smd_read(hsmd->data_channel, (void *)buf, len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 if (rc < len) {
163 BT_ERR("Error in reading from the channel");
Bhasker Netid08d9012011-09-05 19:37:54 +0530164 goto out_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165 }
166
167 memcpy(skb_put(skb, len), buf, len);
168 skb->dev = (void *)hsmd->hdev;
169 bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
170
171 skb_orphan(skb);
172
173 rc = hci_recv_frame(skb);
174 if (rc < 0) {
175 BT_ERR("Error in passing the packet to HCI Layer");
Bhasker Neti44a792b2011-10-11 19:25:22 +0530176 /*
177 * skb is getting freed in hci_recv_frame, making it
178 * to null to avoid multiple access
179 */
180 skb = NULL;
Bhasker Netid08d9012011-09-05 19:37:54 +0530181 goto out_data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182 }
183
184 kfree(buf);
Bhasker Netid08d9012011-09-05 19:37:54 +0530185 buf = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186 len = smd_read_avail(hsmd->data_channel);
Bhasker Netid08d9012011-09-05 19:37:54 +0530187 /*
188 * Start the timer to monitor whether the Rx queue is
189 * empty for releasing the Rx wake lock
190 */
Bhasker Neti60734c02011-11-23 15:17:54 -0800191 BT_DBG("Rx Timer is starting");
Bhasker Netid08d9012011-09-05 19:37:54 +0530192 mod_timer(&hsmd->rx_q_timer,
193 jiffies + msecs_to_jiffies(RX_Q_MONITOR));
194 }
195out_data:
196 release_lock();
197 if (rc) {
198 if (skb)
199 kfree_skb(skb);
200 kfree(buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 }
202}
203
204static void hci_smd_recv_event(unsigned long arg)
205{
Bhasker Netid08d9012011-09-05 19:37:54 +0530206 int len = 0;
207 int rc = 0;
208 struct sk_buff *skb = NULL;
209 unsigned char *buf = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 struct hci_smd_data *hsmd = &hs;
Bhasker Netid08d9012011-09-05 19:37:54 +0530211 wake_lock(&hs.wake_lock_rx);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212
213 len = smd_read_avail(hsmd->event_channel);
214 if (len > HCI_MAX_FRAME_SIZE) {
215 BT_ERR("Frame larger than the allowed size");
Bhasker Netid08d9012011-09-05 19:37:54 +0530216 goto out_event;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 }
218
219 while (len > 0) {
Ankur Nandwanic8150962011-08-24 13:06:23 -0700220 skb = bt_skb_alloc(len, GFP_ATOMIC);
Bhasker Netid08d9012011-09-05 19:37:54 +0530221 if (!skb) {
Bhasker Neti60734c02011-11-23 15:17:54 -0800222 BT_ERR("Error in allocating socket buffer");
Bhasker Netid08d9012011-09-05 19:37:54 +0530223 goto out_event;
224 }
Ankur Nandwanic8150962011-08-24 13:06:23 -0700225 buf = kmalloc(len, GFP_ATOMIC);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 if (!buf) {
Bhasker Neti60734c02011-11-23 15:17:54 -0800227 BT_ERR("Error in allocating buffer");
Bhasker Netid08d9012011-09-05 19:37:54 +0530228 rc = -ENOMEM;
229 goto out_event;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 }
Ankur Nandwani531333f2011-10-25 15:47:43 -0700231 rc = smd_read(hsmd->event_channel, (void *)buf, len);
Bhasker Netid08d9012011-09-05 19:37:54 +0530232 if (rc < len) {
233 BT_ERR("Error in reading from the event channel");
234 goto out_event;
235 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236
237 memcpy(skb_put(skb, len), buf, len);
238 skb->dev = (void *)hsmd->hdev;
239 bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
240
241 skb_orphan(skb);
242
243 rc = hci_recv_frame(skb);
244 if (rc < 0) {
245 BT_ERR("Error in passing the packet to HCI Layer");
Bhasker Neti44a792b2011-10-11 19:25:22 +0530246 /*
247 * skb is getting freed in hci_recv_frame, making it
248 * to null to avoid multiple access
249 */
250 skb = NULL;
Bhasker Netid08d9012011-09-05 19:37:54 +0530251 goto out_event;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252 }
253
254 kfree(buf);
Bhasker Netid08d9012011-09-05 19:37:54 +0530255 buf = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256 len = smd_read_avail(hsmd->event_channel);
Bhasker Netid08d9012011-09-05 19:37:54 +0530257 /*
258 * Start the timer to monitor whether the Rx queue is
259 * empty for releasing the Rx wake lock
260 */
Bhasker Neti60734c02011-11-23 15:17:54 -0800261 BT_DBG("Rx Timer is starting");
Bhasker Netid08d9012011-09-05 19:37:54 +0530262 mod_timer(&hsmd->rx_q_timer,
263 jiffies + msecs_to_jiffies(RX_Q_MONITOR));
264 }
265out_event:
266 release_lock();
267 if (rc) {
268 if (skb)
269 kfree_skb(skb);
270 kfree(buf);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271 }
272}
273
274static int hci_smd_send_frame(struct sk_buff *skb)
275{
276 int len;
Bhasker Netid08d9012011-09-05 19:37:54 +0530277 int avail;
278 int ret = 0;
279 wake_lock(&hs.wake_lock_tx);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280
281 switch (bt_cb(skb)->pkt_type) {
282 case HCI_COMMAND_PKT:
Bhasker Netid08d9012011-09-05 19:37:54 +0530283 avail = smd_write_avail(hs.event_channel);
284 if (!avail) {
285 BT_ERR("No space available for smd frame");
286 ret = -ENOSPC;
287 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288 len = smd_write(hs.event_channel, skb->data, skb->len);
289 if (len < skb->len) {
290 BT_ERR("Failed to write Command %d", len);
Bhasker Netid08d9012011-09-05 19:37:54 +0530291 ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292 }
293 break;
294 case HCI_ACLDATA_PKT:
295 case HCI_SCODATA_PKT:
Ankur Nandwani23abeb22011-10-26 16:35:22 -0700296 avail = smd_write_avail(hs.data_channel);
Bhasker Netid08d9012011-09-05 19:37:54 +0530297 if (!avail) {
298 BT_ERR("No space available for smd frame");
299 ret = -ENOSPC;
300 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 len = smd_write(hs.data_channel, skb->data, skb->len);
302 if (len < skb->len) {
303 BT_ERR("Failed to write Data %d", len);
Bhasker Netid08d9012011-09-05 19:37:54 +0530304 ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700305 }
306 break;
307 default:
Bhasker Neti60734c02011-11-23 15:17:54 -0800308 BT_ERR("Uknown packet type");
Bhasker Netid08d9012011-09-05 19:37:54 +0530309 ret = -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 break;
311 }
Bhasker Netid08d9012011-09-05 19:37:54 +0530312
Anubhav Guptaddf48ec2011-10-03 14:24:13 +0530313 kfree_skb(skb);
Bhasker Netid08d9012011-09-05 19:37:54 +0530314 wake_unlock(&hs.wake_lock_tx);
315 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316}
317
318
319static void hci_smd_notify_event(void *data, unsigned int event)
320{
321 struct hci_dev *hdev = hs.hdev;
Bhasker Neti60734c02011-11-23 15:17:54 -0800322 struct hci_smd_data *hsmd = &hs;
323 int len = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324
325 if (!hdev) {
326 BT_ERR("Frame for unknown HCI device (hdev=NULL)");
327 return;
328 }
329
330 switch (event) {
331 case SMD_EVENT_DATA:
Bhasker Neti60734c02011-11-23 15:17:54 -0800332 len = smd_read_avail(hsmd->event_channel);
333 if (len > 0)
334 tasklet_hi_schedule(&hs.hci_event_task);
335 else if (len < 0)
336 BT_ERR("Failed to read event from smd %d", len);
337
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338 break;
339 case SMD_EVENT_OPEN:
Bhasker Neti60734c02011-11-23 15:17:54 -0800340 BT_INFO("opening HCI-SMD channel :%s", EVENT_CHANNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700341 hci_smd_open(hdev);
342 break;
343 case SMD_EVENT_CLOSE:
Bhasker Neti60734c02011-11-23 15:17:54 -0800344 BT_INFO("Closing HCI-SMD channel :%s", EVENT_CHANNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345 hci_smd_close(hdev);
346 break;
347 default:
348 break;
349 }
350}
351
352static void hci_smd_notify_data(void *data, unsigned int event)
353{
354 struct hci_dev *hdev = hs.hdev;
Bhasker Neti60734c02011-11-23 15:17:54 -0800355 struct hci_smd_data *hsmd = &hs;
356 int len = 0;
357
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700358 if (!hdev) {
359 BT_ERR("HCI device (hdev=NULL)");
360 return;
361 }
362
363 switch (event) {
364 case SMD_EVENT_DATA:
Bhasker Neti60734c02011-11-23 15:17:54 -0800365 len = smd_read_avail(hsmd->data_channel);
366 if (len > 0)
367 tasklet_hi_schedule(&hs.hci_data_task);
368 else if (len < 0)
369 BT_ERR("Failed to read data from smd %d", len);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 break;
371 case SMD_EVENT_OPEN:
Bhasker Neti60734c02011-11-23 15:17:54 -0800372 BT_INFO("opening HCI-SMD channel :%s", DATA_CHANNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700373 hci_smd_open(hdev);
374 break;
375 case SMD_EVENT_CLOSE:
Bhasker Neti60734c02011-11-23 15:17:54 -0800376 BT_INFO("Closing HCI-SMD channel :%s", DATA_CHANNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 hci_smd_close(hdev);
378 break;
379 default:
380 break;
381 }
382
383}
384
385static int hci_smd_register_dev(struct hci_smd_data *hsmd)
386{
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700387 static struct hci_dev *hdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 int rc;
389
390 /* Initialize and register HCI device */
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700391 if (!driver_state) {
392 hdev = hci_alloc_dev();
393 if (!hdev) {
394 BT_ERR("Can't allocate HCI device");
395 return -ENOMEM;
396 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700398 hsmd->hdev = hdev;
399 hdev->bus = HCI_SMD;
400 hdev->driver_data = hsmd;
401 hdev->open = hci_smd_open;
402 hdev->close = hci_smd_close;
403 hdev->send = hci_smd_send_frame;
404 hdev->destruct = hci_smd_destruct;
405 hdev->owner = THIS_MODULE;
406 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
Ankur Nandwani034ed892011-10-07 11:15:53 -0700408 tasklet_init(&hsmd->hci_event_task,
409 hci_smd_recv_event, (unsigned long) hsmd);
410 tasklet_init(&hsmd->hci_data_task,
411 hci_smd_recv_data, (unsigned long) hsmd);
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700412 if (!driver_state) {
413 wake_lock_init(&hs.wake_lock_rx, WAKE_LOCK_SUSPEND,
414 "msm_smd_Rx");
415 wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
416 "msm_smd_Tx");
417 }
Bhasker Netid08d9012011-09-05 19:37:54 +0530418 /*
419 * Setup the timer to monitor whether the Rx queue is empty,
420 * to control the wake lock release
421 */
422 setup_timer(&hsmd->rx_q_timer, schedule_timer,
423 (unsigned long) hsmd->hdev);
424
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425 /* Open the SMD Channel and device and register the callback function */
426 rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS,
427 &hsmd->event_channel, hdev, hci_smd_notify_event);
428 if (rc < 0) {
429 BT_ERR("Cannot open the command channel");
430 hci_free_dev(hdev);
431 return -ENODEV;
432 }
433
434 rc = smd_named_open_on_edge(DATA_CHANNEL, SMD_APPS_WCNSS,
435 &hsmd->data_channel, hdev, hci_smd_notify_data);
436 if (rc < 0) {
Bhasker Neti60734c02011-11-23 15:17:54 -0800437 BT_ERR("Failed to open the Data channel");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438 hci_free_dev(hdev);
439 return -ENODEV;
440 }
441
442 /* Disable the read interrupts on the channel */
443 smd_disable_read_intr(hsmd->event_channel);
444 smd_disable_read_intr(hsmd->data_channel);
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700445 if (!driver_state) {
446 if (hci_register_dev(hdev) < 0) {
447 BT_ERR("Can't register HCI device");
448 hci_free_dev(hdev);
449 return -ENODEV;
450 }
451 driver_state = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453 return 0;
454}
455
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700456static void hci_smd_deregister_dev(void)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457{
458 smd_close(hs.event_channel);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700459 smd_close(hs.data_channel);
Bhasker Netid08d9012011-09-05 19:37:54 +0530460
Ankur Nandwani327e0502011-11-11 15:47:25 -0800461 if (wake_lock_active(&hs.wake_lock_rx))
462 wake_unlock(&hs.wake_lock_rx);
463
Bhasker Netid08d9012011-09-05 19:37:54 +0530464 /*Destroy the timer used to monitor the Rx queue for emptiness */
465 del_timer_sync(&hs.rx_q_timer);
Ankur Nandwani034ed892011-10-07 11:15:53 -0700466 tasklet_kill(&hs.hci_event_task);
467 tasklet_kill(&hs.hci_data_task);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700468}
469
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700470static int hcismd_set_enable(const char *val, struct kernel_param *kp)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471{
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700472 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700474 mutex_lock(&hci_smd_enable);
475
476 ret = param_set_int(val, kp);
477
478 if (ret)
479 goto done;
480
481 switch (hcismd_set) {
482
483 case 1:
484 hci_smd_register_dev(&hs);
485 break;
486 case 0:
487 hci_smd_deregister_dev();
488 break;
489 default:
490 ret = -EFAULT;
491 }
492
493done:
494 mutex_unlock(&hci_smd_enable);
495 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700496}
Ankur Nandwani8ffe4e72011-10-10 21:51:48 -0700497
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700498
499MODULE_AUTHOR("Ankur Nandwani <ankurn@codeaurora.org>");
500MODULE_DESCRIPTION("Bluetooth SMD driver");
501MODULE_LICENSE("GPL v2");