diff options
Diffstat (limited to 'drivers/bluetooth/hci_vhci.c')
-rw-r--r-- | drivers/bluetooth/hci_vhci.c | 368 |
1 files changed, 194 insertions, 174 deletions
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index f9b956fb2b8b..4aa5dfff12be 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c | |||
@@ -1,229 +1,220 @@ | |||
1 | /* | ||
2 | BlueZ - Bluetooth protocol stack for Linux | ||
3 | Copyright (C) 2000-2001 Qualcomm Incorporated | ||
4 | |||
5 | Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com> | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License version 2 as | ||
9 | published by the Free Software Foundation; | ||
10 | |||
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||
12 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. | ||
14 | IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY | ||
15 | CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES | ||
16 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
17 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
18 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
19 | |||
20 | ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, | ||
21 | COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS | ||
22 | SOFTWARE IS DISCLAIMED. | ||
23 | */ | ||
24 | |||
25 | /* | 1 | /* |
26 | * Bluetooth HCI virtual device driver. | ||
27 | * | 2 | * |
28 | * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ | 3 | * Bluetooth virtual HCI driver |
4 | * | ||
5 | * Copyright (C) 2000-2001 Qualcomm Incorporated | ||
6 | * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> | ||
7 | * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> | ||
8 | * | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
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 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
29 | */ | 24 | */ |
30 | #define VERSION "1.1" | ||
31 | 25 | ||
32 | #include <linux/config.h> | 26 | #include <linux/config.h> |
33 | #include <linux/module.h> | 27 | #include <linux/module.h> |
34 | 28 | ||
35 | #include <linux/errno.h> | ||
36 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
37 | #include <linux/major.h> | 30 | #include <linux/init.h> |
38 | #include <linux/sched.h> | ||
39 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/types.h> | ||
33 | #include <linux/errno.h> | ||
34 | #include <linux/sched.h> | ||
40 | #include <linux/poll.h> | 35 | #include <linux/poll.h> |
41 | #include <linux/fcntl.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/random.h> | ||
44 | 36 | ||
45 | #include <linux/skbuff.h> | 37 | #include <linux/skbuff.h> |
46 | #include <linux/miscdevice.h> | 38 | #include <linux/miscdevice.h> |
47 | 39 | ||
48 | #include <asm/system.h> | ||
49 | #include <asm/uaccess.h> | ||
50 | |||
51 | #include <net/bluetooth/bluetooth.h> | 40 | #include <net/bluetooth/bluetooth.h> |
52 | #include <net/bluetooth/hci_core.h> | 41 | #include <net/bluetooth/hci_core.h> |
53 | #include "hci_vhci.h" | ||
54 | 42 | ||
55 | /* HCI device part */ | 43 | #ifndef CONFIG_BT_HCIVHCI_DEBUG |
44 | #undef BT_DBG | ||
45 | #define BT_DBG(D...) | ||
46 | #endif | ||
47 | |||
48 | #define VERSION "1.2" | ||
49 | |||
50 | static int minor = MISC_DYNAMIC_MINOR; | ||
56 | 51 | ||
57 | static int hci_vhci_open(struct hci_dev *hdev) | 52 | struct vhci_data { |
53 | struct hci_dev *hdev; | ||
54 | |||
55 | unsigned long flags; | ||
56 | |||
57 | wait_queue_head_t read_wait; | ||
58 | struct sk_buff_head readq; | ||
59 | |||
60 | struct fasync_struct *fasync; | ||
61 | }; | ||
62 | |||
63 | #define VHCI_FASYNC 0x0010 | ||
64 | |||
65 | static struct miscdevice vhci_miscdev; | ||
66 | |||
67 | static int vhci_open_dev(struct hci_dev *hdev) | ||
58 | { | 68 | { |
59 | set_bit(HCI_RUNNING, &hdev->flags); | 69 | set_bit(HCI_RUNNING, &hdev->flags); |
60 | return 0; | ||
61 | } | ||
62 | 70 | ||
63 | static int hci_vhci_flush(struct hci_dev *hdev) | ||
64 | { | ||
65 | struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; | ||
66 | skb_queue_purge(&hci_vhci->readq); | ||
67 | return 0; | 71 | return 0; |
68 | } | 72 | } |
69 | 73 | ||
70 | static int hci_vhci_close(struct hci_dev *hdev) | 74 | static int vhci_close_dev(struct hci_dev *hdev) |
71 | { | 75 | { |
76 | struct vhci_data *vhci = hdev->driver_data; | ||
77 | |||
72 | if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) | 78 | if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) |
73 | return 0; | 79 | return 0; |
74 | 80 | ||
75 | hci_vhci_flush(hdev); | 81 | skb_queue_purge(&vhci->readq); |
82 | |||
76 | return 0; | 83 | return 0; |
77 | } | 84 | } |
78 | 85 | ||
79 | static void hci_vhci_destruct(struct hci_dev *hdev) | 86 | static int vhci_flush(struct hci_dev *hdev) |
80 | { | 87 | { |
81 | struct hci_vhci_struct *vhci; | 88 | struct vhci_data *vhci = hdev->driver_data; |
82 | 89 | ||
83 | if (!hdev) return; | 90 | skb_queue_purge(&vhci->readq); |
84 | 91 | ||
85 | vhci = (struct hci_vhci_struct *) hdev->driver_data; | 92 | return 0; |
86 | kfree(vhci); | ||
87 | } | 93 | } |
88 | 94 | ||
89 | static int hci_vhci_send_frame(struct sk_buff *skb) | 95 | static int vhci_send_frame(struct sk_buff *skb) |
90 | { | 96 | { |
91 | struct hci_dev* hdev = (struct hci_dev *) skb->dev; | 97 | struct hci_dev* hdev = (struct hci_dev *) skb->dev; |
92 | struct hci_vhci_struct *hci_vhci; | 98 | struct vhci_data *vhci; |
93 | 99 | ||
94 | if (!hdev) { | 100 | if (!hdev) { |
95 | BT_ERR("Frame for uknown device (hdev=NULL)"); | 101 | BT_ERR("Frame for unknown HCI device (hdev=NULL)"); |
96 | return -ENODEV; | 102 | return -ENODEV; |
97 | } | 103 | } |
98 | 104 | ||
99 | if (!test_bit(HCI_RUNNING, &hdev->flags)) | 105 | if (!test_bit(HCI_RUNNING, &hdev->flags)) |
100 | return -EBUSY; | 106 | return -EBUSY; |
101 | 107 | ||
102 | hci_vhci = (struct hci_vhci_struct *) hdev->driver_data; | 108 | vhci = hdev->driver_data; |
103 | 109 | ||
104 | memcpy(skb_push(skb, 1), &skb->pkt_type, 1); | 110 | memcpy(skb_push(skb, 1), &skb->pkt_type, 1); |
105 | skb_queue_tail(&hci_vhci->readq, skb); | 111 | skb_queue_tail(&vhci->readq, skb); |
112 | |||
113 | if (vhci->flags & VHCI_FASYNC) | ||
114 | kill_fasync(&vhci->fasync, SIGIO, POLL_IN); | ||
106 | 115 | ||
107 | if (hci_vhci->flags & VHCI_FASYNC) | 116 | wake_up_interruptible(&vhci->read_wait); |
108 | kill_fasync(&hci_vhci->fasync, SIGIO, POLL_IN); | ||
109 | wake_up_interruptible(&hci_vhci->read_wait); | ||
110 | 117 | ||
111 | return 0; | 118 | return 0; |
112 | } | 119 | } |
113 | 120 | ||
114 | /* Character device part */ | 121 | static void vhci_destruct(struct hci_dev *hdev) |
115 | 122 | { | |
116 | /* Poll */ | 123 | kfree(hdev->driver_data); |
117 | static unsigned int hci_vhci_chr_poll(struct file *file, poll_table * wait) | ||
118 | { | ||
119 | struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; | ||
120 | |||
121 | poll_wait(file, &hci_vhci->read_wait, wait); | ||
122 | |||
123 | if (!skb_queue_empty(&hci_vhci->readq)) | ||
124 | return POLLIN | POLLRDNORM; | ||
125 | |||
126 | return POLLOUT | POLLWRNORM; | ||
127 | } | 124 | } |
128 | 125 | ||
129 | /* Get packet from user space buffer(already verified) */ | 126 | static inline ssize_t vhci_get_user(struct vhci_data *vhci, |
130 | static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const char __user *buf, size_t count) | 127 | const char __user *buf, size_t count) |
131 | { | 128 | { |
132 | struct sk_buff *skb; | 129 | struct sk_buff *skb; |
133 | 130 | ||
134 | if (count > HCI_MAX_FRAME_SIZE) | 131 | if (count > HCI_MAX_FRAME_SIZE) |
135 | return -EINVAL; | 132 | return -EINVAL; |
136 | 133 | ||
137 | if (!(skb = bt_skb_alloc(count, GFP_KERNEL))) | 134 | skb = bt_skb_alloc(count, GFP_KERNEL); |
135 | if (!skb) | ||
138 | return -ENOMEM; | 136 | return -ENOMEM; |
139 | 137 | ||
140 | if (copy_from_user(skb_put(skb, count), buf, count)) { | 138 | if (copy_from_user(skb_put(skb, count), buf, count)) { |
141 | kfree_skb(skb); | 139 | kfree_skb(skb); |
142 | return -EFAULT; | 140 | return -EFAULT; |
143 | } | 141 | } |
144 | 142 | ||
145 | skb->dev = (void *) hci_vhci->hdev; | 143 | skb->dev = (void *) vhci->hdev; |
146 | skb->pkt_type = *((__u8 *) skb->data); | 144 | skb->pkt_type = *((__u8 *) skb->data); |
147 | skb_pull(skb, 1); | 145 | skb_pull(skb, 1); |
148 | 146 | ||
149 | hci_recv_frame(skb); | 147 | hci_recv_frame(skb); |
150 | 148 | ||
151 | return count; | 149 | return count; |
152 | } | ||
153 | |||
154 | /* Write */ | ||
155 | static ssize_t hci_vhci_chr_write(struct file * file, const char __user * buf, | ||
156 | size_t count, loff_t *pos) | ||
157 | { | ||
158 | struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; | ||
159 | |||
160 | if (!access_ok(VERIFY_READ, buf, count)) | ||
161 | return -EFAULT; | ||
162 | |||
163 | return hci_vhci_get_user(hci_vhci, buf, count); | ||
164 | } | 150 | } |
165 | 151 | ||
166 | /* Put packet to user space buffer(already verified) */ | 152 | static inline ssize_t vhci_put_user(struct vhci_data *vhci, |
167 | static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci, | 153 | struct sk_buff *skb, char __user *buf, int count) |
168 | struct sk_buff *skb, char __user *buf, | ||
169 | int count) | ||
170 | { | 154 | { |
171 | int len = count, total = 0; | ||
172 | char __user *ptr = buf; | 155 | char __user *ptr = buf; |
156 | int len, total = 0; | ||
157 | |||
158 | len = min_t(unsigned int, skb->len, count); | ||
173 | 159 | ||
174 | len = min_t(unsigned int, skb->len, len); | ||
175 | if (copy_to_user(ptr, skb->data, len)) | 160 | if (copy_to_user(ptr, skb->data, len)) |
176 | return -EFAULT; | 161 | return -EFAULT; |
162 | |||
177 | total += len; | 163 | total += len; |
178 | 164 | ||
179 | hci_vhci->hdev->stat.byte_tx += len; | 165 | vhci->hdev->stat.byte_tx += len; |
166 | |||
180 | switch (skb->pkt_type) { | 167 | switch (skb->pkt_type) { |
181 | case HCI_COMMAND_PKT: | 168 | case HCI_COMMAND_PKT: |
182 | hci_vhci->hdev->stat.cmd_tx++; | 169 | vhci->hdev->stat.cmd_tx++; |
183 | break; | 170 | break; |
184 | 171 | ||
185 | case HCI_ACLDATA_PKT: | 172 | case HCI_ACLDATA_PKT: |
186 | hci_vhci->hdev->stat.acl_tx++; | 173 | vhci->hdev->stat.acl_tx++; |
187 | break; | 174 | break; |
188 | 175 | ||
189 | case HCI_SCODATA_PKT: | 176 | case HCI_SCODATA_PKT: |
190 | hci_vhci->hdev->stat.cmd_tx++; | 177 | vhci->hdev->stat.cmd_tx++; |
191 | break; | 178 | break; |
192 | }; | 179 | }; |
193 | 180 | ||
194 | return total; | 181 | return total; |
195 | } | 182 | } |
196 | 183 | ||
197 | /* Read */ | 184 | static loff_t vhci_llseek(struct file * file, loff_t offset, int origin) |
198 | static ssize_t hci_vhci_chr_read(struct file * file, char __user * buf, size_t count, loff_t *pos) | 185 | { |
186 | return -ESPIPE; | ||
187 | } | ||
188 | |||
189 | static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, loff_t *pos) | ||
199 | { | 190 | { |
200 | struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; | ||
201 | DECLARE_WAITQUEUE(wait, current); | 191 | DECLARE_WAITQUEUE(wait, current); |
192 | struct vhci_data *vhci = file->private_data; | ||
202 | struct sk_buff *skb; | 193 | struct sk_buff *skb; |
203 | ssize_t ret = 0; | 194 | ssize_t ret = 0; |
204 | 195 | ||
205 | add_wait_queue(&hci_vhci->read_wait, &wait); | 196 | add_wait_queue(&vhci->read_wait, &wait); |
206 | while (count) { | 197 | while (count) { |
207 | set_current_state(TASK_INTERRUPTIBLE); | 198 | set_current_state(TASK_INTERRUPTIBLE); |
208 | 199 | ||
209 | /* Read frames from device queue */ | 200 | skb = skb_dequeue(&vhci->readq); |
210 | if (!(skb = skb_dequeue(&hci_vhci->readq))) { | 201 | if (!skb) { |
211 | if (file->f_flags & O_NONBLOCK) { | 202 | if (file->f_flags & O_NONBLOCK) { |
212 | ret = -EAGAIN; | 203 | ret = -EAGAIN; |
213 | break; | 204 | break; |
214 | } | 205 | } |
206 | |||
215 | if (signal_pending(current)) { | 207 | if (signal_pending(current)) { |
216 | ret = -ERESTARTSYS; | 208 | ret = -ERESTARTSYS; |
217 | break; | 209 | break; |
218 | } | 210 | } |
219 | 211 | ||
220 | /* Nothing to read, let's sleep */ | ||
221 | schedule(); | 212 | schedule(); |
222 | continue; | 213 | continue; |
223 | } | 214 | } |
224 | 215 | ||
225 | if (access_ok(VERIFY_WRITE, buf, count)) | 216 | if (access_ok(VERIFY_WRITE, buf, count)) |
226 | ret = hci_vhci_put_user(hci_vhci, skb, buf, count); | 217 | ret = vhci_put_user(vhci, skb, buf, count); |
227 | else | 218 | else |
228 | ret = -EFAULT; | 219 | ret = -EFAULT; |
229 | 220 | ||
@@ -231,84 +222,90 @@ static ssize_t hci_vhci_chr_read(struct file * file, char __user * buf, size_t c | |||
231 | break; | 222 | break; |
232 | } | 223 | } |
233 | set_current_state(TASK_RUNNING); | 224 | set_current_state(TASK_RUNNING); |
234 | remove_wait_queue(&hci_vhci->read_wait, &wait); | 225 | remove_wait_queue(&vhci->read_wait, &wait); |
235 | 226 | ||
236 | return ret; | 227 | return ret; |
237 | } | 228 | } |
238 | 229 | ||
239 | static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin) | 230 | static ssize_t vhci_write(struct file *file, |
231 | const char __user *buf, size_t count, loff_t *pos) | ||
240 | { | 232 | { |
241 | return -ESPIPE; | 233 | struct vhci_data *vhci = file->private_data; |
242 | } | ||
243 | 234 | ||
244 | static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | 235 | if (!access_ok(VERIFY_READ, buf, count)) |
245 | { | 236 | return -EFAULT; |
246 | return -EINVAL; | 237 | |
238 | return vhci_get_user(vhci, buf, count); | ||
247 | } | 239 | } |
248 | 240 | ||
249 | static int hci_vhci_chr_fasync(int fd, struct file *file, int on) | 241 | static unsigned int vhci_poll(struct file *file, poll_table *wait) |
250 | { | 242 | { |
251 | struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; | 243 | struct vhci_data *vhci = file->private_data; |
252 | int ret; | ||
253 | 244 | ||
254 | if ((ret = fasync_helper(fd, file, on, &hci_vhci->fasync)) < 0) | 245 | poll_wait(file, &vhci->read_wait, wait); |
255 | return ret; | ||
256 | |||
257 | if (on) | ||
258 | hci_vhci->flags |= VHCI_FASYNC; | ||
259 | else | ||
260 | hci_vhci->flags &= ~VHCI_FASYNC; | ||
261 | 246 | ||
262 | return 0; | 247 | if (!skb_queue_empty(&vhci->readq)) |
248 | return POLLIN | POLLRDNORM; | ||
249 | |||
250 | return POLLOUT | POLLWRNORM; | ||
251 | } | ||
252 | |||
253 | static int vhci_ioctl(struct inode *inode, struct file *file, | ||
254 | unsigned int cmd, unsigned long arg) | ||
255 | { | ||
256 | return -EINVAL; | ||
263 | } | 257 | } |
264 | 258 | ||
265 | static int hci_vhci_chr_open(struct inode *inode, struct file * file) | 259 | static int vhci_open(struct inode *inode, struct file *file) |
266 | { | 260 | { |
267 | struct hci_vhci_struct *hci_vhci = NULL; | 261 | struct vhci_data *vhci; |
268 | struct hci_dev *hdev; | 262 | struct hci_dev *hdev; |
269 | 263 | ||
270 | if (!(hci_vhci = kmalloc(sizeof(struct hci_vhci_struct), GFP_KERNEL))) | 264 | vhci = kmalloc(sizeof(struct vhci_data), GFP_KERNEL); |
265 | if (!vhci) | ||
271 | return -ENOMEM; | 266 | return -ENOMEM; |
272 | 267 | ||
273 | memset(hci_vhci, 0, sizeof(struct hci_vhci_struct)); | 268 | memset(vhci, 0, sizeof(struct vhci_data)); |
274 | 269 | ||
275 | skb_queue_head_init(&hci_vhci->readq); | 270 | skb_queue_head_init(&vhci->readq); |
276 | init_waitqueue_head(&hci_vhci->read_wait); | 271 | init_waitqueue_head(&vhci->read_wait); |
277 | 272 | ||
278 | /* Initialize and register HCI device */ | ||
279 | hdev = hci_alloc_dev(); | 273 | hdev = hci_alloc_dev(); |
280 | if (!hdev) { | 274 | if (!hdev) { |
281 | kfree(hci_vhci); | 275 | kfree(vhci); |
282 | return -ENOMEM; | 276 | return -ENOMEM; |
283 | } | 277 | } |
284 | 278 | ||
285 | hci_vhci->hdev = hdev; | 279 | vhci->hdev = hdev; |
286 | 280 | ||
287 | hdev->type = HCI_VHCI; | 281 | hdev->type = HCI_VHCI; |
288 | hdev->driver_data = hci_vhci; | 282 | hdev->driver_data = vhci; |
283 | SET_HCIDEV_DEV(hdev, vhci_miscdev.dev); | ||
289 | 284 | ||
290 | hdev->open = hci_vhci_open; | 285 | hdev->open = vhci_open_dev; |
291 | hdev->close = hci_vhci_close; | 286 | hdev->close = vhci_close_dev; |
292 | hdev->flush = hci_vhci_flush; | 287 | hdev->flush = vhci_flush; |
293 | hdev->send = hci_vhci_send_frame; | 288 | hdev->send = vhci_send_frame; |
294 | hdev->destruct = hci_vhci_destruct; | 289 | hdev->destruct = vhci_destruct; |
295 | 290 | ||
296 | hdev->owner = THIS_MODULE; | 291 | hdev->owner = THIS_MODULE; |
297 | 292 | ||
298 | if (hci_register_dev(hdev) < 0) { | 293 | if (hci_register_dev(hdev) < 0) { |
299 | kfree(hci_vhci); | 294 | BT_ERR("Can't register HCI device"); |
295 | kfree(vhci); | ||
300 | hci_free_dev(hdev); | 296 | hci_free_dev(hdev); |
301 | return -EBUSY; | 297 | return -EBUSY; |
302 | } | 298 | } |
303 | 299 | ||
304 | file->private_data = hci_vhci; | 300 | file->private_data = vhci; |
305 | return nonseekable_open(inode, file); | 301 | |
302 | return nonseekable_open(inode, file); | ||
306 | } | 303 | } |
307 | 304 | ||
308 | static int hci_vhci_chr_close(struct inode *inode, struct file *file) | 305 | static int vhci_release(struct inode *inode, struct file *file) |
309 | { | 306 | { |
310 | struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data; | 307 | struct vhci_data *vhci = file->private_data; |
311 | struct hci_dev *hdev = hci_vhci->hdev; | 308 | struct hci_dev *hdev = vhci->hdev; |
312 | 309 | ||
313 | if (hci_unregister_dev(hdev) < 0) { | 310 | if (hci_unregister_dev(hdev) < 0) { |
314 | BT_ERR("Can't unregister HCI device %s", hdev->name); | 311 | BT_ERR("Can't unregister HCI device %s", hdev->name); |
@@ -317,48 +314,71 @@ static int hci_vhci_chr_close(struct inode *inode, struct file *file) | |||
317 | hci_free_dev(hdev); | 314 | hci_free_dev(hdev); |
318 | 315 | ||
319 | file->private_data = NULL; | 316 | file->private_data = NULL; |
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int vhci_fasync(int fd, struct file *file, int on) | ||
322 | { | ||
323 | struct vhci_data *vhci = file->private_data; | ||
324 | int err; | ||
325 | |||
326 | err = fasync_helper(fd, file, on, &vhci->fasync); | ||
327 | if (err < 0) | ||
328 | return err; | ||
329 | |||
330 | if (on) | ||
331 | vhci->flags |= VHCI_FASYNC; | ||
332 | else | ||
333 | vhci->flags &= ~VHCI_FASYNC; | ||
334 | |||
320 | return 0; | 335 | return 0; |
321 | } | 336 | } |
322 | 337 | ||
323 | static struct file_operations hci_vhci_fops = { | 338 | static struct file_operations vhci_fops = { |
324 | .owner = THIS_MODULE, | 339 | .owner = THIS_MODULE, |
325 | .llseek = hci_vhci_chr_lseek, | 340 | .llseek = vhci_llseek, |
326 | .read = hci_vhci_chr_read, | 341 | .read = vhci_read, |
327 | .write = hci_vhci_chr_write, | 342 | .write = vhci_write, |
328 | .poll = hci_vhci_chr_poll, | 343 | .poll = vhci_poll, |
329 | .ioctl = hci_vhci_chr_ioctl, | 344 | .ioctl = vhci_ioctl, |
330 | .open = hci_vhci_chr_open, | 345 | .open = vhci_open, |
331 | .release = hci_vhci_chr_close, | 346 | .release = vhci_release, |
332 | .fasync = hci_vhci_chr_fasync | 347 | .fasync = vhci_fasync, |
333 | }; | 348 | }; |
334 | 349 | ||
335 | static struct miscdevice hci_vhci_miscdev= | 350 | static struct miscdevice vhci_miscdev= { |
336 | { | 351 | .name = "vhci", |
337 | VHCI_MINOR, | 352 | .fops = &vhci_fops, |
338 | "hci_vhci", | ||
339 | &hci_vhci_fops | ||
340 | }; | 353 | }; |
341 | 354 | ||
342 | static int __init hci_vhci_init(void) | 355 | static int __init vhci_init(void) |
343 | { | 356 | { |
344 | BT_INFO("VHCI driver ver %s", VERSION); | 357 | BT_INFO("Virtual HCI driver ver %s", VERSION); |
345 | 358 | ||
346 | if (misc_register(&hci_vhci_miscdev)) { | 359 | vhci_miscdev.minor = minor; |
347 | BT_ERR("Can't register misc device %d\n", VHCI_MINOR); | 360 | |
361 | if (misc_register(&vhci_miscdev) < 0) { | ||
362 | BT_ERR("Can't register misc device with minor %d", minor); | ||
348 | return -EIO; | 363 | return -EIO; |
349 | } | 364 | } |
350 | 365 | ||
351 | return 0; | 366 | return 0; |
352 | } | 367 | } |
353 | 368 | ||
354 | static void hci_vhci_cleanup(void) | 369 | static void __exit vhci_exit(void) |
355 | { | 370 | { |
356 | misc_deregister(&hci_vhci_miscdev); | 371 | if (misc_deregister(&vhci_miscdev) < 0) |
372 | BT_ERR("Can't unregister misc device with minor %d", minor); | ||
357 | } | 373 | } |
358 | 374 | ||
359 | module_init(hci_vhci_init); | 375 | module_init(vhci_init); |
360 | module_exit(hci_vhci_cleanup); | 376 | module_exit(vhci_exit); |
377 | |||
378 | module_param(minor, int, 0444); | ||
379 | MODULE_PARM_DESC(minor, "Miscellaneous minor device number"); | ||
361 | 380 | ||
362 | MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>"); | 381 | MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>"); |
363 | MODULE_DESCRIPTION("Bluetooth VHCI driver ver " VERSION); | 382 | MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION); |
364 | MODULE_LICENSE("GPL"); | 383 | MODULE_VERSION(VERSION); |
384 | MODULE_LICENSE("GPL"); | ||