PageRenderTime 28ms CodeModel.GetById 1ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/bluetooth/hci_vhci.c

https://bitbucket.org/evzijst/gittest
C | 364 lines | 248 code | 78 blank | 38 comment | 25 complexity | 0e9ca90616ea532dd02400588a9046b6 MD5 | raw file
  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/*
 26 * Bluetooth HCI virtual device driver.
 27 *
 28 * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ 
 29 */
 30#define VERSION "1.1"
 31
 32#include <linux/config.h>
 33#include <linux/module.h>
 34
 35#include <linux/errno.h>
 36#include <linux/kernel.h>
 37#include <linux/major.h>
 38#include <linux/sched.h>
 39#include <linux/slab.h>
 40#include <linux/poll.h>
 41#include <linux/fcntl.h>
 42#include <linux/init.h>
 43#include <linux/random.h>
 44
 45#include <linux/skbuff.h>
 46#include <linux/miscdevice.h>
 47
 48#include <asm/system.h>
 49#include <asm/uaccess.h>
 50
 51#include <net/bluetooth/bluetooth.h>
 52#include <net/bluetooth/hci_core.h>
 53#include "hci_vhci.h"
 54
 55/* HCI device part */
 56
 57static int hci_vhci_open(struct hci_dev *hdev)
 58{
 59	set_bit(HCI_RUNNING, &hdev->flags);
 60	return 0;
 61}
 62
 63static 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;
 68}
 69
 70static int hci_vhci_close(struct hci_dev *hdev)
 71{
 72	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 73		return 0;
 74
 75	hci_vhci_flush(hdev);
 76	return 0;
 77}
 78
 79static void hci_vhci_destruct(struct hci_dev *hdev)
 80{
 81	struct hci_vhci_struct *vhci;
 82
 83	if (!hdev) return;
 84
 85	vhci = (struct hci_vhci_struct *) hdev->driver_data;
 86	kfree(vhci);
 87}
 88
 89static int hci_vhci_send_frame(struct sk_buff *skb)
 90{
 91	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
 92	struct hci_vhci_struct *hci_vhci;
 93
 94	if (!hdev) {
 95		BT_ERR("Frame for uknown device (hdev=NULL)");
 96		return -ENODEV;
 97	}
 98
 99	if (!test_bit(HCI_RUNNING, &hdev->flags))
100		return -EBUSY;
101
102	hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
103
104	memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
105	skb_queue_tail(&hci_vhci->readq, skb);
106
107	if (hci_vhci->flags & VHCI_FASYNC)
108		kill_fasync(&hci_vhci->fasync, SIGIO, POLL_IN);
109	wake_up_interruptible(&hci_vhci->read_wait);
110
111	return 0;
112}
113
114/* Character device part */
115
116/* Poll */
117static 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_len(&hci_vhci->readq))
124		return POLLIN | POLLRDNORM;
125
126	return POLLOUT | POLLWRNORM;
127}
128
129/* Get packet from user space buffer(already verified) */
130static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const char __user *buf, size_t count)
131{
132	struct sk_buff *skb;
133
134	if (count > HCI_MAX_FRAME_SIZE)
135		return -EINVAL;
136
137	if (!(skb = bt_skb_alloc(count, GFP_KERNEL)))
138		return -ENOMEM;
139	
140	if (copy_from_user(skb_put(skb, count), buf, count)) {
141		kfree_skb(skb);
142		return -EFAULT;
143	}
144
145	skb->dev = (void *) hci_vhci->hdev;
146	skb->pkt_type = *((__u8 *) skb->data);
147	skb_pull(skb, 1);
148
149	hci_recv_frame(skb);
150
151	return count;
152} 
153
154/* Write */
155static 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}
165
166/* Put packet to user space buffer(already verified) */
167static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci,
168				       struct sk_buff *skb, char __user *buf,
169				       int count)
170{
171	int len = count, total = 0;
172	char __user *ptr = buf;
173
174	len = min_t(unsigned int, skb->len, len);
175	if (copy_to_user(ptr, skb->data, len))
176		return -EFAULT;
177	total += len;
178
179	hci_vhci->hdev->stat.byte_tx += len;
180	switch (skb->pkt_type) {
181		case HCI_COMMAND_PKT:
182			hci_vhci->hdev->stat.cmd_tx++;
183			break;
184
185		case HCI_ACLDATA_PKT:
186			hci_vhci->hdev->stat.acl_tx++;
187			break;
188
189		case HCI_SCODATA_PKT:
190			hci_vhci->hdev->stat.cmd_tx++;
191			break;
192	};
193
194	return total;
195}
196
197/* Read */
198static ssize_t hci_vhci_chr_read(struct file * file, char __user * buf, size_t count, loff_t *pos)
199{
200	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
201	DECLARE_WAITQUEUE(wait, current);
202	struct sk_buff *skb;
203	ssize_t ret = 0;
204
205	add_wait_queue(&hci_vhci->read_wait, &wait);
206	while (count) {
207		set_current_state(TASK_INTERRUPTIBLE);
208
209		/* Read frames from device queue */
210		if (!(skb = skb_dequeue(&hci_vhci->readq))) {
211			if (file->f_flags & O_NONBLOCK) {
212				ret = -EAGAIN;
213				break;
214			}
215			if (signal_pending(current)) {
216				ret = -ERESTARTSYS;
217				break;
218			}
219
220			/* Nothing to read, let's sleep */
221			schedule();
222			continue;
223		}
224
225		if (access_ok(VERIFY_WRITE, buf, count))
226			ret = hci_vhci_put_user(hci_vhci, skb, buf, count);
227		else
228			ret = -EFAULT;
229
230		kfree_skb(skb);
231		break;
232	}
233	set_current_state(TASK_RUNNING);
234	remove_wait_queue(&hci_vhci->read_wait, &wait);
235
236	return ret;
237}
238
239static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin)
240{
241	return -ESPIPE;
242}
243
244static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
245{
246	return -EINVAL;
247}
248
249static int hci_vhci_chr_fasync(int fd, struct file *file, int on)
250{
251	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
252	int ret;
253
254	if ((ret = fasync_helper(fd, file, on, &hci_vhci->fasync)) < 0)
255		return ret; 
256 
257	if (on)
258		hci_vhci->flags |= VHCI_FASYNC;
259	else 
260		hci_vhci->flags &= ~VHCI_FASYNC;
261
262	return 0;
263}
264
265static int hci_vhci_chr_open(struct inode *inode, struct file * file)
266{
267	struct hci_vhci_struct *hci_vhci = NULL; 
268	struct hci_dev *hdev;
269
270	if (!(hci_vhci = kmalloc(sizeof(struct hci_vhci_struct), GFP_KERNEL)))
271		return -ENOMEM;
272
273	memset(hci_vhci, 0, sizeof(struct hci_vhci_struct));
274
275	skb_queue_head_init(&hci_vhci->readq);
276	init_waitqueue_head(&hci_vhci->read_wait);
277
278	/* Initialize and register HCI device */
279	hdev = hci_alloc_dev();
280	if (!hdev) {
281		kfree(hci_vhci);
282		return -ENOMEM;
283	}
284
285	hci_vhci->hdev = hdev;
286
287	hdev->type = HCI_VHCI;
288	hdev->driver_data = hci_vhci;
289
290	hdev->open  = hci_vhci_open;
291	hdev->close = hci_vhci_close;
292	hdev->flush = hci_vhci_flush;
293	hdev->send  = hci_vhci_send_frame;
294	hdev->destruct = hci_vhci_destruct;
295
296	hdev->owner = THIS_MODULE;
297	
298	if (hci_register_dev(hdev) < 0) {
299		kfree(hci_vhci);
300		hci_free_dev(hdev);
301		return -EBUSY;
302	}
303
304	file->private_data = hci_vhci;
305	return nonseekable_open(inode, file);   
306}
307
308static int hci_vhci_chr_close(struct inode *inode, struct file *file)
309{
310	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
311	struct hci_dev *hdev = hci_vhci->hdev;
312
313	if (hci_unregister_dev(hdev) < 0) {
314		BT_ERR("Can't unregister HCI device %s", hdev->name);
315	}
316
317	hci_free_dev(hdev);
318
319	file->private_data = NULL;
320	return 0;
321}
322
323static struct file_operations hci_vhci_fops = {
324	.owner	= THIS_MODULE,	
325	.llseek	= hci_vhci_chr_lseek,
326	.read	= hci_vhci_chr_read,
327	.write	= hci_vhci_chr_write,
328	.poll	= hci_vhci_chr_poll,
329	.ioctl	= hci_vhci_chr_ioctl,
330	.open	= hci_vhci_chr_open,
331	.release	= hci_vhci_chr_close,
332	.fasync	= hci_vhci_chr_fasync		
333};
334
335static struct miscdevice hci_vhci_miscdev=
336{
337        VHCI_MINOR,
338        "hci_vhci",
339        &hci_vhci_fops
340};
341
342static int __init hci_vhci_init(void)
343{
344	BT_INFO("VHCI driver ver %s", VERSION);
345
346	if (misc_register(&hci_vhci_miscdev)) {
347		BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
348		return -EIO;
349	}
350
351	return 0;
352}
353
354static void hci_vhci_cleanup(void)
355{
356	misc_deregister(&hci_vhci_miscdev);
357}
358
359module_init(hci_vhci_init);
360module_exit(hci_vhci_cleanup);
361
362MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
363MODULE_DESCRIPTION("Bluetooth VHCI driver ver " VERSION);
364MODULE_LICENSE("GPL");