diff options
Diffstat (limited to 'drivers/misc/ti-st/st_core.c')
| -rw-r--r-- | drivers/misc/ti-st/st_core.c | 468 | 
1 files changed, 187 insertions, 281 deletions
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index f9aad06d1ae..1972d57aadb 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -22,37 +22,56 @@  #define pr_fmt(fmt)	"(stc): " fmt  #include <linux/module.h>  #include <linux/kernel.h> -#include <linux/init.h>  #include <linux/tty.h> -/* understand BT, FM and GPS for now */ -#include <net/bluetooth/bluetooth.h> -#include <net/bluetooth/hci_core.h> -#include <net/bluetooth/hci.h> +#include <linux/seq_file.h> +#include <linux/skbuff.h> +  #include <linux/ti_wilink_st.h> +extern void st_kim_recv(void *, const unsigned char *, long); +void st_int_recv(void *, const unsigned char *, long);  /* 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   */ -void (*st_recv) (void*, const unsigned char*, long); +static void (*st_recv) (void *, const unsigned char *, long);  /********************************************************************/ -#if 0 -/* internal misc functions */ -bool is_protocol_list_empty(void) +static void add_channel_to_table(struct st_data_s *st_gdata, +		struct st_proto_s *new_proto)  { -	unsigned char i = 0; -	pr_debug(" %s ", __func__); -	for (i = 0; i < ST_MAX; i++) { -		if (st_gdata->list[i] != NULL) -			return ST_NOTEMPTY; -		/* not empty */ +	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; +	st_gdata->is_registered[new_proto->chnl_id] = true; +} + +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); +/*	st_gdata->list[proto->chnl_id] = NULL; */ +	st_gdata->is_registered[proto->chnl_id] = false; +} + +/* + * 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;  	} -	/* list empty */ -	return ST_EMPTY; +	tty = st_gdata->tty; +	return tty->ops->write_room(tty);  } -#endif  /* can be called in from   * -- KIM (during fw download) @@ -67,7 +86,7 @@ int st_int_write(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; +		return -EINVAL;  	}  	tty = st_gdata->tty;  #ifdef VERBOSE @@ -82,15 +101,15 @@ int st_int_write(struct st_data_s *st_gdata,   * push the skb received to relevant   * protocol stacks   */ -void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata) +static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)  { -	pr_info(" %s(prot:%d) ", __func__, protoid); +	pr_debug(" %s(prot:%d) ", __func__, chnl_id);  	if (unlikely  	    (st_gdata == NULL || st_gdata->rx_skb == NULL -	     || st_gdata->list[protoid] == NULL)) { -		pr_err("protocol %d not registered, no data to send?", -			   protoid); +	     || st_gdata->is_registered[chnl_id] == false)) { +		pr_err("chnl_id %d not registered, no data to send?", +			   chnl_id);  		kfree_skb(st_gdata->rx_skb);  		return;  	} @@ -99,17 +118,17 @@ void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)  	 * - should be just skb_queue_tail for the  	 *   protocol stack driver  	 */ -	if (likely(st_gdata->list[protoid]->recv != NULL)) { +	if (likely(st_gdata->list[chnl_id]->recv != NULL)) {  		if (unlikely -			(st_gdata->list[protoid]->recv -			(st_gdata->list[protoid]->priv_data, st_gdata->rx_skb) +			(st_gdata->list[chnl_id]->recv +			(st_gdata->list[chnl_id]->priv_data, st_gdata->rx_skb)  			     != 0)) { -			pr_err(" proto stack %d's ->recv failed", protoid); +			pr_err(" proto stack %d's ->recv failed", chnl_id);  			kfree_skb(st_gdata->rx_skb);  			return;  		}  	} else { -		pr_err(" proto stack %d's ->recv null", protoid); +		pr_err(" proto stack %d's ->recv null", chnl_id);  		kfree_skb(st_gdata->rx_skb);  	}  	return; @@ -119,21 +138,30 @@ void st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)   * st_reg_complete -   * to call registration complete callbacks   * of all protocol stack drivers + * This function is being called with spin lock held, protocol drivers are + * only expected to complete their waits and do nothing more than that.   */ -void st_reg_complete(struct st_data_s *st_gdata, char err) +static void st_reg_complete(struct st_data_s *st_gdata, char err)  {  	unsigned char i = 0;  	pr_info(" %s ", __func__); -	for (i = 0; i < ST_MAX; i++) { -		if (likely(st_gdata != NULL && st_gdata->list[i] != NULL && -			   st_gdata->list[i]->reg_complete_cb != NULL)) +	for (i = 0; i < ST_MAX_CHANNELS; i++) { +		if (likely(st_gdata != NULL && +			st_gdata->is_registered[i] == true && +				st_gdata->list[i]->reg_complete_cb != NULL)) {  			st_gdata->list[i]->reg_complete_cb  				(st_gdata->list[i]->priv_data, err); +			pr_info("protocol %d's cb sent %d\n", i, err); +			if (err) { /* cleanup registered protocol */ +				st_gdata->protos_registered--; +				st_gdata->is_registered[i] = false; +			} +		}  	}  }  static inline int st_check_data_len(struct st_data_s *st_gdata, -	int protoid, int len) +	unsigned char chnl_id, int len)  {  	int room = skb_tailroom(st_gdata->rx_skb); @@ -144,7 +172,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,  		 * has zero length payload. So, ask ST CORE to  		 * forward the packet to protocol driver (BT/FM/GPS)  		 */ -		st_send_frame(protoid, st_gdata); +		st_send_frame(chnl_id, st_gdata);  	} else if (len > room) {  		/* Received packet's payload length is larger. @@ -157,7 +185,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,  		/* Packet header has non-zero payload length and  		 * we have enough space in created skb. Lets read  		 * payload data */ -		st_gdata->rx_state = ST_BT_W4_DATA; +		st_gdata->rx_state = ST_W4_DATA;  		st_gdata->rx_count = len;  		return len;  	} @@ -167,6 +195,7 @@ static inline int st_check_data_len(struct st_data_s *st_gdata,  	st_gdata->rx_state = ST_W4_PACKET_TYPE;  	st_gdata->rx_skb = NULL;  	st_gdata->rx_count = 0; +	st_gdata->rx_chnl = 0;  	return 0;  } @@ -208,14 +237,13 @@ void st_int_recv(void *disc_data,  	const unsigned char *data, long count)  {  	char *ptr; -	struct hci_event_hdr *eh; -	struct hci_acl_hdr *ah; -	struct hci_sco_hdr *sh; -	struct fm_event_hdr *fm; -	struct gps_event_hdr *gps; -	int len = 0, type = 0, dlen = 0; -	static enum proto_type protoid = ST_MAX; +	struct st_proto_s *proto; +	unsigned short payload_len = 0; +	int len = 0; +	unsigned char type = 0; +	unsigned char *plen;  	struct st_data_s *st_gdata = (struct st_data_s *)disc_data; +	unsigned long flags;  	ptr = (char *)data;  	/* tty_receive sent null ? */ @@ -224,10 +252,11 @@ void st_int_recv(void *disc_data,  		return;  	} -	pr_info("count %ld rx_state %ld" +	pr_debug("count %ld rx_state %ld"  		   "rx_count %ld", count, st_gdata->rx_state,  		   st_gdata->rx_count); +	spin_lock_irqsave(&st_gdata->lock, flags);  	/* Decode received bytes here */  	while (count) {  		if (st_gdata->rx_count) { @@ -242,64 +271,36 @@ void st_int_recv(void *disc_data,  			/* Check ST RX state machine , where are we? */  			switch (st_gdata->rx_state) { - -				/* Waiting for complete packet ? */ -			case ST_BT_W4_DATA: +			/* Waiting for complete packet ? */ +			case ST_W4_DATA:  				pr_debug("Complete pkt received"); -  				/* Ask ST CORE to forward  				 * the packet to protocol driver */ -				st_send_frame(protoid, st_gdata); +				st_send_frame(st_gdata->rx_chnl, st_gdata);  				st_gdata->rx_state = ST_W4_PACKET_TYPE;  				st_gdata->rx_skb = NULL; -				protoid = ST_MAX;	/* is this required ? */  				continue; - -				/* Waiting for Bluetooth event header ? */ -			case ST_BT_W4_EVENT_HDR: -				eh = (struct hci_event_hdr *)st_gdata->rx_skb-> -				    data; - -				pr_debug("Event header: evt 0x%2.2x" -					   "plen %d", eh->evt, eh->plen); - -				st_check_data_len(st_gdata, protoid, eh->plen); -				continue; - -				/* Waiting for Bluetooth acl header ? */ -			case ST_BT_W4_ACL_HDR: -				ah = (struct hci_acl_hdr *)st_gdata->rx_skb-> -				    data; -				dlen = __le16_to_cpu(ah->dlen); - -				pr_info("ACL header: dlen %d", dlen); - -				st_check_data_len(st_gdata, protoid, dlen); -				continue; - -				/* Waiting for Bluetooth sco header ? */ -			case ST_BT_W4_SCO_HDR: -				sh = (struct hci_sco_hdr *)st_gdata->rx_skb-> -				    data; - -				pr_info("SCO header: dlen %d", sh->dlen); - -				st_check_data_len(st_gdata, protoid, sh->dlen); -				continue; -			case ST_FM_W4_EVENT_HDR: -				fm = (struct fm_event_hdr *)st_gdata->rx_skb-> -				    data; -				pr_info("FM Header: "); -				st_check_data_len(st_gdata, ST_FM, fm->plen); -				continue; -				/* TODO : Add GPS packet machine logic here */ -			case ST_GPS_W4_EVENT_HDR: -				/* [0x09 pkt hdr][R/W byte][2 byte len] */ -				gps = (struct gps_event_hdr *)st_gdata->rx_skb-> -				     data; -				pr_info("GPS Header: "); -				st_check_data_len(st_gdata, ST_GPS, gps->plen); +			/* 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]; +				pr_debug("plen pointing to %x\n", *plen); +				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); +				pr_debug("off %d, pay len %d\n", +					proto->offset_len_in_hdr, payload_len);  				continue;  			}	/* end of switch rx_state */  		} @@ -308,123 +309,68 @@ void st_int_recv(void *disc_data,  		/* Check first byte of packet and identify module  		 * owner (BT/FM/GPS) */  		switch (*ptr) { - -			/* Bluetooth event packet? */ -		case HCI_EVENT_PKT: -			pr_info("Event packet"); -			st_gdata->rx_state = ST_BT_W4_EVENT_HDR; -			st_gdata->rx_count = HCI_EVENT_HDR_SIZE; -			type = HCI_EVENT_PKT; -			protoid = ST_BT; -			break; - -			/* Bluetooth acl packet? */ -		case HCI_ACLDATA_PKT: -			pr_info("ACL packet"); -			st_gdata->rx_state = ST_BT_W4_ACL_HDR; -			st_gdata->rx_count = HCI_ACL_HDR_SIZE; -			type = HCI_ACLDATA_PKT; -			protoid = ST_BT; -			break; - -			/* Bluetooth sco packet? */ -		case HCI_SCODATA_PKT: -			pr_info("SCO packet"); -			st_gdata->rx_state = ST_BT_W4_SCO_HDR; -			st_gdata->rx_count = HCI_SCO_HDR_SIZE; -			type = HCI_SCODATA_PKT; -			protoid = ST_BT; -			break; - -			/* Channel 8(FM) packet? */ -		case ST_FM_CH8_PKT: -			pr_info("FM CH8 packet"); -			type = ST_FM_CH8_PKT; -			st_gdata->rx_state = ST_FM_W4_EVENT_HDR; -			st_gdata->rx_count = FM_EVENT_HDR_SIZE; -			protoid = ST_FM; -			break; - -			/* Channel 9(GPS) packet? */ -		case 0x9:	/*ST_LL_GPS_CH9_PKT */ -			pr_info("GPS CH9 packet"); -			type = 0x9;	/* ST_LL_GPS_CH9_PKT; */ -			protoid = ST_GPS; -			st_gdata->rx_state = ST_GPS_W4_EVENT_HDR; -			st_gdata->rx_count = 3;	/* GPS_EVENT_HDR_SIZE -1*/ -			break;  		case LL_SLEEP_IND:  		case LL_SLEEP_ACK:  		case LL_WAKE_UP_IND: -			pr_info("PM packet"); +			pr_debug("PM packet");  			/* this takes appropriate action based on  			 * sleep state received --  			 */  			st_ll_sleep_state(st_gdata, *ptr); +			/* 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); +  			ptr++;  			count--;  			continue;  		case LL_WAKE_UP_ACK: -			pr_info("PM packet"); +			pr_debug("PM packet"); + +			spin_unlock_irqrestore(&st_gdata->lock, flags);  			/* wake up ack received */  			st_wakeup_ack(st_gdata, *ptr); +			spin_lock_irqsave(&st_gdata->lock, flags); +  			ptr++;  			count--;  			continue;  			/* Unknow packet? */  		default: -			pr_err("Unknown packet type %2.2x", (__u8) *ptr); -			ptr++; -			count--; -			continue; -		}; -		ptr++; -		count--; +			type = *ptr; +			if (st_gdata->list[type] == NULL) { +				pr_err("chip/interface misbehavior dropping" +					" frame starting with 0x%02x", type); +				goto done; -		switch (protoid) { -		case ST_BT: -			/* Allocate new packet to hold received data */ -			st_gdata->rx_skb = -			    bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); -			if (!st_gdata->rx_skb) { -				pr_err("Can't allocate mem for new packet"); -				st_gdata->rx_state = ST_W4_PACKET_TYPE; -				st_gdata->rx_count = 0; -				return; -			} -			bt_cb(st_gdata->rx_skb)->pkt_type = type; -			break; -		case ST_FM:	/* for FM */ -			st_gdata->rx_skb = -			    alloc_skb(FM_MAX_FRAME_SIZE, GFP_ATOMIC); -			if (!st_gdata->rx_skb) { -				pr_err("Can't allocate mem for new packet"); -				st_gdata->rx_state = ST_W4_PACKET_TYPE; -				st_gdata->rx_count = 0; -				return;  			} -			/* place holder 0x08 */ -			skb_reserve(st_gdata->rx_skb, 1); -			st_gdata->rx_skb->cb[0] = ST_FM_CH8_PKT; -			break; -		case ST_GPS: -			/* for GPS */ -			st_gdata->rx_skb = -			    alloc_skb(100 /*GPS_MAX_FRAME_SIZE */ , GFP_ATOMIC); -			if (!st_gdata->rx_skb) { -				pr_err("Can't allocate mem for new packet"); -				st_gdata->rx_state = ST_W4_PACKET_TYPE; -				st_gdata->rx_count = 0; -				return; +			st_gdata->rx_skb = alloc_skb( +					st_gdata->list[type]->max_frame_size, +					GFP_ATOMIC); +			if (st_gdata->rx_skb == NULL) { +				pr_err("out of memory: dropping\n"); +				goto done;  			} -			/* place holder 0x09 */ -			skb_reserve(st_gdata->rx_skb, 1); -			st_gdata->rx_skb->cb[0] = 0x09;	/*ST_GPS_CH9_PKT; */ -			break; -		case ST_MAX: -			break; -		} + +			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; +			pr_debug("rx_count %ld\n", st_gdata->rx_count); +		}; +		ptr++; +		count--;  	} +done: +	spin_unlock_irqrestore(&st_gdata->lock, flags);  	pr_debug("done %s", __func__);  	return;  } @@ -435,7 +381,7 @@ void st_int_recv(void *disc_data,   *	completely, return that skb which has the pending data.   *	In normal cases, return top of txq.   */ -struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata) +static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)  {  	struct sk_buff *returning_skb; @@ -457,7 +403,7 @@ struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)   *	txq and waitq needs protection since the other contexts   *	may be sending data, waking up chip.   */ -void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb) +static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)  {  	unsigned long flags = 0; @@ -466,7 +412,7 @@ void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)  	switch (st_ll_getstate(st_gdata)) {  	case ST_LL_AWAKE: -		pr_info("ST LL is AWAKE, sending normally"); +		pr_debug("ST LL is AWAKE, sending normally");  		skb_queue_tail(&st_gdata->txq, skb);  		break;  	case ST_LL_ASLEEP_TO_AWAKE: @@ -506,7 +452,7 @@ void st_tx_wakeup(struct st_data_s *st_data)  	pr_debug("%s", __func__);  	/* check for sending & set flag sending here */  	if (test_and_set_bit(ST_TX_SENDING, &st_data->tx_state)) { -		pr_info("ST already sending"); +		pr_debug("ST already sending");  		/* keep sending */  		set_bit(ST_TX_WAKEUP, &st_data->tx_state);  		return; @@ -548,9 +494,9 @@ void kim_st_list_protocols(struct st_data_s *st_gdata, void *buf)  {  	seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n",  			st_gdata->protos_registered, -			st_gdata->list[ST_BT] != NULL ? 'R' : 'U', -			st_gdata->list[ST_FM] != NULL ? 'R' : 'U', -			st_gdata->list[ST_GPS] != NULL ? 'R' : 'U'); +			st_gdata->is_registered[0x04] == true ? 'R' : 'U', +			st_gdata->is_registered[0x08] == true ? 'R' : 'U', +			st_gdata->is_registered[0x09] == true ? 'R' : 'U');  }  /********************************************************************/ @@ -565,20 +511,19 @@ long st_register(struct st_proto_s *new_proto)  	unsigned long flags = 0;  	st_kim_ref(&st_gdata, 0); -	pr_info("%s(%d) ", __func__, new_proto->type);  	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"); -		return -1; +		return -EINVAL;  	} -	if (new_proto->type < ST_BT || new_proto->type >= ST_MAX) { -		pr_err("protocol %d not supported", new_proto->type); +	if (new_proto->chnl_id >= ST_MAX_CHANNELS) { +		pr_err("chnl_id %d not supported", new_proto->chnl_id);  		return -EPROTONOSUPPORT;  	} -	if (st_gdata->list[new_proto->type] != NULL) { -		pr_err("protocol %d already registered", new_proto->type); +	if (st_gdata->is_registered[new_proto->chnl_id] == true) { +		pr_err("chnl_id %d already registered", new_proto->chnl_id);  		return -EALREADY;  	} @@ -586,11 +531,10 @@ long st_register(struct st_proto_s *new_proto)  	spin_lock_irqsave(&st_gdata->lock, flags);  	if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) { -		pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->type); +		pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->chnl_id);  		/* fw download in progress */ -		st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); -		st_gdata->list[new_proto->type] = new_proto; +		add_channel_to_table(st_gdata, new_proto);  		st_gdata->protos_registered++;  		new_proto->write = st_write; @@ -598,15 +542,16 @@ long st_register(struct st_proto_s *new_proto)  		spin_unlock_irqrestore(&st_gdata->lock, flags);  		return -EINPROGRESS;  	} else if (st_gdata->protos_registered == ST_EMPTY) { -		pr_info(" protocol list empty :%d ", new_proto->type); +		pr_info(" chnl_id list empty :%d ", new_proto->chnl_id);  		set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);  		st_recv = st_kim_recv; +		/* enable the ST LL - to set default chip state */ +		st_ll_enable(st_gdata); +  		/* release lock previously held - re-locked below */  		spin_unlock_irqrestore(&st_gdata->lock, flags); -		/* enable the ST LL - to set default chip state */ -		st_ll_enable(st_gdata);  		/* this may take a while to complete  		 * since it involves BT fw download  		 */ @@ -616,15 +561,15 @@ long st_register(struct st_proto_s *new_proto)  			if ((st_gdata->protos_registered != ST_EMPTY) &&  			    (test_bit(ST_REG_PENDING, &st_gdata->st_state))) {  				pr_err(" KIM failure complete callback "); -				st_reg_complete(st_gdata, -1); +				spin_lock_irqsave(&st_gdata->lock, flags); +				st_reg_complete(st_gdata, err); +				spin_unlock_irqrestore(&st_gdata->lock, flags); +				clear_bit(ST_REG_PENDING, &st_gdata->st_state);  			} - -			return -1; +			return -EINVAL;  		} -		/* the protocol might require other gpios to be toggled -		 */ -		st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); +		spin_lock_irqsave(&st_gdata->lock, flags);  		clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);  		st_recv = st_int_recv; @@ -642,14 +587,14 @@ long st_register(struct st_proto_s *new_proto)  		/* check for already registered once more,  		 * since the above check is old  		 */ -		if (st_gdata->list[new_proto->type] != NULL) { +		if (st_gdata->is_registered[new_proto->chnl_id] == true) {  			pr_err(" proto %d already registered ", -				   new_proto->type); +				   new_proto->chnl_id); +			spin_unlock_irqrestore(&st_gdata->lock, flags);  			return -EALREADY;  		} -		spin_lock_irqsave(&st_gdata->lock, flags); -		st_gdata->list[new_proto->type] = new_proto; +		add_channel_to_table(st_gdata, new_proto);  		st_gdata->protos_registered++;  		new_proto->write = st_write;  		spin_unlock_irqrestore(&st_gdata->lock, flags); @@ -657,22 +602,7 @@ long st_register(struct st_proto_s *new_proto)  	}  	/* if fw is already downloaded & new stack registers protocol */  	else { -		switch (new_proto->type) { -		case ST_BT: -			/* do nothing */ -			break; -		case ST_FM: -		case ST_GPS: -			st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE); -			break; -		case ST_MAX: -		default: -			pr_err("%d protocol not supported", -				   new_proto->type); -			spin_unlock_irqrestore(&st_gdata->lock, flags); -			return -EPROTONOSUPPORT; -		} -		st_gdata->list[new_proto->type] = new_proto; +		add_channel_to_table(st_gdata, new_proto);  		st_gdata->protos_registered++;  		new_proto->write = st_write; @@ -680,48 +610,46 @@ long st_register(struct st_proto_s *new_proto)  		spin_unlock_irqrestore(&st_gdata->lock, flags);  		return err;  	} -	pr_debug("done %s(%d) ", __func__, new_proto->type); +	pr_debug("done %s(%d) ", __func__, new_proto->chnl_id);  }  EXPORT_SYMBOL_GPL(st_register);  /* to unregister a protocol -   * to be called from protocol stack driver   */ -long st_unregister(enum proto_type type) +long st_unregister(struct st_proto_s *proto)  {  	long err = 0;  	unsigned long flags = 0;  	struct st_data_s	*st_gdata; -	pr_debug("%s: %d ", __func__, type); +	pr_debug("%s: %d ", __func__, proto->chnl_id);  	st_kim_ref(&st_gdata, 0); -	if (type < ST_BT || type >= ST_MAX) { -		pr_err(" protocol %d not supported", type); +	if (!st_gdata || proto->chnl_id >= ST_MAX_CHANNELS) { +		pr_err(" chnl_id %d not supported", proto->chnl_id);  		return -EPROTONOSUPPORT;  	}  	spin_lock_irqsave(&st_gdata->lock, flags); -	if (st_gdata->list[type] == NULL) { -		pr_err(" protocol %d not registered", type); +	if (st_gdata->is_registered[proto->chnl_id] == false) { +		pr_err(" chnl_id %d not registered", proto->chnl_id);  		spin_unlock_irqrestore(&st_gdata->lock, flags);  		return -EPROTONOSUPPORT;  	}  	st_gdata->protos_registered--; -	st_gdata->list[type] = NULL; - -	/* kim ignores BT in the below function -	 * and handles the rest, BT is toggled -	 * only in kim_start and kim_stop -	 */ -	st_kim_chip_toggle(type, KIM_GPIO_INACTIVE); +	remove_channel_from_table(st_gdata, proto);  	spin_unlock_irqrestore(&st_gdata->lock, flags); +	/* paranoid check */ +	if (st_gdata->protos_registered < ST_EMPTY) +		st_gdata->protos_registered = ST_EMPTY; +  	if ((st_gdata->protos_registered == ST_EMPTY) &&  	    (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { -		pr_info(" all protocols unregistered "); +		pr_info(" all chnl_ids unregistered ");  		/* stop traffic on tty */  		if (st_gdata->tty) { @@ -729,7 +657,7 @@ long st_unregister(enum proto_type type)  			stop_tty(st_gdata->tty);  		} -		/* all protocols now unregistered */ +		/* all chnl_ids now unregistered */  		st_kim_stop(st_gdata->kim_data);  		/* disable ST LL */  		st_ll_disable(st_gdata); @@ -744,37 +672,15 @@ long st_unregister(enum proto_type type)  long st_write(struct sk_buff *skb)  {  	struct st_data_s *st_gdata; -#ifdef DEBUG -	enum proto_type protoid = ST_MAX; -#endif  	long len;  	st_kim_ref(&st_gdata, 0);  	if (unlikely(skb == NULL || st_gdata == NULL  		|| st_gdata->tty == NULL)) {  		pr_err("data/tty unavailable to perform write"); -		return -1; -	} -#ifdef DEBUG			/* open-up skb to read the 1st byte */ -	switch (skb->data[0]) { -	case HCI_COMMAND_PKT: -	case HCI_ACLDATA_PKT: -	case HCI_SCODATA_PKT: -		protoid = ST_BT; -		break; -	case ST_FM_CH8_PKT: -		protoid = ST_FM; -		break; -	case 0x09: -		protoid = ST_GPS; -		break; +		return -EINVAL;  	} -	if (unlikely(st_gdata->list[protoid] == NULL)) { -		pr_err(" protocol %d not registered, and writing? ", -			   protoid); -		return -1; -	} -#endif +  	pr_debug("%d to be written", skb->len);  	len = skb->len; @@ -824,7 +730,7 @@ static int st_tty_open(struct tty_struct *tty)  static void st_tty_close(struct tty_struct *tty)  { -	unsigned char i = ST_MAX; +	unsigned char i = ST_MAX_CHANNELS;  	unsigned long flags = 0;  	struct	st_data_s *st_gdata = tty->disc_data; @@ -835,10 +741,11 @@ static void st_tty_close(struct tty_struct *tty)  	 * un-installed for some reason - what should be done ?  	 */  	spin_lock_irqsave(&st_gdata->lock, flags); -	for (i = ST_BT; i < ST_MAX; i++) { -		if (st_gdata->list[i] != NULL) +	for (i = ST_BT; i < ST_MAX_CHANNELS; i++) { +		if (st_gdata->is_registered[i] == true)  			pr_err("%d not un-registered", i);  		st_gdata->list[i] = NULL; +		st_gdata->is_registered[i] = false;  	}  	st_gdata->protos_registered = 0;  	spin_unlock_irqrestore(&st_gdata->lock, flags); @@ -869,7 +776,6 @@ static void st_tty_close(struct tty_struct *tty)  static void st_tty_receive(struct tty_struct *tty, const unsigned char *data,  			   char *tty_flags, int count)  { -  #ifdef VERBOSE  	print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE,  		16, 1, data, count, 0); @@ -905,7 +811,7 @@ static void st_tty_flush_buffer(struct tty_struct *tty)  	kfree_skb(st_gdata->tx_skb);  	st_gdata->tx_skb = NULL; -	tty->ops->flush_buffer(tty); +	tty_driver_flush_buffer(tty);  	return;  } @@ -960,7 +866,7 @@ int st_core_init(struct st_data_s **core_data)  		err = tty_unregister_ldisc(N_TI_WL);  		if (err)  			pr_err("unable to un-register ldisc"); -		return -1; +		return err;  	}  	*core_data = st_gdata;  	return 0;  | 
