2005-04-17 02:20:36 +04:00
/*
*
* Digianswer Bluetooth USB driver
*
2007-10-20 15:41:33 +04:00
* Copyright ( C ) 2004 - 2007 Marcel Holtmann < marcel @ holtmann . org >
2005-04-17 02:20:36 +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
*
*/
# include <linux/kernel.h>
2007-10-20 15:41:33 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/types.h>
2007-10-20 15:41:33 +04:00
# include <linux/sched.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
2007-10-20 15:41:33 +04:00
# include <linux/skbuff.h>
2005-04-17 02:20:36 +04:00
# 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
2007-10-20 15:41:33 +04:00
# define VERSION "0.9"
2005-04-17 02:20:36 +04:00
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 ) ;
struct bpa10x_data {
2007-10-20 15:41:33 +04:00
struct hci_dev * hdev ;
struct usb_device * udev ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
struct usb_anchor tx_anchor ;
struct usb_anchor rx_anchor ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
struct sk_buff * rx_skb [ 2 ] ;
2005-04-17 02:20:36 +04:00
} ;
2007-10-20 15:41:33 +04:00
# define HCI_VENDOR_HDR_SIZE 5
2005-04-17 02:20:36 +04:00
struct hci_vendor_hdr {
2007-10-20 15:41:33 +04:00
__u8 type ;
__le16 snum ;
__le16 dlen ;
2005-04-17 02:20:36 +04:00
} __attribute__ ( ( packed ) ) ;
2007-10-20 15:41:33 +04:00
static int bpa10x_recv ( struct hci_dev * hdev , int queue , void * buf , int count )
2005-04-17 02:20:36 +04:00
{
2007-10-20 15:41:33 +04:00
struct bpa10x_data * data = hdev - > driver_data ;
BT_DBG ( " %s queue %d buffer %p count %d " , hdev - > name ,
queue , buf , count ) ;
if ( queue < 0 | | queue > 1 )
return - EILSEQ ;
hdev - > stat . byte_rx + = count ;
2005-04-17 02:20:36 +04:00
while ( count ) {
2007-10-20 15:41:33 +04:00
struct sk_buff * skb = data - > rx_skb [ queue ] ;
struct { __u8 type ; int expect ; } * scb ;
int type , len = 0 ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
if ( ! skb ) {
/* Start of the frame */
type = * ( ( __u8 * ) buf ) ;
count - - ; buf + + ;
switch ( type ) {
case HCI_EVENT_PKT :
if ( count > = HCI_EVENT_HDR_SIZE ) {
struct hci_event_hdr * h = buf ;
len = HCI_EVENT_HDR_SIZE + h - > plen ;
} else
return - EILSEQ ;
break ;
case HCI_ACLDATA_PKT :
if ( count > = HCI_ACL_HDR_SIZE ) {
struct hci_acl_hdr * h = buf ;
len = HCI_ACL_HDR_SIZE +
__le16_to_cpu ( h - > dlen ) ;
} else
return - EILSEQ ;
break ;
case HCI_SCODATA_PKT :
if ( count > = HCI_SCO_HDR_SIZE ) {
struct hci_sco_hdr * h = buf ;
len = HCI_SCO_HDR_SIZE + h - > dlen ;
} else
return - EILSEQ ;
break ;
case HCI_VENDOR_PKT :
if ( count > = HCI_VENDOR_HDR_SIZE ) {
struct hci_vendor_hdr * h = buf ;
len = HCI_VENDOR_HDR_SIZE +
__le16_to_cpu ( h - > dlen ) ;
} else
return - EILSEQ ;
break ;
2005-04-17 02:20:36 +04:00
}
skb = bt_skb_alloc ( len , GFP_ATOMIC ) ;
2007-10-20 15:41:33 +04:00
if ( ! skb ) {
BT_ERR ( " %s no memory for packet " , hdev - > name ) ;
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2007-10-20 15:41:33 +04:00
skb - > dev = ( void * ) hdev ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
data - > rx_skb [ queue ] = skb ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
scb = ( void * ) skb - > cb ;
scb - > type = type ;
scb - > expect = len ;
} else {
/* Continuation */
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
scb = ( void * ) skb - > cb ;
len = scb - > expect ;
2005-04-17 02:20:36 +04:00
}
2007-10-20 15:41:33 +04:00
len = min ( len , count ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
memcpy ( skb_put ( skb , len ) , buf , len ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
scb - > expect - = len ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
if ( scb - > expect = = 0 ) {
/* Complete frame */
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
data - > rx_skb [ queue ] = NULL ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
bt_cb ( skb ) - > pkt_type = scb - > type ;
2005-04-17 02:20:36 +04:00
hci_recv_frame ( skb ) ;
}
2007-10-20 15:41:33 +04:00
count - = len ; buf + = len ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2007-10-20 15:41:33 +04:00
static void bpa10x_tx_complete ( struct urb * urb )
2005-04-17 02:20:36 +04:00
{
2007-10-20 15:41:33 +04:00
struct sk_buff * skb = urb - > context ;
struct hci_dev * hdev = ( struct hci_dev * ) skb - > dev ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s urb %p status %d count %d " , hdev - > name ,
urb , urb - > status , urb - > actual_length ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
if ( ! test_bit ( HCI_RUNNING , & hdev - > flags ) )
goto done ;
if ( ! urb - > status )
hdev - > stat . byte_tx + = urb - > transfer_buffer_length ;
2005-04-17 02:20:36 +04:00
else
2007-10-20 15:41:33 +04:00
hdev - > stat . err_tx + + ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
done :
kfree ( urb - > setup_packet ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
kfree_skb ( skb ) ;
}
static void bpa10x_rx_complete ( struct urb * urb )
{
struct hci_dev * hdev = urb - > context ;
struct bpa10x_data * data = hdev - > driver_data ;
int err ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s urb %p status %d count %d " , hdev - > name ,
urb , urb - > status , urb - > actual_length ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
if ( ! test_bit ( HCI_RUNNING , & hdev - > flags ) )
return ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
if ( urb - > status = = 0 ) {
if ( bpa10x_recv ( hdev , usb_pipebulk ( urb - > pipe ) ,
urb - > transfer_buffer ,
urb - > actual_length ) < 0 ) {
BT_ERR ( " %s corrupted event packet " , hdev - > name ) ;
hdev - > stat . err_rx + + ;
}
2005-04-17 02:20:36 +04:00
}
2007-10-20 15:41:33 +04:00
usb_anchor_urb ( urb , & data - > rx_anchor ) ;
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 ) {
BT_ERR ( " %s urb %p failed to resubmit (%d) " ,
hdev - > name , urb , - err ) ;
usb_unanchor_urb ( urb ) ;
2005-04-17 02:20:36 +04:00
}
}
2007-10-20 15:41:33 +04:00
static inline int bpa10x_submit_intr_urb ( struct hci_dev * hdev )
2005-04-17 02:20:36 +04:00
{
2007-10-20 15:41:33 +04:00
struct bpa10x_data * data = hdev - > driver_data ;
struct urb * urb ;
unsigned char * buf ;
unsigned int pipe ;
int err , size = 16 ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urb )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
buf = kmalloc ( size , GFP_KERNEL ) ;
if ( ! buf ) {
usb_free_urb ( urb ) ;
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
pipe = usb_rcvintpipe ( data - > udev , 0x81 ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
usb_fill_int_urb ( urb , data - > udev , pipe , buf , size ,
bpa10x_rx_complete , hdev , 1 ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
urb - > transfer_flags | = URB_FREE_BUFFER ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
usb_anchor_urb ( urb , & data - > rx_anchor ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
err = usb_submit_urb ( urb , GFP_KERNEL ) ;
if ( err < 0 ) {
BT_ERR ( " %s urb %p submission failed (%d) " ,
hdev - > name , urb , - err ) ;
usb_unanchor_urb ( urb ) ;
kfree ( buf ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-20 15:41:33 +04:00
usb_free_urb ( urb ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-10-20 15:41:33 +04:00
static inline int bpa10x_submit_bulk_urb ( struct hci_dev * hdev )
2005-04-17 02:20:36 +04:00
{
2007-10-20 15:41:33 +04:00
struct bpa10x_data * data = hdev - > driver_data ;
2005-04-17 02:20:36 +04:00
struct urb * urb ;
unsigned char * buf ;
2007-10-20 15:41:33 +04:00
unsigned int pipe ;
int err , size = 64 ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! urb )
2007-10-20 15:41:33 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
buf = kmalloc ( size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! buf ) {
usb_free_urb ( urb ) ;
2007-10-20 15:41:33 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2007-10-20 15:41:33 +04:00
pipe = usb_rcvbulkpipe ( data - > udev , 0x82 ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
usb_fill_bulk_urb ( urb , data - > udev , pipe ,
buf , size , bpa10x_rx_complete , hdev ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
urb - > transfer_flags | = URB_FREE_BUFFER ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
usb_anchor_urb ( urb , & data - > rx_anchor ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
err = usb_submit_urb ( urb , GFP_KERNEL ) ;
if ( err < 0 ) {
BT_ERR ( " %s urb %p submission failed (%d) " ,
hdev - > name , urb , - err ) ;
usb_unanchor_urb ( urb ) ;
2005-04-17 02:20:36 +04:00
kfree ( buf ) ;
}
usb_free_urb ( urb ) ;
2007-10-20 15:41:33 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
static int bpa10x_open ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
int err ;
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
if ( test_and_set_bit ( HCI_RUNNING , & hdev - > flags ) )
return 0 ;
2007-10-20 15:41:33 +04:00
err = bpa10x_submit_intr_urb ( hdev ) ;
if ( err < 0 )
goto error ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
err = bpa10x_submit_bulk_urb ( hdev ) ;
if ( err < 0 )
goto error ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
error :
usb_kill_anchored_urbs ( & data - > rx_anchor ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
clear_bit ( HCI_RUNNING , & hdev - > flags ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static int bpa10x_close ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
if ( ! test_and_clear_bit ( HCI_RUNNING , & hdev - > flags ) )
return 0 ;
2007-10-20 15:41:33 +04:00
usb_kill_anchored_urbs ( & data - > rx_anchor ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int bpa10x_flush ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
usb_kill_anchored_urbs ( & data - > tx_anchor ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int bpa10x_send_frame ( struct sk_buff * skb )
{
struct hci_dev * hdev = ( struct hci_dev * ) skb - > dev ;
2007-10-20 15:41:33 +04:00
struct bpa10x_data * data = hdev - > driver_data ;
struct usb_ctrlrequest * dr ;
struct urb * urb ;
unsigned int pipe ;
int err ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
if ( ! test_bit ( HCI_RUNNING , & hdev - > flags ) )
return - EBUSY ;
2007-10-20 15:41:33 +04:00
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( ! urb )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
/* Prepend skb with frame type */
2007-10-20 15:41:33 +04:00
* skb_push ( skb , 1 ) = bt_cb ( skb ) - > pkt_type ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:30:28 +04:00
switch ( bt_cb ( skb ) - > pkt_type ) {
2005-04-17 02:20:36 +04:00
case HCI_COMMAND_PKT :
2007-10-20 15:41:33 +04:00
dr = kmalloc ( sizeof ( * dr ) , GFP_ATOMIC ) ;
if ( ! dr ) {
usb_free_urb ( urb ) ;
return - ENOMEM ;
}
dr - > bRequestType = USB_TYPE_VENDOR ;
dr - > bRequest = 0 ;
dr - > wIndex = 0 ;
dr - > wValue = 0 ;
dr - > wLength = __cpu_to_le16 ( skb - > len ) ;
pipe = usb_sndctrlpipe ( data - > udev , 0x00 ) ;
usb_fill_control_urb ( urb , data - > udev , pipe , ( void * ) dr ,
skb - > data , skb - > len , bpa10x_tx_complete , skb ) ;
2005-04-17 02:20:36 +04:00
hdev - > stat . cmd_tx + + ;
break ;
case HCI_ACLDATA_PKT :
2007-10-20 15:41:33 +04:00
pipe = usb_sndbulkpipe ( data - > udev , 0x02 ) ;
usb_fill_bulk_urb ( urb , data - > udev , pipe ,
skb - > data , skb - > len , bpa10x_tx_complete , skb ) ;
2005-04-17 02:20:36 +04:00
hdev - > stat . acl_tx + + ;
break ;
case HCI_SCODATA_PKT :
2007-10-20 15:41:33 +04:00
pipe = usb_sndbulkpipe ( data - > udev , 0x02 ) ;
usb_fill_bulk_urb ( urb , data - > udev , pipe ,
skb - > data , skb - > len , bpa10x_tx_complete , skb ) ;
2005-04-17 02:20:36 +04:00
hdev - > stat . sco_tx + + ;
break ;
2007-10-20 15:41:33 +04:00
default :
2008-02-05 14:08:45 +03:00
usb_free_urb ( urb ) ;
2007-10-20 15:41:33 +04:00
return - EILSEQ ;
}
usb_anchor_urb ( urb , & data - > tx_anchor ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
err = usb_submit_urb ( urb , GFP_ATOMIC ) ;
if ( err < 0 ) {
BT_ERR ( " %s urb %p submission failed " , hdev - > name , urb ) ;
kfree ( urb - > setup_packet ) ;
usb_unanchor_urb ( urb ) ;
}
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
usb_free_urb ( urb ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void bpa10x_destruct ( struct hci_dev * hdev )
{
struct bpa10x_data * data = hdev - > driver_data ;
2007-10-20 15:41:33 +04:00
BT_DBG ( " %s " , hdev - > name ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
kfree ( data - > rx_skb [ 0 ] ) ;
kfree ( data - > rx_skb [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
kfree ( data ) ;
}
static int bpa10x_probe ( struct usb_interface * intf , const struct usb_device_id * id )
{
struct bpa10x_data * data ;
2007-10-20 15:41:33 +04:00
struct hci_dev * hdev ;
2005-04-17 02:20:36 +04:00
int err ;
BT_DBG ( " intf %p id %p " , intf , id ) ;
if ( ignore )
2005-10-28 21:20:57 +04:00
return - ENODEV ;
2007-10-20 15:41:33 +04:00
if ( intf - > cur_altsetting - > desc . bInterfaceNumber ! = 0 )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2005-11-07 12:01:26 +03:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2007-10-20 15:41:33 +04:00
if ( ! data )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2007-10-20 15:41:33 +04:00
data - > udev = interface_to_usbdev ( intf ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
init_usb_anchor ( & data - > tx_anchor ) ;
init_usb_anchor ( & data - > rx_anchor ) ;
2005-04-17 02:20:36 +04:00
hdev = hci_alloc_dev ( ) ;
if ( ! hdev ) {
kfree ( data ) ;
return - ENOMEM ;
}
hdev - > type = HCI_USB ;
hdev - > driver_data = data ;
2007-10-20 15:41:33 +04:00
data - > hdev = hdev ;
2005-04-17 02:20:36 +04:00
SET_HCIDEV_DEV ( hdev , & intf - > dev ) ;
2007-10-20 15:41:33 +04:00
hdev - > open = bpa10x_open ;
hdev - > close = bpa10x_close ;
hdev - > flush = bpa10x_flush ;
hdev - > send = bpa10x_send_frame ;
hdev - > destruct = bpa10x_destruct ;
2005-04-17 02:20:36 +04:00
hdev - > owner = THIS_MODULE ;
err = hci_register_dev ( hdev ) ;
if ( err < 0 ) {
hci_free_dev ( hdev ) ;
2007-10-20 15:41:33 +04:00
kfree ( data ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
BT_DBG ( " intf %p " , intf ) ;
2007-10-20 15:41:33 +04:00
if ( ! data )
2005-04-17 02:20:36 +04:00
return ;
usb_set_intfdata ( intf , NULL ) ;
2007-10-20 15:41:33 +04:00
hci_unregister_dev ( data - > hdev ) ;
2005-04-17 02:20:36 +04:00
2007-10-20 15:41:33 +04:00
hci_free_dev ( data - > hdev ) ;
2005-04-17 02:20:36 +04:00
}
static struct usb_driver bpa10x_driver = {
. name = " bpa10x " ,
. probe = bpa10x_probe ,
. disconnect = bpa10x_disconnect ,
. id_table = bpa10x_table ,
} ;
static int __init bpa10x_init ( void )
{
BT_INFO ( " Digianswer Bluetooth USB driver ver %s " , VERSION ) ;
2007-10-20 15:41:33 +04:00
return usb_register ( & bpa10x_driver ) ;
2005-04-17 02:20:36 +04:00
}
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 " ) ;