2005-04-17 02:20:36 +04:00
/*
*
2005-08-10 07:27:37 +04:00
* Bluetooth virtual HCI driver
*
2006-07-06 17:45:23 +04:00
* Copyright ( C ) 2000 - 2001 Qualcomm Incorporated
* Copyright ( C ) 2002 - 2003 Maxim Krasnyansky < maxk @ qualcomm . com >
* Copyright ( C ) 2004 - 2006 Marcel Holtmann < marcel @ holtmann . org >
2005-08-10 07:27:37 +04:00
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
2005-08-10 07:27:37 +04:00
# include <linux/init.h>
2005-04-17 02:20:36 +04:00
# include <linux/slab.h>
2008-05-20 21:15:40 +04:00
# include <linux/smp_lock.h>
2005-08-10 07:27:37 +04:00
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/poll.h>
# include <linux/skbuff.h>
# include <linux/miscdevice.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
2005-08-10 07:27:37 +04:00
# define VERSION "1.2"
static int minor = MISC_DYNAMIC_MINOR ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:27:37 +04:00
struct vhci_data {
struct hci_dev * hdev ;
unsigned long flags ;
wait_queue_head_t read_wait ;
struct sk_buff_head readq ;
struct fasync_struct * fasync ;
} ;
# define VHCI_FASYNC 0x0010
static struct miscdevice vhci_miscdev ;
static int vhci_open_dev ( struct hci_dev * hdev )
2005-04-17 02:20:36 +04:00
{
set_bit ( HCI_RUNNING , & hdev - > flags ) ;
return 0 ;
}
2005-08-10 07:27:37 +04:00
static int vhci_close_dev ( struct hci_dev * hdev )
2005-04-17 02:20:36 +04:00
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data = hdev - > driver_data ;
2005-08-10 07:27:37 +04:00
2005-04-17 02:20:36 +04:00
if ( ! test_and_clear_bit ( HCI_RUNNING , & hdev - > flags ) )
return 0 ;
2006-07-06 17:45:23 +04:00
skb_queue_purge ( & data - > readq ) ;
2005-08-10 07:27:37 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-08-10 07:27:37 +04:00
static int vhci_flush ( struct hci_dev * hdev )
2005-04-17 02:20:36 +04:00
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data = hdev - > driver_data ;
2005-04-17 02:20:36 +04:00
2006-07-06 17:45:23 +04:00
skb_queue_purge ( & data - > readq ) ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:27:37 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-08-10 07:27:37 +04:00
static int vhci_send_frame ( struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
struct hci_dev * hdev = ( struct hci_dev * ) skb - > dev ;
2006-07-06 17:45:23 +04:00
struct vhci_data * data ;
2005-04-17 02:20:36 +04:00
if ( ! hdev ) {
2005-08-10 07:27:37 +04:00
BT_ERR ( " Frame for unknown HCI device (hdev=NULL) " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
if ( ! test_bit ( HCI_RUNNING , & hdev - > flags ) )
return - EBUSY ;
2006-07-06 17:45:23 +04:00
data = hdev - > driver_data ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:30:28 +04:00
memcpy ( skb_push ( skb , 1 ) , & bt_cb ( skb ) - > pkt_type , 1 ) ;
2006-07-06 17:45:23 +04:00
skb_queue_tail ( & data - > readq , skb ) ;
2005-08-10 07:27:37 +04:00
2006-07-06 17:45:23 +04:00
if ( data - > flags & VHCI_FASYNC )
kill_fasync ( & data - > fasync , SIGIO , POLL_IN ) ;
2005-04-17 02:20:36 +04:00
2006-07-06 17:45:23 +04:00
wake_up_interruptible ( & data - > read_wait ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-08-10 07:27:37 +04:00
static void vhci_destruct ( struct hci_dev * hdev )
{
kfree ( hdev - > driver_data ) ;
2005-04-17 02:20:36 +04:00
}
2006-07-06 17:45:23 +04:00
static inline ssize_t vhci_get_user ( struct vhci_data * data ,
2005-08-10 07:27:37 +04:00
const char __user * buf , size_t count )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
if ( count > HCI_MAX_FRAME_SIZE )
return - EINVAL ;
2005-08-10 07:27:37 +04:00
skb = bt_skb_alloc ( count , GFP_KERNEL ) ;
if ( ! skb )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2005-08-10 07:27:37 +04:00
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( skb_put ( skb , count ) , buf , count ) ) {
kfree_skb ( skb ) ;
return - EFAULT ;
}
2006-07-06 17:45:23 +04:00
skb - > dev = ( void * ) data - > hdev ;
2005-08-10 07:30:28 +04:00
bt_cb ( skb ) - > pkt_type = * ( ( __u8 * ) skb - > data ) ;
2005-04-17 02:20:36 +04:00
skb_pull ( skb , 1 ) ;
hci_recv_frame ( skb ) ;
return count ;
}
2006-07-06 17:45:23 +04:00
static inline ssize_t vhci_put_user ( struct vhci_data * data ,
2005-08-10 07:27:37 +04:00
struct sk_buff * skb , char __user * buf , int count )
2005-04-17 02:20:36 +04:00
{
char __user * ptr = buf ;
2005-08-10 07:27:37 +04:00
int len , total = 0 ;
len = min_t ( unsigned int , skb - > len , count ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( ptr , skb - > data , len ) )
return - EFAULT ;
2005-08-10 07:27:37 +04:00
2005-04-17 02:20:36 +04:00
total + = len ;
2006-07-06 17:45:23 +04:00
data - > hdev - > stat . byte_tx + = len ;
2005-08-10 07:27:37 +04:00
2005-08-10 07:30:28 +04:00
switch ( bt_cb ( skb ) - > pkt_type ) {
case HCI_COMMAND_PKT :
2006-07-06 17:45:23 +04:00
data - > hdev - > stat . cmd_tx + + ;
2005-08-10 07:30:28 +04:00
break ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:30:28 +04:00
case HCI_ACLDATA_PKT :
2006-07-06 17:45:23 +04:00
data - > hdev - > stat . acl_tx + + ;
2005-08-10 07:30:28 +04:00
break ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:30:28 +04:00
case HCI_SCODATA_PKT :
2006-07-06 17:45:23 +04:00
data - > hdev - > stat . cmd_tx + + ;
2005-08-10 07:30:28 +04:00
break ;
2005-04-17 02:20:36 +04:00
} ;
return total ;
}
2006-07-06 17:45:23 +04:00
static ssize_t vhci_read ( struct file * file ,
char __user * buf , size_t count , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
DECLARE_WAITQUEUE ( wait , current ) ;
2006-07-06 17:45:23 +04:00
struct vhci_data * data = file - > private_data ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
ssize_t ret = 0 ;
2006-07-06 17:45:23 +04:00
add_wait_queue ( & data - > read_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
while ( count ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
2006-07-06 17:45:23 +04:00
skb = skb_dequeue ( & data - > readq ) ;
2005-08-10 07:27:37 +04:00
if ( ! skb ) {
2005-04-17 02:20:36 +04:00
if ( file - > f_flags & O_NONBLOCK ) {
ret = - EAGAIN ;
break ;
}
2005-08-10 07:27:37 +04:00
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) ) {
ret = - ERESTARTSYS ;
break ;
}
schedule ( ) ;
continue ;
}
if ( access_ok ( VERIFY_WRITE , buf , count ) )
2006-07-06 17:45:23 +04:00
ret = vhci_put_user ( data , skb , buf , count ) ;
2005-04-17 02:20:36 +04:00
else
ret = - EFAULT ;
kfree_skb ( skb ) ;
break ;
}
set_current_state ( TASK_RUNNING ) ;
2006-07-06 17:45:23 +04:00
remove_wait_queue ( & data - > read_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2005-08-10 07:27:37 +04:00
static ssize_t vhci_write ( struct file * file ,
const char __user * buf , size_t count , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data = file - > private_data ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:27:37 +04:00
if ( ! access_ok ( VERIFY_READ , buf , count ) )
return - EFAULT ;
2006-07-06 17:45:23 +04:00
return vhci_get_user ( data , buf , count ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-10 07:27:37 +04:00
static unsigned int vhci_poll ( struct file * file , poll_table * wait )
2005-04-17 02:20:36 +04:00
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data = file - > private_data ;
2005-04-17 02:20:36 +04:00
2006-07-06 17:45:23 +04:00
poll_wait ( file , & data - > read_wait , wait ) ;
2005-04-17 02:20:36 +04:00
2006-07-06 17:45:23 +04:00
if ( ! skb_queue_empty ( & data - > readq ) )
2005-08-10 07:27:37 +04:00
return POLLIN | POLLRDNORM ;
return POLLOUT | POLLWRNORM ;
}
static int vhci_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2005-08-10 07:27:37 +04:00
static int vhci_open ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data ;
2005-04-17 02:20:36 +04:00
struct hci_dev * hdev ;
2006-07-06 17:45:23 +04:00
data = kzalloc ( sizeof ( struct vhci_data ) , GFP_KERNEL ) ;
if ( ! data )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2006-07-06 17:45:23 +04:00
skb_queue_head_init ( & data - > readq ) ;
init_waitqueue_head ( & data - > read_wait ) ;
2005-04-17 02:20:36 +04:00
2008-05-20 21:15:40 +04:00
lock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
hdev = hci_alloc_dev ( ) ;
if ( ! hdev ) {
2006-07-06 17:45:23 +04:00
kfree ( data ) ;
2008-05-20 21:15:40 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
2006-07-06 17:45:23 +04:00
data - > hdev = hdev ;
2005-04-17 02:20:36 +04:00
2006-07-08 15:57:15 +04:00
hdev - > type = HCI_VIRTUAL ;
2006-07-06 17:45:23 +04:00
hdev - > driver_data = data ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:27:37 +04:00
hdev - > open = vhci_open_dev ;
hdev - > close = vhci_close_dev ;
hdev - > flush = vhci_flush ;
hdev - > send = vhci_send_frame ;
hdev - > destruct = vhci_destruct ;
2005-04-17 02:20:36 +04:00
hdev - > owner = THIS_MODULE ;
2005-08-10 07:27:37 +04:00
2005-04-17 02:20:36 +04:00
if ( hci_register_dev ( hdev ) < 0 ) {
2005-08-10 07:27:37 +04:00
BT_ERR ( " Can't register HCI device " ) ;
2006-07-06 17:45:23 +04:00
kfree ( data ) ;
2005-04-17 02:20:36 +04:00
hci_free_dev ( hdev ) ;
2008-05-20 21:15:40 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
2006-07-06 17:45:23 +04:00
file - > private_data = data ;
2008-05-20 21:15:40 +04:00
unlock_kernel ( ) ;
2005-08-10 07:27:37 +04:00
return nonseekable_open ( inode , file ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-10 07:27:37 +04:00
static int vhci_release ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data = file - > private_data ;
struct hci_dev * hdev = data - > hdev ;
2005-04-17 02:20:36 +04:00
if ( hci_unregister_dev ( hdev ) < 0 ) {
BT_ERR ( " Can't unregister HCI device %s " , hdev - > name ) ;
}
hci_free_dev ( hdev ) ;
file - > private_data = NULL ;
2005-08-10 07:27:37 +04:00
return 0 ;
}
static int vhci_fasync ( int fd , struct file * file , int on )
{
2006-07-06 17:45:23 +04:00
struct vhci_data * data = file - > private_data ;
2008-06-20 02:07:51 +04:00
int err = 0 ;
2005-08-10 07:27:37 +04:00
2008-06-20 02:07:51 +04:00
lock_kernel ( ) ;
2006-07-06 17:45:23 +04:00
err = fasync_helper ( fd , file , on , & data - > fasync ) ;
2005-08-10 07:27:37 +04:00
if ( err < 0 )
2008-06-20 02:07:51 +04:00
goto out ;
2005-08-10 07:27:37 +04:00
if ( on )
2006-07-06 17:45:23 +04:00
data - > flags | = VHCI_FASYNC ;
2005-08-10 07:27:37 +04:00
else
2006-07-06 17:45:23 +04:00
data - > flags & = ~ VHCI_FASYNC ;
2005-08-10 07:27:37 +04:00
2008-06-20 02:07:51 +04:00
out :
unlock_kernel ( ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:32 +03:00
static const struct file_operations vhci_fops = {
2005-08-10 07:27:37 +04:00
. owner = THIS_MODULE ,
. read = vhci_read ,
. write = vhci_write ,
. poll = vhci_poll ,
. ioctl = vhci_ioctl ,
. open = vhci_open ,
. release = vhci_release ,
. fasync = vhci_fasync ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-10 07:27:37 +04:00
static struct miscdevice vhci_miscdev = {
. name = " vhci " ,
. fops = & vhci_fops ,
2005-04-17 02:20:36 +04:00
} ;
2005-08-10 07:27:37 +04:00
static int __init vhci_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-10 07:27:37 +04:00
BT_INFO ( " Virtual HCI driver ver %s " , VERSION ) ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:27:37 +04:00
vhci_miscdev . minor = minor ;
if ( misc_register ( & vhci_miscdev ) < 0 ) {
BT_ERR ( " Can't register misc device with minor %d " , minor ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
return 0 ;
}
2005-08-10 07:27:37 +04:00
static void __exit vhci_exit ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-10 07:27:37 +04:00
if ( misc_deregister ( & vhci_miscdev ) < 0 )
BT_ERR ( " Can't unregister misc device with minor %d " , minor ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-10 07:27:37 +04:00
module_init ( vhci_init ) ;
module_exit ( vhci_exit ) ;
module_param ( minor , int , 0444 ) ;
MODULE_PARM_DESC ( minor , " Miscellaneous minor device number " ) ;
2005-04-17 02:20:36 +04:00
2008-08-18 15:23:53 +04:00
MODULE_AUTHOR ( " Marcel Holtmann <marcel@holtmann.org> " ) ;
2005-08-10 07:27:37 +04:00
MODULE_DESCRIPTION ( " Bluetooth virtual HCI driver ver " VERSION ) ;
MODULE_VERSION ( VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;