linux-old/drivers/bluetooth/hci_vhci.c
<<
>>
Prefs
   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 * BlueZ 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        MOD_DEC_USE_COUNT;
  89}
  90
  91static int hci_vhci_send_frame(struct sk_buff *skb)
  92{
  93        struct hci_dev* hdev = (struct hci_dev *) skb->dev;
  94        struct hci_vhci_struct *hci_vhci;
  95
  96        if (!hdev) {
  97                BT_ERR("Frame for uknown device (hdev=NULL)");
  98                return -ENODEV;
  99        }
 100
 101        if (!test_bit(HCI_RUNNING, &hdev->flags))
 102                return -EBUSY;
 103
 104        hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
 105
 106        memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
 107        skb_queue_tail(&hci_vhci->readq, skb);
 108
 109        if (hci_vhci->flags & VHCI_FASYNC)
 110                kill_fasync(&hci_vhci->fasync, SIGIO, POLL_IN);
 111        wake_up_interruptible(&hci_vhci->read_wait);
 112
 113        return 0;
 114}
 115
 116/* Character device part */
 117
 118/* Poll */
 119static unsigned int hci_vhci_chr_poll(struct file *file, poll_table * wait)
 120{  
 121        struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 122
 123        poll_wait(file, &hci_vhci->read_wait, wait);
 124 
 125        if (skb_queue_len(&hci_vhci->readq))
 126                return POLLIN | POLLRDNORM;
 127
 128        return POLLOUT | POLLWRNORM;
 129}
 130
 131/* Get packet from user space buffer(already verified) */
 132static inline ssize_t hci_vhci_get_user(struct hci_vhci_struct *hci_vhci, const char *buf, size_t count)
 133{
 134        struct sk_buff *skb;
 135
 136        if (count > HCI_MAX_FRAME_SIZE)
 137                return -EINVAL;
 138
 139        if (!(skb = bluez_skb_alloc(count, GFP_KERNEL)))
 140                return -ENOMEM;
 141        
 142        copy_from_user(skb_put(skb, count), buf, count); 
 143
 144        skb->dev = (void *) &hci_vhci->hdev;
 145        skb->pkt_type = *((__u8 *) skb->data);
 146        skb_pull(skb, 1);
 147
 148        hci_recv_frame(skb);
 149
 150        return count;
 151} 
 152
 153/* Write */
 154static ssize_t hci_vhci_chr_write(struct file * file, const char * buf, 
 155                             size_t count, loff_t *pos)
 156{
 157        struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 158
 159        if (verify_area(VERIFY_READ, buf, count))
 160                return -EFAULT;
 161
 162        return hci_vhci_get_user(hci_vhci, buf, count);
 163}
 164
 165/* Put packet to user space buffer(already verified) */
 166static inline ssize_t hci_vhci_put_user(struct hci_vhci_struct *hci_vhci,
 167                                       struct sk_buff *skb, char *buf, int count)
 168{
 169        int len = count, total = 0;
 170        char *ptr = buf;
 171
 172        len = MIN(skb->len, len); 
 173        copy_to_user(ptr, skb->data, len); 
 174        total += len;
 175
 176        hci_vhci->hdev.stat.byte_tx += len;
 177        switch (skb->pkt_type) {
 178                case HCI_COMMAND_PKT:
 179                        hci_vhci->hdev.stat.cmd_tx++;
 180                        break;
 181
 182                case HCI_ACLDATA_PKT:
 183                        hci_vhci->hdev.stat.acl_tx++;
 184                        break;
 185
 186                case HCI_SCODATA_PKT:
 187                        hci_vhci->hdev.stat.cmd_tx++;
 188                        break;
 189        };
 190
 191        return total;
 192}
 193
 194/* Read */
 195static ssize_t hci_vhci_chr_read(struct file * file, char * buf, size_t count, loff_t *pos)
 196{
 197        struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 198        DECLARE_WAITQUEUE(wait, current);
 199        struct sk_buff *skb;
 200        ssize_t ret = 0;
 201
 202        add_wait_queue(&hci_vhci->read_wait, &wait);
 203        while (count) {
 204                set_current_state(TASK_INTERRUPTIBLE);
 205
 206                /* Read frames from device queue */
 207                if (!(skb = skb_dequeue(&hci_vhci->readq))) {
 208                        if (file->f_flags & O_NONBLOCK) {
 209                                ret = -EAGAIN;
 210                                break;
 211                        }
 212                        if (signal_pending(current)) {
 213                                ret = -ERESTARTSYS;
 214                                break;
 215                        }
 216
 217                        /* Nothing to read, let's sleep */
 218                        schedule();
 219                        continue;
 220                }
 221
 222                if (!verify_area(VERIFY_WRITE, buf, count))
 223                        ret = hci_vhci_put_user(hci_vhci, skb, buf, count);
 224                else
 225                        ret = -EFAULT;
 226
 227                kfree_skb(skb);
 228                break;
 229        }
 230        set_current_state(TASK_RUNNING);
 231        remove_wait_queue(&hci_vhci->read_wait, &wait);
 232
 233        return ret;
 234}
 235
 236static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin)
 237{
 238        return -ESPIPE;
 239}
 240
 241static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 242{
 243        return -EINVAL;
 244}
 245
 246static int hci_vhci_chr_fasync(int fd, struct file *file, int on)
 247{
 248        struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 249        int ret;
 250
 251        if ((ret = fasync_helper(fd, file, on, &hci_vhci->fasync)) < 0)
 252                return ret; 
 253 
 254        if (on)
 255                hci_vhci->flags |= VHCI_FASYNC;
 256        else 
 257                hci_vhci->flags &= ~VHCI_FASYNC;
 258
 259        return 0;
 260}
 261
 262static int hci_vhci_chr_open(struct inode *inode, struct file * file)
 263{
 264        struct hci_vhci_struct *hci_vhci = NULL; 
 265        struct hci_dev *hdev;
 266
 267        if (!(hci_vhci = kmalloc(sizeof(struct hci_vhci_struct), GFP_KERNEL)))
 268                return -ENOMEM;
 269
 270        memset(hci_vhci, 0, sizeof(struct hci_vhci_struct));
 271
 272        skb_queue_head_init(&hci_vhci->readq);
 273        init_waitqueue_head(&hci_vhci->read_wait);
 274
 275        /* Initialize and register HCI device */
 276        hdev = &hci_vhci->hdev;
 277
 278        hdev->type = HCI_VHCI;
 279        hdev->driver_data = hci_vhci;
 280
 281        hdev->open  = hci_vhci_open;
 282        hdev->close = hci_vhci_close;
 283        hdev->flush = hci_vhci_flush;
 284        hdev->send  = hci_vhci_send_frame;
 285        hdev->destruct = hci_vhci_destruct;
 286
 287        if (hci_register_dev(hdev) < 0) {
 288                kfree(hci_vhci);
 289                return -EBUSY;
 290        }
 291        MOD_INC_USE_COUNT;
 292
 293        file->private_data = hci_vhci;
 294        return 0;   
 295}
 296
 297static int hci_vhci_chr_close(struct inode *inode, struct file *file)
 298{
 299        struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 300
 301        if (hci_unregister_dev(&hci_vhci->hdev) < 0) {
 302                BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
 303        }
 304
 305        file->private_data = NULL;
 306        return 0;
 307}
 308
 309static struct file_operations hci_vhci_fops = {
 310        owner:  THIS_MODULE,    
 311        llseek: hci_vhci_chr_lseek,
 312        read:   hci_vhci_chr_read,
 313        write:  hci_vhci_chr_write,
 314        poll:   hci_vhci_chr_poll,
 315        ioctl:  hci_vhci_chr_ioctl,
 316        open:   hci_vhci_chr_open,
 317        release:hci_vhci_chr_close,
 318        fasync: hci_vhci_chr_fasync             
 319};
 320
 321static struct miscdevice hci_vhci_miscdev=
 322{
 323        VHCI_MINOR,
 324        "hci_vhci",
 325        &hci_vhci_fops
 326};
 327
 328int __init hci_vhci_init(void)
 329{
 330        BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
 331                VERSION);
 332        BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 333
 334        if (misc_register(&hci_vhci_miscdev)) {
 335                BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
 336                return -EIO;
 337        }
 338
 339        return 0;
 340}
 341
 342void hci_vhci_cleanup(void)
 343{
 344        misc_deregister(&hci_vhci_miscdev);
 345}
 346
 347module_init(hci_vhci_init);
 348module_exit(hci_vhci_cleanup);
 349
 350MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
 351MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION);
 352MODULE_LICENSE("GPL"); 
 353
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.