2010-04-08 22:16:53 +04:00
/*
* Shared Transport Line discipline driver Core
* This hooks up ST KIM driver and ST LL driver
2010-10-06 20:18:14 +04:00
* Copyright ( C ) 2009 - 2010 Texas Instruments
* Author : Pavan Savoy < pavan_savoy @ ti . com >
2010-04-08 22:16:53 +04:00
*
* 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
*
*/
# define pr_fmt(fmt) "(stc): " fmt
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/tty.h>
2011-02-04 11:23:09 +03:00
# include <linux/seq_file.h>
# include <linux/skbuff.h>
2010-10-01 00:13:30 +04:00
# include <linux/ti_wilink_st.h>
2010-04-08 22:16:53 +04:00
2012-08-03 23:49:40 +04:00
extern void st_kim_recv ( void * , const unsigned char * , long ) ;
void st_int_recv ( void * , const unsigned char * , long ) ;
2010-04-08 22:16:53 +04:00
/* function pointer pointing to either,
* st_kim_recv during registration to receive fw download responses
* st_int_recv after registration to receive proto stack responses
*/
2012-08-03 23:49:40 +04:00
static void ( * st_recv ) ( void * , const unsigned char * , long ) ;
2010-04-08 22:16:53 +04:00
/********************************************************************/
2011-02-04 11:23:09 +03:00
static void add_channel_to_table ( struct st_data_s * st_gdata ,
struct st_proto_s * new_proto )
2010-04-08 22:16:53 +04:00
{
2011-02-04 11:23:09 +03:00
pr_info ( " %s: id %d \n " , __func__ , new_proto - > chnl_id ) ;
/* list now has the channel id as index itself */
st_gdata - > list [ new_proto - > chnl_id ] = new_proto ;
2011-04-08 13:57:42 +04:00
st_gdata - > is_registered [ new_proto - > chnl_id ] = true ;
2011-02-04 11:23:09 +03:00
}
static void remove_channel_from_table ( struct st_data_s * st_gdata ,
struct st_proto_s * proto )
{
pr_info ( " %s: id %d \n " , __func__ , proto - > chnl_id ) ;
2011-04-08 13:57:42 +04:00
/* st_gdata->list[proto->chnl_id] = NULL; */
st_gdata - > is_registered [ proto - > chnl_id ] = false ;
2010-04-08 22:16:53 +04:00
}
2010-07-22 14:32:06 +04:00
2011-02-04 11:23:13 +03:00
/*
* called from KIM during firmware download .
*
* This is a wrapper function to tty - > ops - > write_room .
* It returns number of free space available in
* uart tx buffer .
*/
int st_get_uart_wr_room ( struct st_data_s * st_gdata )
{
struct tty_struct * tty ;
if ( unlikely ( st_gdata = = NULL | | st_gdata - > tty = = NULL ) ) {
pr_err ( " tty unavailable to perform write " ) ;
return - 1 ;
}
tty = st_gdata - > tty ;
return tty - > ops - > write_room ( tty ) ;
}
2010-04-08 22:16:53 +04:00
/* can be called in from
* - - KIM ( during fw download )
* - - ST Core ( during st_write )
*
* This is the internal write function - a wrapper
* to tty - > ops - > write
*/
int st_int_write ( struct st_data_s * st_gdata ,
const unsigned char * data , int count )
{
struct tty_struct * tty ;
if ( unlikely ( st_gdata = = NULL | | st_gdata - > tty = = NULL ) ) {
pr_err ( " tty unavailable to perform write " ) ;
2011-02-04 11:23:11 +03:00
return - EINVAL ;
2010-04-08 22:16:53 +04:00
}
tty = st_gdata - > tty ;
# ifdef VERBOSE
2010-07-22 14:32:05 +04:00
print_hex_dump ( KERN_DEBUG , " <out< " , DUMP_PREFIX_NONE ,
16 , 1 , data , count , 0 ) ;
2010-04-08 22:16:53 +04:00
# endif
return tty - > ops - > write ( tty , data , count ) ;
}
/*
* push the skb received to relevant
* protocol stacks
*/
2012-08-03 23:49:40 +04:00
static void st_send_frame ( unsigned char chnl_id , struct st_data_s * st_gdata )
2010-04-08 22:16:53 +04:00
{
2011-02-04 11:23:12 +03:00
pr_debug ( " %s(prot:%d) " , __func__ , chnl_id ) ;
2010-04-08 22:16:53 +04:00
if ( unlikely
( st_gdata = = NULL | | st_gdata - > rx_skb = = NULL
2011-04-08 13:57:42 +04:00
| | st_gdata - > is_registered [ chnl_id ] = = false ) ) {
2011-02-04 11:23:09 +03:00
pr_err ( " chnl_id %d not registered, no data to send? " ,
chnl_id ) ;
2010-04-08 22:16:53 +04:00
kfree_skb ( st_gdata - > rx_skb ) ;
return ;
}
/* this cannot fail
* this shouldn ' t take long
* - should be just skb_queue_tail for the
* protocol stack driver
*/
2011-02-04 11:23:09 +03:00
if ( likely ( st_gdata - > list [ chnl_id ] - > recv ! = NULL ) ) {
2010-07-22 14:32:07 +04:00
if ( unlikely
2011-02-04 11:23:09 +03:00
( st_gdata - > list [ chnl_id ] - > recv
( st_gdata - > list [ chnl_id ] - > priv_data , st_gdata - > rx_skb )
2010-07-14 17:21:12 +04:00
! = 0 ) ) {
2011-02-04 11:23:09 +03:00
pr_err ( " proto stack %d's ->recv failed " , chnl_id ) ;
2010-04-08 22:16:53 +04:00
kfree_skb ( st_gdata - > rx_skb ) ;
return ;
}
} else {
2011-02-04 11:23:09 +03:00
pr_err ( " proto stack %d's ->recv null " , chnl_id ) ;
2010-04-08 22:16:53 +04:00
kfree_skb ( st_gdata - > rx_skb ) ;
}
return ;
}
2010-07-22 14:32:06 +04:00
/**
* st_reg_complete -
2010-04-08 22:16:53 +04:00
* to call registration complete callbacks
* of all protocol stack drivers
2011-12-15 20:38:20 +04:00
* This function is being called with spin lock held , protocol drivers are
* only expected to complete their waits and do nothing more than that .
2010-04-08 22:16:53 +04:00
*/
2012-08-03 23:49:40 +04:00
static void st_reg_complete ( struct st_data_s * st_gdata , char err )
2010-04-08 22:16:53 +04:00
{
unsigned char i = 0 ;
pr_info ( " %s " , __func__ ) ;
2011-02-04 11:23:09 +03:00
for ( i = 0 ; i < ST_MAX_CHANNELS ; i + + ) {
2011-04-08 13:57:42 +04:00
if ( likely ( st_gdata ! = NULL & &
st_gdata - > is_registered [ i ] = = true & &
st_gdata - > list [ i ] - > reg_complete_cb ! = NULL ) ) {
2010-07-22 14:32:07 +04:00
st_gdata - > list [ i ] - > reg_complete_cb
( st_gdata - > list [ i ] - > priv_data , err ) ;
2011-02-04 11:23:11 +03:00
pr_info ( " protocol %d's cb sent %d \n " , i , err ) ;
if ( err ) { /* cleanup registered protocol */
st_gdata - > protos_registered - - ;
2011-04-08 13:57:42 +04:00
st_gdata - > is_registered [ i ] = false ;
2011-02-04 11:23:11 +03:00
}
}
2010-04-08 22:16:53 +04:00
}
}
static inline int st_check_data_len ( struct st_data_s * st_gdata ,
2011-02-04 11:23:09 +03:00
unsigned char chnl_id , int len )
2010-04-08 22:16:53 +04:00
{
2010-10-13 00:27:38 +04:00
int room = skb_tailroom ( st_gdata - > rx_skb ) ;
2010-04-08 22:16:53 +04:00
2010-07-22 14:32:05 +04:00
pr_debug ( " len %d room %d " , len , room ) ;
2010-04-08 22:16:53 +04:00
if ( ! len ) {
/* Received packet has only packet header and
* has zero length payload . So , ask ST CORE to
* forward the packet to protocol driver ( BT / FM / GPS )
*/
2011-02-04 11:23:09 +03:00
st_send_frame ( chnl_id , st_gdata ) ;
2010-04-08 22:16:53 +04:00
} else if ( len > room ) {
/* Received packet's payload length is larger.
* We can ' t accommodate it in created skb .
*/
pr_err ( " Data length is too large len %d room %d " , len ,
room ) ;
kfree_skb ( st_gdata - > rx_skb ) ;
} else {
/* Packet header has non-zero payload length and
* we have enough space in created skb . Lets read
* payload data */
2011-02-04 11:23:09 +03:00
st_gdata - > rx_state = ST_W4_DATA ;
2010-04-08 22:16:53 +04:00
st_gdata - > rx_count = len ;
return len ;
}
/* Change ST state to continue to process next
* packet */
st_gdata - > rx_state = ST_W4_PACKET_TYPE ;
st_gdata - > rx_skb = NULL ;
st_gdata - > rx_count = 0 ;
2011-02-04 11:23:09 +03:00
st_gdata - > rx_chnl = 0 ;
2010-04-08 22:16:53 +04:00
return 0 ;
}
2010-07-22 14:32:06 +04:00
/**
* st_wakeup_ack - internal function for action when wake - up ack
* received
2010-04-08 22:16:53 +04:00
*/
static inline void st_wakeup_ack ( struct st_data_s * st_gdata ,
unsigned char cmd )
{
2010-10-13 00:27:38 +04:00
struct sk_buff * waiting_skb ;
2010-04-08 22:16:53 +04:00
unsigned long flags = 0 ;
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
/* de-Q from waitQ and Q in txQ now that the
* chip is awake
*/
while ( ( waiting_skb = skb_dequeue ( & st_gdata - > tx_waitq ) ) )
skb_queue_tail ( & st_gdata - > txq , waiting_skb ) ;
/* state forwarded to ST LL */
st_ll_sleep_state ( st_gdata , ( unsigned long ) cmd ) ;
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
/* wake up to send the recently copied skbs from waitQ */
st_tx_wakeup ( st_gdata ) ;
}
2010-07-22 14:32:06 +04:00
/**
* st_int_recv - ST ' s internal receive function .
* Decodes received RAW data and forwards to corresponding
* client drivers ( Bluetooth , FM , GPS . . etc ) .
* This can receive various types of packets ,
* HCI - Events , ACL , SCO , 4 types of HCI - LL PM packets
* CH - 8 packets from FM , CH - 9 packets from GPS cores .
2010-04-08 22:16:53 +04:00
*/
void st_int_recv ( void * disc_data ,
const unsigned char * data , long count )
{
2010-10-13 00:27:38 +04:00
char * ptr ;
2011-02-04 11:23:09 +03:00
struct st_proto_s * proto ;
unsigned short payload_len = 0 ;
2013-01-10 12:27:29 +04:00
int len = 0 ;
unsigned char type = 0 ;
2011-02-04 11:23:09 +03:00
unsigned char * plen ;
2010-04-08 22:16:53 +04:00
struct st_data_s * st_gdata = ( struct st_data_s * ) disc_data ;
2011-02-04 11:23:14 +03:00
unsigned long flags ;
2010-04-08 22:16:53 +04:00
ptr = ( char * ) data ;
/* tty_receive sent null ? */
if ( unlikely ( ptr = = NULL ) | | ( st_gdata = = NULL ) ) {
pr_err ( " received null from TTY " ) ;
return ;
}
2011-02-04 11:23:12 +03:00
pr_debug ( " count %ld rx_state %ld "
2010-04-08 22:16:53 +04:00
" rx_count %ld " , count , st_gdata - > rx_state ,
st_gdata - > rx_count ) ;
2011-02-04 11:23:14 +03:00
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
2010-04-08 22:16:53 +04:00
/* Decode received bytes here */
while ( count ) {
if ( st_gdata - > rx_count ) {
len = min_t ( unsigned int , st_gdata - > rx_count , count ) ;
memcpy ( skb_put ( st_gdata - > rx_skb , len ) , ptr , len ) ;
st_gdata - > rx_count - = len ;
count - = len ;
ptr + = len ;
if ( st_gdata - > rx_count )
continue ;
/* Check ST RX state machine , where are we? */
switch ( st_gdata - > rx_state ) {
2011-02-04 11:23:09 +03:00
/* Waiting for complete packet ? */
case ST_W4_DATA :
2010-07-22 14:32:05 +04:00
pr_debug ( " Complete pkt received " ) ;
2010-04-08 22:16:53 +04:00
/* Ask ST CORE to forward
* the packet to protocol driver */
2011-02-04 11:23:09 +03:00
st_send_frame ( st_gdata - > rx_chnl , st_gdata ) ;
2010-04-08 22:16:53 +04:00
st_gdata - > rx_state = ST_W4_PACKET_TYPE ;
st_gdata - > rx_skb = NULL ;
continue ;
2011-02-04 11:23:09 +03:00
/* parse the header to know details */
case ST_W4_HEADER :
proto = st_gdata - > list [ st_gdata - > rx_chnl ] ;
plen =
& st_gdata - > rx_skb - > data
[ proto - > offset_len_in_hdr ] ;
2011-02-04 11:23:12 +03:00
pr_debug ( " plen pointing to %x \n " , * plen ) ;
2011-02-04 11:23:09 +03:00
if ( proto - > len_size = = 1 ) /* 1 byte len field */
payload_len = * ( unsigned char * ) plen ;
else if ( proto - > len_size = = 2 )
payload_len =
__le16_to_cpu ( * ( unsigned short * ) plen ) ;
else
pr_info ( " %s: invalid length "
" for id %d \n " ,
__func__ , proto - > chnl_id ) ;
st_check_data_len ( st_gdata , proto - > chnl_id ,
payload_len ) ;
2011-02-04 11:23:12 +03:00
pr_debug ( " off %d, pay len %d \n " ,
2011-02-04 11:23:09 +03:00
proto - > offset_len_in_hdr , payload_len ) ;
2010-04-08 22:16:53 +04:00
continue ;
} /* end of switch rx_state */
}
/* end of if rx_count */
/* Check first byte of packet and identify module
* owner ( BT / FM / GPS ) */
switch ( * ptr ) {
case LL_SLEEP_IND :
case LL_SLEEP_ACK :
case LL_WAKE_UP_IND :
2011-02-04 11:23:12 +03:00
pr_debug ( " PM packet " ) ;
2010-04-08 22:16:53 +04:00
/* this takes appropriate action based on
* sleep state received - -
*/
st_ll_sleep_state ( st_gdata , * ptr ) ;
2011-02-04 11:23:14 +03:00
/* if WAKEUP_IND collides copy from waitq to txq
* and assume chip awake
*/
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
if ( st_ll_getstate ( st_gdata ) = = ST_LL_AWAKE )
st_wakeup_ack ( st_gdata , LL_WAKE_UP_ACK ) ;
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
2010-04-08 22:16:53 +04:00
ptr + + ;
count - - ;
continue ;
case LL_WAKE_UP_ACK :
2011-02-04 11:23:12 +03:00
pr_debug ( " PM packet " ) ;
2011-02-04 11:23:14 +03:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-04-08 22:16:53 +04:00
/* wake up ack received */
st_wakeup_ack ( st_gdata , * ptr ) ;
2011-02-04 11:23:14 +03:00
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
2010-04-08 22:16:53 +04:00
ptr + + ;
count - - ;
continue ;
/* Unknow packet? */
default :
2011-02-04 11:23:09 +03:00
type = * ptr ;
2011-08-10 19:18:33 +04:00
if ( st_gdata - > list [ type ] = = NULL ) {
pr_err ( " chip/interface misbehavior dropping "
" frame starting with 0x%02x " , type ) ;
goto done ;
}
2011-02-04 11:23:09 +03:00
st_gdata - > rx_skb = alloc_skb (
st_gdata - > list [ type ] - > max_frame_size ,
GFP_ATOMIC ) ;
2012-07-31 01:40:06 +04:00
if ( st_gdata - > rx_skb = = NULL ) {
pr_err ( " out of memory: dropping \n " ) ;
goto done ;
}
2011-02-04 11:23:09 +03:00
skb_reserve ( st_gdata - > rx_skb ,
st_gdata - > list [ type ] - > reserve ) ;
/* next 2 required for BT only */
st_gdata - > rx_skb - > cb [ 0 ] = type ; /*pkt_type*/
st_gdata - > rx_skb - > cb [ 1 ] = 0 ; /*incoming*/
st_gdata - > rx_chnl = * ptr ;
st_gdata - > rx_state = ST_W4_HEADER ;
st_gdata - > rx_count = st_gdata - > list [ type ] - > hdr_len ;
2011-02-04 11:23:12 +03:00
pr_debug ( " rx_count %ld \n " , st_gdata - > rx_count ) ;
2010-04-08 22:16:53 +04:00
} ;
ptr + + ;
count - - ;
}
2011-08-10 19:18:33 +04:00
done :
2011-02-04 11:23:14 +03:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-07-22 14:32:05 +04:00
pr_debug ( " done %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
return ;
}
2010-07-22 14:32:06 +04:00
/**
* st_int_dequeue - internal de - Q function .
* If the previous data set was not written
* completely , return that skb which has the pending data .
* In normal cases , return top of txq .
2010-04-08 22:16:53 +04:00
*/
2012-08-03 23:49:40 +04:00
static struct sk_buff * st_int_dequeue ( struct st_data_s * st_gdata )
2010-04-08 22:16:53 +04:00
{
struct sk_buff * returning_skb ;
2010-07-22 14:32:05 +04:00
pr_debug ( " %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
if ( st_gdata - > tx_skb ! = NULL ) {
returning_skb = st_gdata - > tx_skb ;
st_gdata - > tx_skb = NULL ;
return returning_skb ;
}
return skb_dequeue ( & st_gdata - > txq ) ;
}
2010-07-22 14:32:06 +04:00
/**
* st_int_enqueue - internal Q - ing function .
* Will either Q the skb to txq or the tx_waitq
* depending on the ST LL state .
* If the chip is asleep , then Q it onto waitq and
* wakeup the chip .
* txq and waitq needs protection since the other contexts
* may be sending data , waking up chip .
2010-04-08 22:16:53 +04:00
*/
2012-08-03 23:49:40 +04:00
static void st_int_enqueue ( struct st_data_s * st_gdata , struct sk_buff * skb )
2010-04-08 22:16:53 +04:00
{
unsigned long flags = 0 ;
2010-07-22 14:32:05 +04:00
pr_debug ( " %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
switch ( st_ll_getstate ( st_gdata ) ) {
case ST_LL_AWAKE :
2011-02-04 11:23:12 +03:00
pr_debug ( " ST LL is AWAKE, sending normally " ) ;
2010-04-08 22:16:53 +04:00
skb_queue_tail ( & st_gdata - > txq , skb ) ;
break ;
case ST_LL_ASLEEP_TO_AWAKE :
skb_queue_tail ( & st_gdata - > tx_waitq , skb ) ;
break ;
2010-07-22 14:32:06 +04:00
case ST_LL_AWAKE_TO_ASLEEP :
2010-04-08 22:16:53 +04:00
pr_err ( " ST LL is illegal state(%ld), "
" purging received skb. " , st_ll_getstate ( st_gdata ) ) ;
kfree_skb ( skb ) ;
break ;
case ST_LL_ASLEEP :
skb_queue_tail ( & st_gdata - > tx_waitq , skb ) ;
st_ll_wakeup ( st_gdata ) ;
break ;
default :
pr_err ( " ST LL is illegal state(%ld), "
" purging received skb. " , st_ll_getstate ( st_gdata ) ) ;
kfree_skb ( skb ) ;
break ;
}
2010-07-22 14:32:06 +04:00
2010-04-08 22:16:53 +04:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-07-22 14:32:05 +04:00
pr_debug ( " done %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
return ;
}
/*
* internal wakeup function
* called from either
* - TTY layer when write ' s finished
* - st_write ( in context of the protocol stack )
*/
void st_tx_wakeup ( struct st_data_s * st_data )
{
struct sk_buff * skb ;
unsigned long flags ; /* for irq save flags */
2010-07-22 14:32:05 +04:00
pr_debug ( " %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
/* check for sending & set flag sending here */
if ( test_and_set_bit ( ST_TX_SENDING , & st_data - > tx_state ) ) {
2011-02-04 11:23:12 +03:00
pr_debug ( " ST already sending " ) ;
2010-04-08 22:16:53 +04:00
/* keep sending */
set_bit ( ST_TX_WAKEUP , & st_data - > tx_state ) ;
return ;
/* TX_WAKEUP will be checked in another
* context
*/
}
do { /* come back if st_tx_wakeup is set */
/* woke-up to write */
clear_bit ( ST_TX_WAKEUP , & st_data - > tx_state ) ;
while ( ( skb = st_int_dequeue ( st_data ) ) ) {
int len ;
spin_lock_irqsave ( & st_data - > lock , flags ) ;
/* enable wake-up from TTY */
set_bit ( TTY_DO_WRITE_WAKEUP , & st_data - > tty - > flags ) ;
len = st_int_write ( st_data , skb - > data , skb - > len ) ;
skb_pull ( skb , len ) ;
/* if skb->len = len as expected, skb->len=0 */
if ( skb - > len ) {
/* would be the next skb to be sent */
st_data - > tx_skb = skb ;
spin_unlock_irqrestore ( & st_data - > lock , flags ) ;
break ;
}
kfree_skb ( skb ) ;
spin_unlock_irqrestore ( & st_data - > lock , flags ) ;
}
/* if wake-up is set in another context- restart sending */
} while ( test_bit ( ST_TX_WAKEUP , & st_data - > tx_state ) ) ;
/* clear flag sending */
clear_bit ( ST_TX_SENDING , & st_data - > tx_state ) ;
}
/********************************************************************/
/* functions called from ST KIM
*/
2010-07-28 11:25:59 +04:00
void kim_st_list_protocols ( struct st_data_s * st_gdata , void * buf )
2010-04-08 22:16:53 +04:00
{
2010-07-28 11:25:59 +04:00
seq_printf ( buf , " [%d] \n BT=%c \n FM=%c \n GPS=%c \n " ,
2010-06-09 16:20:40 +04:00
st_gdata - > protos_registered ,
2011-04-08 13:57:42 +04:00
st_gdata - > is_registered [ 0x04 ] = = true ? ' R ' : ' U ' ,
st_gdata - > is_registered [ 0x08 ] = = true ? ' R ' : ' U ' ,
st_gdata - > is_registered [ 0x09 ] = = true ? ' R ' : ' U ' ) ;
2010-04-08 22:16:53 +04:00
}
/********************************************************************/
/*
* functions called from protocol stack drivers
* to be EXPORT - ed
*/
long st_register ( struct st_proto_s * new_proto )
{
struct st_data_s * st_gdata ;
2010-07-14 17:21:12 +04:00
long err = 0 ;
2010-04-08 22:16:53 +04:00
unsigned long flags = 0 ;
2010-08-19 22:08:51 +04:00
st_kim_ref ( & st_gdata , 0 ) ;
2010-04-08 22:16:53 +04:00
if ( st_gdata = = NULL | | new_proto = = NULL | | new_proto - > recv = = NULL
| | new_proto - > reg_complete_cb = = NULL ) {
pr_err ( " gdata/new_proto/recv or reg_complete_cb not ready " ) ;
2011-02-04 11:23:11 +03:00
return - EINVAL ;
2010-04-08 22:16:53 +04:00
}
2011-02-04 11:23:09 +03:00
if ( new_proto - > chnl_id > = ST_MAX_CHANNELS ) {
pr_err ( " chnl_id %d not supported " , new_proto - > chnl_id ) ;
2010-07-14 17:21:12 +04:00
return - EPROTONOSUPPORT ;
2010-04-08 22:16:53 +04:00
}
2011-04-08 13:57:42 +04:00
if ( st_gdata - > is_registered [ new_proto - > chnl_id ] = = true ) {
2011-02-04 11:23:09 +03:00
pr_err ( " chnl_id %d already registered " , new_proto - > chnl_id ) ;
2010-07-14 17:21:12 +04:00
return - EALREADY ;
2010-04-08 22:16:53 +04:00
}
/* can be from process context only */
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
if ( test_bit ( ST_REG_IN_PROGRESS , & st_gdata - > st_state ) ) {
2011-02-04 11:23:09 +03:00
pr_info ( " ST_REG_IN_PROGRESS:%d " , new_proto - > chnl_id ) ;
2010-04-08 22:16:53 +04:00
/* fw download in progress */
2011-02-04 11:23:09 +03:00
add_channel_to_table ( st_gdata , new_proto ) ;
2010-06-09 16:20:40 +04:00
st_gdata - > protos_registered + + ;
2010-04-08 22:16:53 +04:00
new_proto - > write = st_write ;
set_bit ( ST_REG_PENDING , & st_gdata - > st_state ) ;
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-07-14 17:21:12 +04:00
return - EINPROGRESS ;
2010-04-08 22:16:53 +04:00
} else if ( st_gdata - > protos_registered = = ST_EMPTY ) {
2011-02-04 11:23:09 +03:00
pr_info ( " chnl_id list empty :%d " , new_proto - > chnl_id ) ;
2010-04-08 22:16:53 +04:00
set_bit ( ST_REG_IN_PROGRESS , & st_gdata - > st_state ) ;
st_recv = st_kim_recv ;
2011-12-15 20:38:20 +04:00
/* enable the ST LL - to set default chip state */
st_ll_enable ( st_gdata ) ;
2010-04-08 22:16:53 +04:00
/* release lock previously held - re-locked below */
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
/* this may take a while to complete
* since it involves BT fw download
*/
2010-07-08 17:55:17 +04:00
err = st_kim_start ( st_gdata - > kim_data ) ;
2010-07-14 17:21:12 +04:00
if ( err ! = 0 ) {
2010-04-08 22:16:53 +04:00
clear_bit ( ST_REG_IN_PROGRESS , & st_gdata - > st_state ) ;
if ( ( st_gdata - > protos_registered ! = ST_EMPTY ) & &
( test_bit ( ST_REG_PENDING , & st_gdata - > st_state ) ) ) {
pr_err ( " KIM failure complete callback " ) ;
2011-02-04 11:23:11 +03:00
st_reg_complete ( st_gdata , err ) ;
2011-12-15 20:38:20 +04:00
clear_bit ( ST_REG_PENDING , & st_gdata - > st_state ) ;
2010-04-08 22:16:53 +04:00
}
2011-02-04 11:23:11 +03:00
return - EINVAL ;
2010-04-08 22:16:53 +04:00
}
2011-12-15 20:38:20 +04:00
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
2010-04-08 22:16:53 +04:00
clear_bit ( ST_REG_IN_PROGRESS , & st_gdata - > st_state ) ;
st_recv = st_int_recv ;
/* this is where all pending registration
* are signalled to be complete by calling callback functions
*/
if ( ( st_gdata - > protos_registered ! = ST_EMPTY ) & &
( test_bit ( ST_REG_PENDING , & st_gdata - > st_state ) ) ) {
2010-07-22 14:32:05 +04:00
pr_debug ( " call reg complete callback " ) ;
2010-07-14 17:21:12 +04:00
st_reg_complete ( st_gdata , 0 ) ;
2010-04-08 22:16:53 +04:00
}
clear_bit ( ST_REG_PENDING , & st_gdata - > st_state ) ;
/* check for already registered once more,
* since the above check is old
*/
2011-04-08 13:57:42 +04:00
if ( st_gdata - > is_registered [ new_proto - > chnl_id ] = = true ) {
2010-04-08 22:16:53 +04:00
pr_err ( " proto %d already registered " ,
2011-02-04 11:23:09 +03:00
new_proto - > chnl_id ) ;
2011-12-15 20:38:20 +04:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-07-14 17:21:12 +04:00
return - EALREADY ;
2010-04-08 22:16:53 +04:00
}
2011-02-04 11:23:09 +03:00
add_channel_to_table ( st_gdata , new_proto ) ;
2010-06-09 16:20:40 +04:00
st_gdata - > protos_registered + + ;
2010-04-08 22:16:53 +04:00
new_proto - > write = st_write ;
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
return err ;
}
/* if fw is already downloaded & new stack registers protocol */
else {
2011-02-04 11:23:09 +03:00
add_channel_to_table ( st_gdata , new_proto ) ;
2010-06-09 16:20:40 +04:00
st_gdata - > protos_registered + + ;
2010-04-08 22:16:53 +04:00
new_proto - > write = st_write ;
/* lock already held before entering else */
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
return err ;
}
2011-02-04 11:23:09 +03:00
pr_debug ( " done %s(%d) " , __func__ , new_proto - > chnl_id ) ;
2010-04-08 22:16:53 +04:00
}
EXPORT_SYMBOL_GPL ( st_register ) ;
/* to unregister a protocol -
* to be called from protocol stack driver
*/
2011-02-04 11:23:09 +03:00
long st_unregister ( struct st_proto_s * proto )
2010-04-08 22:16:53 +04:00
{
2010-07-14 17:21:12 +04:00
long err = 0 ;
2010-04-08 22:16:53 +04:00
unsigned long flags = 0 ;
struct st_data_s * st_gdata ;
2011-02-04 11:23:09 +03:00
pr_debug ( " %s: %d " , __func__ , proto - > chnl_id ) ;
2010-04-08 22:16:53 +04:00
2010-08-19 22:08:51 +04:00
st_kim_ref ( & st_gdata , 0 ) ;
2011-05-24 00:45:32 +04:00
if ( ! st_gdata | | proto - > chnl_id > = ST_MAX_CHANNELS ) {
2011-02-04 11:23:09 +03:00
pr_err ( " chnl_id %d not supported " , proto - > chnl_id ) ;
2010-07-14 17:21:12 +04:00
return - EPROTONOSUPPORT ;
2010-04-08 22:16:53 +04:00
}
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
2011-12-15 20:38:20 +04:00
if ( st_gdata - > is_registered [ proto - > chnl_id ] = = false ) {
2011-02-04 11:23:09 +03:00
pr_err ( " chnl_id %d not registered " , proto - > chnl_id ) ;
2010-04-08 22:16:53 +04:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-07-14 17:21:12 +04:00
return - EPROTONOSUPPORT ;
2010-04-08 22:16:53 +04:00
}
st_gdata - > protos_registered - - ;
2011-02-04 11:23:09 +03:00
remove_channel_from_table ( st_gdata , proto ) ;
2010-04-08 22:16:53 +04:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2011-12-15 20:38:20 +04:00
/* paranoid check */
if ( st_gdata - > protos_registered < ST_EMPTY )
st_gdata - > protos_registered = ST_EMPTY ;
2010-04-08 22:16:53 +04:00
if ( ( st_gdata - > protos_registered = = ST_EMPTY ) & &
( ! test_bit ( ST_REG_PENDING , & st_gdata - > st_state ) ) ) {
2011-02-04 11:23:09 +03:00
pr_info ( " all chnl_ids unregistered " ) ;
2010-04-08 22:16:53 +04:00
/* stop traffic on tty */
if ( st_gdata - > tty ) {
tty_ldisc_flush ( st_gdata - > tty ) ;
stop_tty ( st_gdata - > tty ) ;
}
2011-02-04 11:23:09 +03:00
/* all chnl_ids now unregistered */
2010-07-08 17:55:17 +04:00
st_kim_stop ( st_gdata - > kim_data ) ;
2010-04-08 22:16:53 +04:00
/* disable ST LL */
st_ll_disable ( st_gdata ) ;
}
return err ;
}
/*
* called in protocol stack drivers
* via the write function pointer
*/
long st_write ( struct sk_buff * skb )
{
struct st_data_s * st_gdata ;
long len ;
2010-08-19 22:08:51 +04:00
st_kim_ref ( & st_gdata , 0 ) ;
2010-04-08 22:16:53 +04:00
if ( unlikely ( skb = = NULL | | st_gdata = = NULL
| | st_gdata - > tty = = NULL ) ) {
pr_err ( " data/tty unavailable to perform write " ) ;
2011-02-04 11:23:11 +03:00
return - EINVAL ;
2010-04-08 22:16:53 +04:00
}
2011-03-02 12:59:56 +03:00
2010-07-22 14:32:05 +04:00
pr_debug ( " %d to be written " , skb - > len ) ;
2010-04-08 22:16:53 +04:00
len = skb - > len ;
/* st_ll to decide where to enqueue the skb */
st_int_enqueue ( st_gdata , skb ) ;
/* wake up */
st_tx_wakeup ( st_gdata ) ;
/* return number of bytes written */
return len ;
}
/* for protocols making use of shared transport */
EXPORT_SYMBOL_GPL ( st_unregister ) ;
/********************************************************************/
/*
* functions called from TTY layer
*/
static int st_tty_open ( struct tty_struct * tty )
{
2010-07-14 17:21:12 +04:00
int err = 0 ;
2010-04-08 22:16:53 +04:00
struct st_data_s * st_gdata ;
pr_info ( " %s " , __func__ ) ;
2010-08-19 22:08:51 +04:00
st_kim_ref ( & st_gdata , 0 ) ;
2010-04-08 22:16:53 +04:00
st_gdata - > tty = tty ;
tty - > disc_data = st_gdata ;
/* don't do an wakeup for now */
clear_bit ( TTY_DO_WRITE_WAKEUP , & tty - > flags ) ;
/* mem already allocated
*/
tty - > receive_room = 65536 ;
/* Flush any pending characters in the driver and discipline. */
tty_ldisc_flush ( tty ) ;
tty_driver_flush_buffer ( tty ) ;
/*
* signal to UIM via KIM that -
* installation of N_TI_WL ldisc is complete
*/
2010-07-08 17:55:17 +04:00
st_kim_complete ( st_gdata - > kim_data ) ;
2010-07-22 14:32:05 +04:00
pr_debug ( " done %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
return err ;
}
static void st_tty_close ( struct tty_struct * tty )
{
2011-02-04 11:23:09 +03:00
unsigned char i = ST_MAX_CHANNELS ;
2010-04-08 22:16:53 +04:00
unsigned long flags = 0 ;
struct st_data_s * st_gdata = tty - > disc_data ;
pr_info ( " %s " , __func__ ) ;
/* TODO:
* if a protocol has been registered & line discipline
* un - installed for some reason - what should be done ?
*/
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
2011-02-04 11:23:09 +03:00
for ( i = ST_BT ; i < ST_MAX_CHANNELS ; i + + ) {
2011-08-10 19:18:30 +04:00
if ( st_gdata - > is_registered [ i ] = = true )
2010-04-08 22:16:53 +04:00
pr_err ( " %d not un-registered " , i ) ;
st_gdata - > list [ i ] = NULL ;
2011-08-10 19:18:37 +04:00
st_gdata - > is_registered [ i ] = false ;
2010-04-08 22:16:53 +04:00
}
2010-07-22 14:32:07 +04:00
st_gdata - > protos_registered = 0 ;
2010-04-08 22:16:53 +04:00
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
/*
* signal to UIM via KIM that -
* N_TI_WL ldisc is un - installed
*/
2010-07-08 17:55:17 +04:00
st_kim_complete ( st_gdata - > kim_data ) ;
2010-04-08 22:16:53 +04:00
st_gdata - > tty = NULL ;
/* Flush any pending characters in the driver and discipline. */
tty_ldisc_flush ( tty ) ;
tty_driver_flush_buffer ( tty ) ;
spin_lock_irqsave ( & st_gdata - > lock , flags ) ;
/* empty out txq and tx_waitq */
skb_queue_purge ( & st_gdata - > txq ) ;
skb_queue_purge ( & st_gdata - > tx_waitq ) ;
/* reset the TTY Rx states of ST */
st_gdata - > rx_count = 0 ;
st_gdata - > rx_state = ST_W4_PACKET_TYPE ;
kfree_skb ( st_gdata - > rx_skb ) ;
st_gdata - > rx_skb = NULL ;
spin_unlock_irqrestore ( & st_gdata - > lock , flags ) ;
2010-07-22 14:32:05 +04:00
pr_debug ( " %s: done " , __func__ ) ;
2010-04-08 22:16:53 +04:00
}
2011-06-04 01:33:24 +04:00
static void st_tty_receive ( struct tty_struct * tty , const unsigned char * data ,
char * tty_flags , int count )
2010-04-08 22:16:53 +04:00
{
# ifdef VERBOSE
2010-07-22 14:32:05 +04:00
print_hex_dump ( KERN_DEBUG , " >in> " , DUMP_PREFIX_NONE ,
16 , 1 , data , count , 0 ) ;
2010-04-08 22:16:53 +04:00
# endif
/*
* if fw download is in progress then route incoming data
* to KIM for validation
*/
st_recv ( tty - > disc_data , data , count ) ;
2010-07-22 14:32:05 +04:00
pr_debug ( " done %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
}
/* wake-up function called in from the TTY layer
* inside the internal wakeup function will be called
*/
static void st_tty_wakeup ( struct tty_struct * tty )
{
struct st_data_s * st_gdata = tty - > disc_data ;
2010-07-22 14:32:05 +04:00
pr_debug ( " %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
/* don't do an wakeup for now */
clear_bit ( TTY_DO_WRITE_WAKEUP , & tty - > flags ) ;
/* call our internal wakeup */
st_tx_wakeup ( ( void * ) st_gdata ) ;
}
static void st_tty_flush_buffer ( struct tty_struct * tty )
{
struct st_data_s * st_gdata = tty - > disc_data ;
2010-07-22 14:32:05 +04:00
pr_debug ( " %s " , __func__ ) ;
2010-04-08 22:16:53 +04:00
kfree_skb ( st_gdata - > tx_skb ) ;
st_gdata - > tx_skb = NULL ;
tty - > ops - > flush_buffer ( tty ) ;
return ;
}
2010-10-13 00:27:38 +04:00
static struct tty_ldisc_ops st_ldisc_ops = {
. magic = TTY_LDISC_MAGIC ,
. name = " n_st " ,
. open = st_tty_open ,
. close = st_tty_close ,
. receive_buf = st_tty_receive ,
. write_wakeup = st_tty_wakeup ,
. flush_buffer = st_tty_flush_buffer ,
. owner = THIS_MODULE
} ;
2010-04-08 22:16:53 +04:00
/********************************************************************/
int st_core_init ( struct st_data_s * * core_data )
{
struct st_data_s * st_gdata ;
long err ;
2010-10-13 00:27:38 +04:00
err = tty_register_ldisc ( N_TI_WL , & st_ldisc_ops ) ;
2010-04-08 22:16:53 +04:00
if ( err ) {
pr_err ( " error registering %d line discipline %ld " ,
N_TI_WL , err ) ;
return err ;
}
2010-07-22 14:32:05 +04:00
pr_debug ( " registered n_shared line discipline " ) ;
2010-04-08 22:16:53 +04:00
st_gdata = kzalloc ( sizeof ( struct st_data_s ) , GFP_KERNEL ) ;
if ( ! st_gdata ) {
pr_err ( " memory allocation failed " ) ;
err = tty_unregister_ldisc ( N_TI_WL ) ;
if ( err )
pr_err ( " unable to un-register ldisc %ld " , err ) ;
err = - ENOMEM ;
return err ;
}
/* Initialize ST TxQ and Tx waitQ queue head. All BT/FM/GPS module skb's
* will be pushed in this queue for actual transmission .
*/
skb_queue_head_init ( & st_gdata - > txq ) ;
skb_queue_head_init ( & st_gdata - > tx_waitq ) ;
/* Locking used in st_int_enqueue() to avoid multiple execution */
spin_lock_init ( & st_gdata - > lock ) ;
err = st_ll_init ( st_gdata ) ;
if ( err ) {
pr_err ( " error during st_ll initialization(%ld) " , err ) ;
kfree ( st_gdata ) ;
err = tty_unregister_ldisc ( N_TI_WL ) ;
if ( err )
pr_err ( " unable to un-register ldisc " ) ;
2011-02-04 11:23:11 +03:00
return err ;
2010-04-08 22:16:53 +04:00
}
* core_data = st_gdata ;
return 0 ;
}
void st_core_exit ( struct st_data_s * st_gdata )
{
long err ;
/* internal module cleanup */
err = st_ll_deinit ( st_gdata ) ;
if ( err )
pr_err ( " error during deinit of ST LL %ld " , err ) ;
2010-10-13 00:27:38 +04:00
2010-04-08 22:16:53 +04:00
if ( st_gdata ! = NULL ) {
/* Free ST Tx Qs and skbs */
skb_queue_purge ( & st_gdata - > txq ) ;
skb_queue_purge ( & st_gdata - > tx_waitq ) ;
kfree_skb ( st_gdata - > rx_skb ) ;
kfree_skb ( st_gdata - > tx_skb ) ;
/* TTY ldisc cleanup */
err = tty_unregister_ldisc ( N_TI_WL ) ;
if ( err )
pr_err ( " unable to un-register ldisc %ld " , err ) ;
/* free the global data pointer */
kfree ( st_gdata ) ;
}
}