~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/bluetooth/hci_vhci.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
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-2006  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
 *
 
24
 */
 
25
 
 
26
#include <linux/module.h>
 
27
 
 
28
#include <linux/kernel.h>
 
29
#include <linux/init.h>
 
30
#include <linux/slab.h>
 
31
#include <linux/types.h>
 
32
#include <linux/errno.h>
 
33
#include <linux/sched.h>
 
34
#include <linux/poll.h>
 
35
 
 
36
#include <linux/skbuff.h>
 
37
#include <linux/miscdevice.h>
 
38
 
 
39
#include <net/bluetooth/bluetooth.h>
 
40
#include <net/bluetooth/hci_core.h>
 
41
 
 
42
#define VERSION "1.3"
 
43
 
 
44
struct vhci_data {
 
45
        struct hci_dev *hdev;
 
46
 
 
47
        unsigned long flags;
 
48
 
 
49
        wait_queue_head_t read_wait;
 
50
        struct sk_buff_head readq;
 
51
};
 
52
 
 
53
static int vhci_open_dev(struct hci_dev *hdev)
 
54
{
 
55
        set_bit(HCI_RUNNING, &hdev->flags);
 
56
 
 
57
        return 0;
 
58
}
 
59
 
 
60
static int vhci_close_dev(struct hci_dev *hdev)
 
61
{
 
62
        struct vhci_data *data = hdev->driver_data;
 
63
 
 
64
        if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 
65
                return 0;
 
66
 
 
67
        skb_queue_purge(&data->readq);
 
68
 
 
69
        return 0;
 
70
}
 
71
 
 
72
static int vhci_flush(struct hci_dev *hdev)
 
73
{
 
74
        struct vhci_data *data = hdev->driver_data;
 
75
 
 
76
        skb_queue_purge(&data->readq);
 
77
 
 
78
        return 0;
 
79
}
 
80
 
 
81
static int vhci_send_frame(struct sk_buff *skb)
 
82
{
 
83
        struct hci_dev* hdev = (struct hci_dev *) skb->dev;
 
84
        struct vhci_data *data;
 
85
 
 
86
        if (!hdev) {
 
87
                BT_ERR("Frame for unknown HCI device (hdev=NULL)");
 
88
                return -ENODEV;
 
89
        }
 
90
 
 
91
        if (!test_bit(HCI_RUNNING, &hdev->flags))
 
92
                return -EBUSY;
 
93
 
 
94
        data = hdev->driver_data;
 
95
 
 
96
        memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
 
97
        skb_queue_tail(&data->readq, skb);
 
98
 
 
99
        wake_up_interruptible(&data->read_wait);
 
100
 
 
101
        return 0;
 
102
}
 
103
 
 
104
static void vhci_destruct(struct hci_dev *hdev)
 
105
{
 
106
        kfree(hdev->driver_data);
 
107
}
 
108
 
 
109
static inline ssize_t vhci_get_user(struct vhci_data *data,
 
110
                                        const char __user *buf, size_t count)
 
111
{
 
112
        struct sk_buff *skb;
 
113
 
 
114
        if (count > HCI_MAX_FRAME_SIZE)
 
115
                return -EINVAL;
 
116
 
 
117
        skb = bt_skb_alloc(count, GFP_KERNEL);
 
118
        if (!skb)
 
119
                return -ENOMEM;
 
120
 
 
121
        if (copy_from_user(skb_put(skb, count), buf, count)) {
 
122
                kfree_skb(skb);
 
123
                return -EFAULT;
 
124
        }
 
125
 
 
126
        skb->dev = (void *) data->hdev;
 
127
        bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
 
128
        skb_pull(skb, 1);
 
129
 
 
130
        hci_recv_frame(skb);
 
131
 
 
132
        return count;
 
133
}
 
134
 
 
135
static inline ssize_t vhci_put_user(struct vhci_data *data,
 
136
                        struct sk_buff *skb, char __user *buf, int count)
 
137
{
 
138
        char __user *ptr = buf;
 
139
        int len, total = 0;
 
140
 
 
141
        len = min_t(unsigned int, skb->len, count);
 
142
 
 
143
        if (copy_to_user(ptr, skb->data, len))
 
144
                return -EFAULT;
 
145
 
 
146
        total += len;
 
147
 
 
148
        data->hdev->stat.byte_tx += len;
 
149
 
 
150
        switch (bt_cb(skb)->pkt_type) {
 
151
        case HCI_COMMAND_PKT:
 
152
                data->hdev->stat.cmd_tx++;
 
153
                break;
 
154
 
 
155
        case HCI_ACLDATA_PKT:
 
156
                data->hdev->stat.acl_tx++;
 
157
                break;
 
158
 
 
159
        case HCI_SCODATA_PKT:
 
160
                data->hdev->stat.sco_tx++;
 
161
                break;
 
162
        };
 
163
 
 
164
        return total;
 
165
}
 
166
 
 
167
static ssize_t vhci_read(struct file *file,
 
168
                                char __user *buf, size_t count, loff_t *pos)
 
169
{
 
170
        struct vhci_data *data = file->private_data;
 
171
        struct sk_buff *skb;
 
172
        ssize_t ret = 0;
 
173
 
 
174
        while (count) {
 
175
                skb = skb_dequeue(&data->readq);
 
176
                if (skb) {
 
177
                        ret = vhci_put_user(data, skb, buf, count);
 
178
                        if (ret < 0)
 
179
                                skb_queue_head(&data->readq, skb);
 
180
                        else
 
181
                                kfree_skb(skb);
 
182
                        break;
 
183
                }
 
184
 
 
185
                if (file->f_flags & O_NONBLOCK) {
 
186
                        ret = -EAGAIN;
 
187
                        break;
 
188
                }
 
189
 
 
190
                ret = wait_event_interruptible(data->read_wait,
 
191
                                        !skb_queue_empty(&data->readq));
 
192
                if (ret < 0)
 
193
                        break;
 
194
        }
 
195
 
 
196
        return ret;
 
197
}
 
198
 
 
199
static ssize_t vhci_write(struct file *file,
 
200
                        const char __user *buf, size_t count, loff_t *pos)
 
201
{
 
202
        struct vhci_data *data = file->private_data;
 
203
 
 
204
        return vhci_get_user(data, buf, count);
 
205
}
 
206
 
 
207
static unsigned int vhci_poll(struct file *file, poll_table *wait)
 
208
{
 
209
        struct vhci_data *data = file->private_data;
 
210
 
 
211
        poll_wait(file, &data->read_wait, wait);
 
212
 
 
213
        if (!skb_queue_empty(&data->readq))
 
214
                return POLLIN | POLLRDNORM;
 
215
 
 
216
        return POLLOUT | POLLWRNORM;
 
217
}
 
218
 
 
219
static int vhci_open(struct inode *inode, struct file *file)
 
220
{
 
221
        struct vhci_data *data;
 
222
        struct hci_dev *hdev;
 
223
 
 
224
        data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
 
225
        if (!data)
 
226
                return -ENOMEM;
 
227
 
 
228
        skb_queue_head_init(&data->readq);
 
229
        init_waitqueue_head(&data->read_wait);
 
230
 
 
231
        hdev = hci_alloc_dev();
 
232
        if (!hdev) {
 
233
                kfree(data);
 
234
                return -ENOMEM;
 
235
        }
 
236
 
 
237
        data->hdev = hdev;
 
238
 
 
239
        hdev->bus = HCI_VIRTUAL;
 
240
        hdev->driver_data = data;
 
241
 
 
242
        hdev->open     = vhci_open_dev;
 
243
        hdev->close    = vhci_close_dev;
 
244
        hdev->flush    = vhci_flush;
 
245
        hdev->send     = vhci_send_frame;
 
246
        hdev->destruct = vhci_destruct;
 
247
 
 
248
        hdev->owner = THIS_MODULE;
 
249
 
 
250
        if (hci_register_dev(hdev) < 0) {
 
251
                BT_ERR("Can't register HCI device");
 
252
                kfree(data);
 
253
                hci_free_dev(hdev);
 
254
                return -EBUSY;
 
255
        }
 
256
 
 
257
        file->private_data = data;
 
258
 
 
259
        return nonseekable_open(inode, file);
 
260
}
 
261
 
 
262
static int vhci_release(struct inode *inode, struct file *file)
 
263
{
 
264
        struct vhci_data *data = file->private_data;
 
265
        struct hci_dev *hdev = data->hdev;
 
266
 
 
267
        if (hci_unregister_dev(hdev) < 0) {
 
268
                BT_ERR("Can't unregister HCI device %s", hdev->name);
 
269
        }
 
270
 
 
271
        hci_free_dev(hdev);
 
272
 
 
273
        file->private_data = NULL;
 
274
 
 
275
        return 0;
 
276
}
 
277
 
 
278
static const struct file_operations vhci_fops = {
 
279
        .owner          = THIS_MODULE,
 
280
        .read           = vhci_read,
 
281
        .write          = vhci_write,
 
282
        .poll           = vhci_poll,
 
283
        .open           = vhci_open,
 
284
        .release        = vhci_release,
 
285
        .llseek         = no_llseek,
 
286
};
 
287
 
 
288
static struct miscdevice vhci_miscdev= {
 
289
        .name   = "vhci",
 
290
        .fops   = &vhci_fops,
 
291
        .minor  = MISC_DYNAMIC_MINOR,
 
292
};
 
293
 
 
294
static int __init vhci_init(void)
 
295
{
 
296
        BT_INFO("Virtual HCI driver ver %s", VERSION);
 
297
 
 
298
        return misc_register(&vhci_miscdev);
 
299
}
 
300
 
 
301
static void __exit vhci_exit(void)
 
302
{
 
303
        misc_deregister(&vhci_miscdev);
 
304
}
 
305
 
 
306
module_init(vhci_init);
 
307
module_exit(vhci_exit);
 
308
 
 
309
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 
310
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
 
311
MODULE_VERSION(VERSION);
 
312
MODULE_LICENSE("GPL");