2005-04-17 02:20:36 +04:00
/*
*
* Digianswer Bluetooth USB driver
*
* Copyright ( C ) 2004 - 2005 Marcel Holtmann < marcel @ holtmann . org >
*
*
* 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
*
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/usb.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# ifndef CONFIG_BT_HCIBPA10X_DEBUG
# undef BT_DBG
# define BT_DBG(D...)
# endif
# define VERSION "0.8"
static int ignore = 0 ;
static struct usb_device_id bpa10x_table [ ] = {
/* Tektronix BPA 100/105 (Digianswer) */
{ USB_DEVICE ( 0x08fd , 0x0002 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , bpa10x_table ) ;
# define BPA10X_CMD_EP 0x00
# define BPA10X_EVT_EP 0x81
# define BPA10X_TX_EP 0x02
# define BPA10X_RX_EP 0x82
# define BPA10X_CMD_BUF_SIZE 252
# define BPA10X_EVT_BUF_SIZE 16
# define BPA10X_TX_BUF_SIZE 384
# define BPA10X_RX_BUF_SIZE 384
struct bpa10x_data {
struct hci_dev * hdev ;
struct usb_device * udev ;
rwlock_t lock ;
struct sk_buff_head cmd_queue ;
struct urb * cmd_urb ;
struct urb * evt_urb ;
struct sk_buff * evt_skb ;
unsigned int evt_len ;
struct sk_buff_head tx_queue ;
struct urb * tx_urb ;
struct urb * rx_urb ;
} ;
# define HCI_VENDOR_HDR_SIZE 5
struct hci_vendor_hdr {
__u8 type ;
__u16 snum ;
__u16 dlen ;
} __attribute__ ( ( packed ) ) ;
static void bpa10x_recv_bulk ( struct bpa10x_data * data , unsigned char * buf , int count )
{
struct hci_acl_hdr * ah ;
struct hci_sco_hdr * sh ;
struct hci_vendor_hdr * vh ;
struct sk_buff * skb ;
int len ;
while ( count ) {
switch ( * buf + + ) {
case HCI_ACLDATA_PKT :
ah = ( struct hci_acl_hdr * ) buf ;
len = HCI_ACL_HDR_SIZE + __le16_to_cpu ( ah - > dlen ) ;
skb = bt_skb_alloc ( len , GFP_ATOMIC ) ;
if ( skb ) {
memcpy ( skb_put ( skb , len ) , buf , len ) ;
skb - > dev = ( void * ) data - > hdev ;
skb - > pkt_type = HCI_ACLDATA_PKT ;
hci_recv_frame ( skb ) ;
}
break ;
case HCI_SCODATA_PKT :
sh = ( struct hci_sco_hdr * ) buf ;
len = HCI_SCO_HDR_SIZE + sh - > dlen ;
skb = bt_skb_alloc ( len , GFP_ATOMIC ) ;
if ( skb ) {
memcpy ( skb_put ( skb , len ) , buf , len ) ;
skb - > dev = ( void * ) data - > hdev ;
skb - > pkt_type = HCI_SCODATA_PKT ;
hci_recv_frame ( skb ) ;
}
break ;
case HCI_VENDOR_PKT :
vh = ( struct hci_vendor_hdr * ) buf ;
len = HCI_VENDOR_HDR_SIZE + __le16_to_cpu ( vh - > dlen ) ;
skb = bt_skb_alloc ( len , GFP_ATOMIC ) ;
if ( skb ) {
memcpy ( skb_put ( skb , len ) , buf , len ) ;
skb - > dev = ( void * ) data - > hdev ;
skb - > pkt_type = HCI_VENDOR_PKT ;
hci_recv_frame ( skb ) ;
}
break ;
default :
len = count - 1 ;
break ;
}
buf + = len ;
count - = ( len + 1 ) ;
}
}
static int bpa10x_recv_event ( struct bpa10x_data * data , unsigned char * buf , int size )
{
BT_DBG ( " data %p buf %p size %d " , data , buf , size ) ;
if ( data - > evt_skb ) {
struct sk_buff * skb = data - > evt_skb ;
memcpy ( skb_put ( skb , size ) , buf , size ) ;
if ( skb - > len = = data - > evt_len ) {
data - > evt_skb = NULL ;
data - > evt_len = 0 ;
hci_recv_frame ( skb ) ;
}
} else {
struct sk_buff * skb ;
struct hci_event_hdr * hdr ;
unsigned char pkt_type ;
int pkt_len = 0 ;
if ( size < HCI_EVENT_HDR_SIZE + 1 ) {
BT_ERR ( " %s event packet block with size %d is too short " ,
data - > hdev - > name , size ) ;
return - EILSEQ ;
}
pkt_type = * buf + + ;
size - - ;
if ( pkt_type ! = HCI_EVENT_PKT ) {
BT_ERR ( " %s unexpected event packet start byte 0x%02x " ,
data - > hdev - > name , pkt_type ) ;
return - EPROTO ;
}
hdr = ( struct hci_event_hdr * ) buf ;
pkt_len = HCI_EVENT_HDR_SIZE + hdr - > plen ;
skb = bt_skb_alloc ( pkt_len , GFP_ATOMIC ) ;
if ( ! skb ) {
BT_ERR ( " %s no memory for new event packet " ,
data - > hdev - > name ) ;
return - ENOMEM ;
}
skb - > dev = ( void * ) data - > hdev ;
skb - > pkt_type = pkt_type ;
memcpy ( skb_put ( skb , size ) , buf , size ) ;
if ( pkt_len = = size ) {
hci_recv_frame ( skb ) ;
} else {
data - > evt_skb = skb ;
data - > evt_len = pkt_len ;
}
}
return 0 ;
}
static void bpa10x_wakeup ( struct bpa10x_data * data )
{
struct urb * urb ;
struct sk_buff * skb ;
int err ;
BT_DBG ( " data %p " , data ) ;
urb = data - > cmd_urb ;
if ( urb - > status = = - EINPROGRESS )
skb = NULL ;
else
skb = skb_dequeue ( & data - > cmd_queue ) ;
if ( skb ) {
struct usb_ctrlrequest * cr ;
if ( skb - > len > BPA10X_CMD_BUF_SIZE ) {
BT_ERR ( " %s command packet with size %d is too big " ,
data - > hdev - > name , skb - > len ) ;
kfree_skb ( skb ) ;
return ;
}
cr = ( struct usb_ctrlrequest * ) urb - > setup_packet ;
cr - > wLength = __cpu_to_le16 ( skb - > len ) ;
memcpy ( urb - > transfer_buffer , skb - > data , skb - > len ) ;
urb - > transfer_buffer_length = skb - > len ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 & & err ! = - ENODEV ) {
BT_ERR ( " %s submit failed for command urb %p with error %d " ,
data - > hdev - > name , urb , err ) ;
skb_queue_head ( & data - > cmd_queue , skb ) ;
} else
kfree_skb ( skb ) ;
}
urb = data - > tx_urb ;
if ( urb - > status = = - EINPROGRESS )
skb = NULL ;
else
skb = skb_dequeue ( & data - > tx_queue ) ;
if ( skb ) {
memcpy ( urb - > transfer_buffer , skb - > data , skb - > len ) ;
urb - > transfer_buffer_length = skb - > len ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 & & err ! = - ENODEV ) {
BT_ERR ( " %s submit failed for command urb %p with error %d " ,
data - > hdev - > name , urb , err ) ;
skb_queue_head ( & data - > tx_queue , skb ) ;
} else
kfree_skb ( skb ) ;
}
}
static void bpa10x_complete ( struct urb * urb , struct pt_regs * regs )
{
struct bpa10x_data * data = urb - > context ;
unsigned char * buf = urb - > transfer_buffer ;
int err , count = urb - > actual_length ;
BT_DBG ( " data %p urb %p buf %p count %d " , data , urb , buf , count ) ;
read_lock ( & data - > lock ) ;
if ( ! test_bit ( HCI_RUNNING , & data - > hdev - > flags ) )
goto unlock ;
if ( urb - > status < 0 | | ! count )
goto resubmit ;
if ( usb_pipein ( urb - > pipe ) ) {
data - > hdev - > stat . byte_rx + = count ;
if ( usb_pipetype ( urb - > pipe ) = = PIPE_INTERRUPT )
bpa10x_recv_event ( data , buf , count ) ;
if ( usb_pipetype ( urb - > pipe ) = = PIPE_BULK )
bpa10x_recv_bulk ( data , buf , count ) ;
} else {
data - > hdev - > stat . byte_tx + = count ;
bpa10x_wakeup ( data ) ;
}
resubmit :
if ( usb_pipein ( urb - > pipe ) ) {
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 & & err ! = - ENODEV ) {
BT_ERR ( " %s urb %p type %d resubmit status %d " ,
data - > hdev - > name , urb , usb_pipetype ( urb - > pipe ) , err ) ;
}
}
unlock :
read_unlock ( & data - > lock ) ;
}
2005-08-10 07:29:11 +04:00
static inline struct urb * bpa10x_alloc_urb ( struct usb_device * udev , unsigned int pipe ,
size_t size , unsigned int __nocast flags , void * data )
2005-04-17 02:20:36 +04:00
{
struct urb * urb ;
struct usb_ctrlrequest * cr ;
unsigned char * buf ;
BT_DBG ( " udev %p data %p " , udev , data ) ;
urb = usb_alloc_urb ( 0 , flags ) ;
if ( ! urb )
return NULL ;
buf = kmalloc ( size , flags ) ;
if ( ! buf ) {
usb_free_urb ( urb ) ;
return NULL ;
}
switch ( usb_pipetype ( pipe ) ) {
case PIPE_CONTROL :
cr = kmalloc ( sizeof ( * cr ) , flags ) ;
if ( ! cr ) {
kfree ( buf ) ;
usb_free_urb ( urb ) ;
return NULL ;
}
cr - > bRequestType = USB_TYPE_VENDOR ;
cr - > bRequest = 0 ;
cr - > wIndex = 0 ;
cr - > wValue = 0 ;
cr - > wLength = __cpu_to_le16 ( 0 ) ;
usb_fill_control_urb ( urb , udev , pipe , ( void * ) cr , buf , 0 , bpa10x_complete , data ) ;
break ;
case PIPE_INTERRUPT :
usb_fill_int_urb ( urb , udev , pipe , buf , size , bpa10x_complete , data , 1 ) ;
break ;
case PIPE_BULK :
usb_fill_bulk_urb ( urb , udev , pipe , buf , size , bpa10x_complete , data ) ;
break ;
default :
kfree ( buf ) ;
usb_free_urb ( urb ) ;
return NULL ;
}
return urb ;
}
static inline void bpa10x_free_urb ( struct urb * urb )
{
BT_DBG ( " urb %p " , urb ) ;
if ( ! urb )
return ;
2005-08-06 14:36:47 +04:00
kfree ( urb - > setup_packet ) ;
kfree ( urb - > transfer_buffer ) ;
2005-04-17 02:20:36 +04:00
usb_free_urb ( urb ) ;
}
static int bpa10x_open ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
struct usb_device * udev = data - > udev ;
unsigned long flags ;
int err ;
BT_DBG ( " hdev %p data %p " , hdev , data ) ;
if ( test_and_set_bit ( HCI_RUNNING , & hdev - > flags ) )
return 0 ;
data - > cmd_urb = bpa10x_alloc_urb ( udev , usb_sndctrlpipe ( udev , BPA10X_CMD_EP ) ,
BPA10X_CMD_BUF_SIZE , GFP_KERNEL , data ) ;
if ( ! data - > cmd_urb ) {
err = - ENOMEM ;
goto done ;
}
data - > evt_urb = bpa10x_alloc_urb ( udev , usb_rcvintpipe ( udev , BPA10X_EVT_EP ) ,
BPA10X_EVT_BUF_SIZE , GFP_KERNEL , data ) ;
if ( ! data - > evt_urb ) {
bpa10x_free_urb ( data - > cmd_urb ) ;
err = - ENOMEM ;
goto done ;
}
data - > rx_urb = bpa10x_alloc_urb ( udev , usb_rcvbulkpipe ( udev , BPA10X_RX_EP ) ,
BPA10X_RX_BUF_SIZE , GFP_KERNEL , data ) ;
if ( ! data - > rx_urb ) {
bpa10x_free_urb ( data - > evt_urb ) ;
bpa10x_free_urb ( data - > cmd_urb ) ;
err = - ENOMEM ;
goto done ;
}
data - > tx_urb = bpa10x_alloc_urb ( udev , usb_sndbulkpipe ( udev , BPA10X_TX_EP ) ,
BPA10X_TX_BUF_SIZE , GFP_KERNEL , data ) ;
if ( ! data - > rx_urb ) {
bpa10x_free_urb ( data - > rx_urb ) ;
bpa10x_free_urb ( data - > evt_urb ) ;
bpa10x_free_urb ( data - > cmd_urb ) ;
err = - ENOMEM ;
goto done ;
}
write_lock_irqsave ( & data - > lock , flags ) ;
err = usb_submit_urb ( data - > evt_urb , GFP_ATOMIC ) ;
if ( err < 0 ) {
BT_ERR ( " %s submit failed for event urb %p with error %d " ,
data - > hdev - > name , data - > evt_urb , err ) ;
} else {
err = usb_submit_urb ( data - > rx_urb , GFP_ATOMIC ) ;
if ( err < 0 ) {
BT_ERR ( " %s submit failed for rx urb %p with error %d " ,
data - > hdev - > name , data - > evt_urb , err ) ;
usb_kill_urb ( data - > evt_urb ) ;
}
}
write_unlock_irqrestore ( & data - > lock , flags ) ;
done :
if ( err < 0 )
clear_bit ( HCI_RUNNING , & hdev - > flags ) ;
return err ;
}
static int bpa10x_close ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
unsigned long flags ;
BT_DBG ( " hdev %p data %p " , hdev , data ) ;
if ( ! test_and_clear_bit ( HCI_RUNNING , & hdev - > flags ) )
return 0 ;
write_lock_irqsave ( & data - > lock , flags ) ;
skb_queue_purge ( & data - > cmd_queue ) ;
usb_kill_urb ( data - > cmd_urb ) ;
usb_kill_urb ( data - > evt_urb ) ;
usb_kill_urb ( data - > rx_urb ) ;
usb_kill_urb ( data - > tx_urb ) ;
write_unlock_irqrestore ( & data - > lock , flags ) ;
bpa10x_free_urb ( data - > cmd_urb ) ;
bpa10x_free_urb ( data - > evt_urb ) ;
bpa10x_free_urb ( data - > rx_urb ) ;
bpa10x_free_urb ( data - > tx_urb ) ;
return 0 ;
}
static int bpa10x_flush ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
BT_DBG ( " hdev %p data %p " , hdev , data ) ;
skb_queue_purge ( & data - > cmd_queue ) ;
return 0 ;
}
static int bpa10x_send_frame ( struct sk_buff * skb )
{
struct hci_dev * hdev = ( struct hci_dev * ) skb - > dev ;
struct bpa10x_data * data ;
BT_DBG ( " hdev %p skb %p type %d len %d " , hdev , skb , skb - > pkt_type , skb - > len ) ;
if ( ! hdev ) {
BT_ERR ( " Frame for unknown HCI device " ) ;
return - ENODEV ;
}
if ( ! test_bit ( HCI_RUNNING , & hdev - > flags ) )
return - EBUSY ;
data = hdev - > driver_data ;
/* Prepend skb with frame type */
memcpy ( skb_push ( skb , 1 ) , & ( skb - > pkt_type ) , 1 ) ;
switch ( skb - > pkt_type ) {
case HCI_COMMAND_PKT :
hdev - > stat . cmd_tx + + ;
skb_queue_tail ( & data - > cmd_queue , skb ) ;
break ;
case HCI_ACLDATA_PKT :
hdev - > stat . acl_tx + + ;
skb_queue_tail ( & data - > tx_queue , skb ) ;
break ;
case HCI_SCODATA_PKT :
hdev - > stat . sco_tx + + ;
skb_queue_tail ( & data - > tx_queue , skb ) ;
break ;
} ;
read_lock ( & data - > lock ) ;
bpa10x_wakeup ( data ) ;
read_unlock ( & data - > lock ) ;
return 0 ;
}
static void bpa10x_destruct ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
BT_DBG ( " hdev %p data %p " , hdev , data ) ;
kfree ( data ) ;
}
static int bpa10x_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
struct usb_device * udev = interface_to_usbdev ( intf ) ;
struct hci_dev * hdev ;
struct bpa10x_data * data ;
int err ;
BT_DBG ( " intf %p id %p " , intf , id ) ;
if ( ignore )
return - ENODEV ;
data = kmalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data ) {
BT_ERR ( " Can't allocate data structure " ) ;
return - ENOMEM ;
}
memset ( data , 0 , sizeof ( * data ) ) ;
data - > udev = udev ;
rwlock_init ( & data - > lock ) ;
skb_queue_head_init ( & data - > cmd_queue ) ;
skb_queue_head_init ( & data - > tx_queue ) ;
hdev = hci_alloc_dev ( ) ;
if ( ! hdev ) {
BT_ERR ( " Can't allocate HCI device " ) ;
kfree ( data ) ;
return - ENOMEM ;
}
data - > hdev = hdev ;
hdev - > type = HCI_USB ;
hdev - > driver_data = data ;
SET_HCIDEV_DEV ( hdev , & intf - > dev ) ;
hdev - > open = bpa10x_open ;
hdev - > close = bpa10x_close ;
hdev - > flush = bpa10x_flush ;
hdev - > send = bpa10x_send_frame ;
hdev - > destruct = bpa10x_destruct ;
hdev - > owner = THIS_MODULE ;
err = hci_register_dev ( hdev ) ;
if ( err < 0 ) {
BT_ERR ( " Can't register HCI device " ) ;
kfree ( data ) ;
hci_free_dev ( hdev ) ;
return err ;
}
usb_set_intfdata ( intf , data ) ;
return 0 ;
}
static void bpa10x_disconnect ( struct usb_interface * intf )
{
struct bpa10x_data * data = usb_get_intfdata ( intf ) ;
struct hci_dev * hdev = data - > hdev ;
BT_DBG ( " intf %p " , intf ) ;
if ( ! hdev )
return ;
usb_set_intfdata ( intf , NULL ) ;
if ( hci_unregister_dev ( hdev ) < 0 )
BT_ERR ( " Can't unregister HCI device %s " , hdev - > name ) ;
hci_free_dev ( hdev ) ;
}
static struct usb_driver bpa10x_driver = {
. owner = THIS_MODULE ,
. name = " bpa10x " ,
. probe = bpa10x_probe ,
. disconnect = bpa10x_disconnect ,
. id_table = bpa10x_table ,
} ;
static int __init bpa10x_init ( void )
{
int err ;
BT_INFO ( " Digianswer Bluetooth USB driver ver %s " , VERSION ) ;
err = usb_register ( & bpa10x_driver ) ;
if ( err < 0 )
BT_ERR ( " Failed to register USB driver " ) ;
return err ;
}
static void __exit bpa10x_exit ( void )
{
usb_deregister ( & bpa10x_driver ) ;
}
module_init ( bpa10x_init ) ;
module_exit ( bpa10x_exit ) ;
module_param ( ignore , bool , 0644 ) ;
MODULE_PARM_DESC ( ignore , " Ignore devices from the matching table " ) ;
MODULE_AUTHOR ( " Marcel Holtmann <marcel@holtmann.org> " ) ;
MODULE_DESCRIPTION ( " Digianswer Bluetooth USB driver ver " VERSION ) ;
MODULE_VERSION ( VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;