2011-02-21 07:41:16 +03:00
/*
* Texas Instrument ' s Bluetooth Driver For Shared Transport .
*
* Bluetooth Driver acts as interface between HCI core and
* TI Shared Transport Layer .
*
* Copyright ( C ) 2009 - 2010 Texas Instruments
* Author : Raja Mani < raja_mani @ ti . com >
* Pavan Savoy < pavan_savoy @ ti . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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
*
*/
2015-05-05 10:09:17 +03:00
2011-02-21 07:41:16 +03:00
# include <linux/platform_device.h>
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
# include <net/bluetooth/hci.h>
# include <linux/ti_wilink_st.h>
2011-08-30 00:44:23 +04:00
# include <linux/module.h>
2011-02-21 07:41:16 +03:00
/* Bluetooth Driver Version */
# define VERSION "1.0"
# define MAX_BT_CHNL_IDS 3
/* Number of seconds to wait for registration completion
* when ST returns PENDING status .
*/
# define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
/**
* struct ti_st - driver operation structure
* @ hdev : hci device pointer which binds to bt driver
* @ reg_status : ST registration callback status
* @ st_write : write function provided by the ST driver
* to be used by the driver during send_frame .
* @ wait_reg_completion - completion sync between ti_st_open
* and st_reg_completion_cb .
*/
struct ti_st {
struct hci_dev * hdev ;
2016-06-06 12:02:03 +03:00
int reg_status ;
2011-02-21 07:41:16 +03:00
long ( * st_write ) ( struct sk_buff * ) ;
struct completion wait_reg_completion ;
} ;
/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
static inline void ti_st_tx_complete ( struct ti_st * hst , int pkt_type )
{
struct hci_dev * hdev = hst - > hdev ;
/* Update HCI stat counters */
switch ( pkt_type ) {
case HCI_COMMAND_PKT :
hdev - > stat . cmd_tx + + ;
break ;
case HCI_ACLDATA_PKT :
hdev - > stat . acl_tx + + ;
break ;
case HCI_SCODATA_PKT :
hdev - > stat . sco_tx + + ;
break ;
}
}
/* ------- Interfaces to Shared Transport ------ */
/* Called by ST layer to indicate protocol registration completion
* status . ti_st_open ( ) function will wait for signal from this
* API when st_register ( ) function returns ST_PENDING .
*/
2016-06-06 12:02:03 +03:00
static void st_reg_completion_cb ( void * priv_data , int data )
2011-02-21 07:41:16 +03:00
{
struct ti_st * lhst = priv_data ;
/* Save registration status for use in ti_st_open() */
lhst - > reg_status = data ;
/* complete the wait in ti_st_open() */
complete ( & lhst - > wait_reg_completion ) ;
}
2017-07-22 04:47:07 +03:00
/* Called by Shared Transport layer when receive data is available */
2011-02-21 07:41:16 +03:00
static long st_receive ( void * priv_data , struct sk_buff * skb )
{
struct ti_st * lhst = priv_data ;
int err ;
if ( ! skb )
return - EFAULT ;
if ( ! lhst ) {
kfree_skb ( skb ) ;
return - EFAULT ;
}
/* Forward skb to HCI core layer */
2013-10-11 03:52:43 +04:00
err = hci_recv_frame ( lhst - > hdev , skb ) ;
2011-02-21 07:41:16 +03:00
if ( err < 0 ) {
BT_ERR ( " Unable to push skb to HCI core(%d) " , err ) ;
return err ;
}
lhst - > hdev - > stat . byte_rx + = skb - > len ;
return 0 ;
}
/* ------- Interfaces to HCI layer ------ */
/* protocol structure registered with shared transport */
static struct st_proto_s ti_st_proto [ MAX_BT_CHNL_IDS ] = {
2011-08-30 14:58:28 +04:00
{
. chnl_id = HCI_EVENT_PKT , /* HCI Events */
. hdr_len = sizeof ( struct hci_event_hdr ) ,
. offset_len_in_hdr = offsetof ( struct hci_event_hdr , plen ) ,
. len_size = 1 , /* sizeof(plen) in struct hci_event_hdr */
. reserve = 8 ,
} ,
2011-02-21 07:41:16 +03:00
{
. chnl_id = HCI_ACLDATA_PKT , /* ACL */
. hdr_len = sizeof ( struct hci_acl_hdr ) ,
. offset_len_in_hdr = offsetof ( struct hci_acl_hdr , dlen ) ,
. len_size = 2 , /* sizeof(dlen) in struct hci_acl_hdr */
. reserve = 8 ,
} ,
{
. chnl_id = HCI_SCODATA_PKT , /* SCO */
. hdr_len = sizeof ( struct hci_sco_hdr ) ,
. offset_len_in_hdr = offsetof ( struct hci_sco_hdr , dlen ) ,
. len_size = 1 , /* sizeof(dlen) in struct hci_sco_hdr */
. reserve = 8 ,
} ,
} ;
/* Called from HCI core to initialize the device */
static int ti_st_open ( struct hci_dev * hdev )
{
unsigned long timeleft ;
struct ti_st * hst ;
int err , i ;
BT_DBG ( " %s %p " , hdev - > name , hdev ) ;
/* provide contexts for callbacks from ST */
2012-02-10 00:58:32 +04:00
hst = hci_get_drvdata ( hdev ) ;
2011-02-21 07:41:16 +03:00
for ( i = 0 ; i < MAX_BT_CHNL_IDS ; i + + ) {
ti_st_proto [ i ] . priv_data = hst ;
ti_st_proto [ i ] . max_frame_size = HCI_MAX_FRAME_SIZE ;
ti_st_proto [ i ] . recv = st_receive ;
ti_st_proto [ i ] . reg_complete_cb = st_reg_completion_cb ;
/* Prepare wait-for-completion handler */
init_completion ( & hst - > wait_reg_completion ) ;
/* Reset ST registration callback status flag,
* this value will be updated in
* st_reg_completion_cb ( )
* function whenever it called from ST driver .
*/
hst - > reg_status = - EINPROGRESS ;
err = st_register ( & ti_st_proto [ i ] ) ;
if ( ! err )
goto done ;
if ( err ! = - EINPROGRESS ) {
BT_ERR ( " st_register failed %d " , err ) ;
return err ;
}
/* ST is busy with either protocol
* registration or firmware download .
*/
BT_DBG ( " waiting for registration "
" completion signal from ST " ) ;
timeleft = wait_for_completion_timeout
( & hst - > wait_reg_completion ,
msecs_to_jiffies ( BT_REGISTER_TIMEOUT ) ) ;
if ( ! timeleft ) {
BT_ERR ( " Timeout(%d sec),didn't get reg "
" completion signal from ST " ,
BT_REGISTER_TIMEOUT / 1000 ) ;
return - ETIMEDOUT ;
}
/* Is ST registration callback
2017-07-22 04:47:07 +03:00
* called with ERROR status ?
*/
2011-02-21 07:41:16 +03:00
if ( hst - > reg_status ! = 0 ) {
BT_ERR ( " ST registration completed with invalid "
" status %d " , hst - > reg_status ) ;
return - EAGAIN ;
}
done :
hst - > st_write = ti_st_proto [ i ] . write ;
if ( ! hst - > st_write ) {
BT_ERR ( " undefined ST write function " ) ;
for ( i = 0 ; i < MAX_BT_CHNL_IDS ; i + + ) {
/* Undo registration with ST */
err = st_unregister ( & ti_st_proto [ i ] ) ;
if ( err )
BT_ERR ( " st_unregister() failed with "
" error %d " , err ) ;
hst - > st_write = NULL ;
}
return - EIO ;
}
}
return 0 ;
}
/* Close device */
static int ti_st_close ( struct hci_dev * hdev )
{
int err , i ;
2012-02-10 00:58:32 +04:00
struct ti_st * hst = hci_get_drvdata ( hdev ) ;
2011-02-21 07:41:16 +03:00
2011-08-30 14:58:28 +04:00
for ( i = MAX_BT_CHNL_IDS - 1 ; i > = 0 ; i - - ) {
2011-02-21 07:41:16 +03:00
err = st_unregister ( & ti_st_proto [ i ] ) ;
if ( err )
BT_ERR ( " st_unregister(%d) failed with error %d " ,
ti_st_proto [ i ] . chnl_id , err ) ;
}
hst - > st_write = NULL ;
return err ;
}
2013-10-11 17:19:18 +04:00
static int ti_st_send_frame ( struct hci_dev * hdev , struct sk_buff * skb )
2011-02-21 07:41:16 +03:00
{
struct ti_st * hst ;
long len ;
2016-09-22 21:20:42 +03:00
int pkt_type ;
2011-02-21 07:41:16 +03:00
2012-02-10 00:58:32 +04:00
hst = hci_get_drvdata ( hdev ) ;
2011-02-21 07:41:16 +03:00
/* Prepend skb with frame type */
2015-11-05 09:33:56 +03:00
memcpy ( skb_push ( skb , 1 ) , & hci_skb_pkt_type ( skb ) , 1 ) ;
2011-02-21 07:41:16 +03:00
2015-11-05 09:33:56 +03:00
BT_DBG ( " %s: type %d len %d " , hdev - > name , hci_skb_pkt_type ( skb ) ,
skb - > len ) ;
2011-02-21 07:41:16 +03:00
/* Insert skb to shared transport layer's transmit queue.
* Freeing skb memory is taken care in shared transport layer ,
* so don ' t free skb memory here .
*/
2016-09-22 21:20:42 +03:00
pkt_type = hci_skb_pkt_type ( skb ) ;
2011-02-21 07:41:16 +03:00
len = hst - > st_write ( skb ) ;
if ( len < 0 ) {
BT_ERR ( " ST write failed (%ld) " , len ) ;
/* Try Again, would only fail if UART has gone bad */
return - EAGAIN ;
}
/* ST accepted our skb. So, Go ahead and do rest */
hdev - > stat . byte_tx + = len ;
2016-09-22 21:20:42 +03:00
ti_st_tx_complete ( hst , pkt_type ) ;
2011-02-21 07:41:16 +03:00
return 0 ;
}
static int bt_ti_probe ( struct platform_device * pdev )
{
2017-07-20 01:49:31 +03:00
struct ti_st * hst ;
2011-02-21 07:41:16 +03:00
struct hci_dev * hdev ;
int err ;
2012-07-27 11:08:40 +04:00
hst = devm_kzalloc ( & pdev - > dev , sizeof ( struct ti_st ) , GFP_KERNEL ) ;
2011-02-21 07:41:16 +03:00
if ( ! hst )
return - ENOMEM ;
/* Expose "hciX" device to user space */
hdev = hci_alloc_dev ( ) ;
2012-07-27 11:08:40 +04:00
if ( ! hdev )
2011-02-21 07:41:16 +03:00
return - ENOMEM ;
BT_DBG ( " hdev %p " , hdev ) ;
hst - > hdev = hdev ;
hdev - > bus = HCI_UART ;
2012-02-10 00:58:32 +04:00
hci_set_drvdata ( hdev , hst ) ;
2011-02-21 07:41:16 +03:00
hdev - > open = ti_st_open ;
hdev - > close = ti_st_close ;
hdev - > flush = NULL ;
hdev - > send = ti_st_send_frame ;
err = hci_register_dev ( hdev ) ;
if ( err < 0 ) {
BT_ERR ( " Can't register HCI device error %d " , err ) ;
hci_free_dev ( hdev ) ;
return err ;
}
BT_DBG ( " HCI device registered (hdev %p) " , hdev ) ;
dev_set_drvdata ( & pdev - > dev , hst ) ;
2016-10-20 10:05:09 +03:00
return 0 ;
2011-02-21 07:41:16 +03:00
}
static int bt_ti_remove ( struct platform_device * pdev )
{
struct hci_dev * hdev ;
struct ti_st * hst = dev_get_drvdata ( & pdev - > dev ) ;
if ( ! hst )
return - EFAULT ;
BT_DBG ( " %s " , hst - > hdev - > name ) ;
hdev = hst - > hdev ;
ti_st_close ( hdev ) ;
hci_unregister_dev ( hdev ) ;
hci_free_dev ( hdev ) ;
dev_set_drvdata ( & pdev - > dev , NULL ) ;
return 0 ;
}
static struct platform_driver btwilink_driver = {
. probe = bt_ti_probe ,
. remove = bt_ti_remove ,
. driver = {
. name = " btwilink " ,
} ,
} ;
2012-08-07 09:07:45 +04:00
module_platform_driver ( btwilink_driver ) ;
2011-02-21 07:41:16 +03:00
/* ------ Module Info ------ */
MODULE_AUTHOR ( " Raja Mani <raja_mani@ti.com> " ) ;
MODULE_DESCRIPTION ( " Bluetooth Driver for TI Shared Transport " VERSION ) ;
MODULE_VERSION ( VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;