st_core.c revision dbd3a8709560365ff9b1e5eca263f608877a8a89
153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  Shared Transport Line discipline driver Core
353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *	This hooks up ST KIM driver and ST LL driver
453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  Copyright (C) 2009 Texas Instruments
553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *
653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  This program is free software; you can redistribute it and/or modify
753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  it under the terms of the GNU General Public License version 2 as
853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  published by the Free Software Foundation.
953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *
1053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  This program is distributed in the hope that it will be useful,
1153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  GNU General Public License for more details.
1453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *
1553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  You should have received a copy of the GNU General Public License
1653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  along with this program; if not, write to the Free Software
1753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *
1953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
2053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
2153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#define pr_fmt(fmt)	"(stc): " fmt
2253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <linux/module.h>
2353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <linux/kernel.h>
2453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <linux/init.h>
2553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <linux/tty.h>
2653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
2753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* understand BT, FM and GPS for now */
2853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <net/bluetooth/bluetooth.h>
2953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <net/bluetooth/hci_core.h>
3053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include <net/bluetooth/hci.h>
3153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include "fm.h"
3253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
3353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * packet formats for fm and gps
3453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * #include "gps.h"
3553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
3653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include "st_core.h"
3753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include "st_kim.h"
3853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include "st_ll.h"
3953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#include "st.h"
4053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
4153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* strings to be used for rfkill entries and by
4253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * ST Core to be used for sysfs debug entry
4353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
4453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#define PROTO_ENTRY(type, name)	name
4553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyconst unsigned char *protocol_strngs[] = {
4653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	PROTO_ENTRY(ST_BT, "Bluetooth"),
4753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	PROTO_ENTRY(ST_FM, "FM"),
4853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	PROTO_ENTRY(ST_GPS, "GPS"),
4953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy};
5053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* function pointer pointing to either,
5153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * st_kim_recv during registration to receive fw download responses
5253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * st_int_recv after registration to receive proto stack responses
5353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
5453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid (*st_recv) (void*, const unsigned char*, long);
5553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
5653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/********************************************************************/
5753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#if 0
5853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* internal misc functions */
5953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoybool is_protocol_list_empty(void)
6053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
6153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned char i = 0;
62e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug(" %s ", __func__);
6353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	for (i = 0; i < ST_MAX; i++) {
6453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (st_gdata->list[i] != NULL)
6553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			return ST_NOTEMPTY;
6653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* not empty */
6753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
6853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* list empty */
6953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return ST_EMPTY;
7053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
7153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
7236b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy
7353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* can be called in from
7453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * -- KIM (during fw download)
7553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * -- ST Core (during st_write)
7653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *
7753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  This is the internal write function - a wrapper
7853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy *  to tty->ops->write
7953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
8053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyint st_int_write(struct st_data_s *st_gdata,
8153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	const unsigned char *data, int count)
8253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
8353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct tty_struct *tty;
8453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (unlikely(st_gdata == NULL || st_gdata->tty == NULL)) {
8553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("tty unavailable to perform write");
86320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -1;
8753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
8853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty = st_gdata->tty;
8953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#ifdef VERBOSE
90e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	print_hex_dump(KERN_DEBUG, "<out<", DUMP_PREFIX_NONE,
91e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy		16, 1, data, count, 0);
9253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
9353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return tty->ops->write(tty, data, count);
9453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
9553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
9653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
9753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
9853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * push the skb received to relevant
9953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * protocol stacks
10053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
10153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid st_send_frame(enum proto_type protoid, struct st_data_s *st_gdata)
10253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
10353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	pr_info(" %s(prot:%d) ", __func__, protoid);
10453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
10553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (unlikely
10653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	    (st_gdata == NULL || st_gdata->rx_skb == NULL
10753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	     || st_gdata->list[protoid] == NULL)) {
10853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("protocol %d not registered, no data to send?",
10953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   protoid);
11053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(st_gdata->rx_skb);
11153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return;
11253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
11353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* this cannot fail
11453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * this shouldn't take long
11553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * - should be just skb_queue_tail for the
11653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 *   protocol stack driver
11753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
11853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (likely(st_gdata->list[protoid]->recv != NULL)) {
119bb8f3c061f2e7282730059ae524ff19d47d70b17Pavan Savoy		if (unlikely
120bb8f3c061f2e7282730059ae524ff19d47d70b17Pavan Savoy			(st_gdata->list[protoid]->recv
121bb8f3c061f2e7282730059ae524ff19d47d70b17Pavan Savoy			(st_gdata->list[protoid]->priv_data, st_gdata->rx_skb)
122320920cba355146258da7de80bed0069c1dff24aPavan Savoy			     != 0)) {
12353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err(" proto stack %d's ->recv failed", protoid);
12453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			kfree_skb(st_gdata->rx_skb);
12553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			return;
12653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
12753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	} else {
12853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err(" proto stack %d's ->recv null", protoid);
12953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(st_gdata->rx_skb);
13053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
13153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return;
13253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
13353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
13436b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy/**
13536b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy * st_reg_complete -
13653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * to call registration complete callbacks
13753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * of all protocol stack drivers
13853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
13953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid st_reg_complete(struct st_data_s *st_gdata, char err)
14053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
14153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned char i = 0;
14253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	pr_info(" %s ", __func__);
14353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	for (i = 0; i < ST_MAX; i++) {
14453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (likely(st_gdata != NULL && st_gdata->list[i] != NULL &&
14553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   st_gdata->list[i]->reg_complete_cb != NULL))
146bb8f3c061f2e7282730059ae524ff19d47d70b17Pavan Savoy			st_gdata->list[i]->reg_complete_cb
147bb8f3c061f2e7282730059ae524ff19d47d70b17Pavan Savoy				(st_gdata->list[i]->priv_data, err);
14853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
14953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
15053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
15153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic inline int st_check_data_len(struct st_data_s *st_gdata,
15253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	int protoid, int len)
15353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
15453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	register int room = skb_tailroom(st_gdata->rx_skb);
15553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
156e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("len %d room %d", len, room);
15753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
15853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (!len) {
15953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* Received packet has only packet header and
16053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * has zero length payload. So, ask ST CORE to
16153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * forward the packet to protocol driver (BT/FM/GPS)
16253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
16353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_send_frame(protoid, st_gdata);
16453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
16553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	} else if (len > room) {
16653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* Received packet's payload length is larger.
16753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * We can't accommodate it in created skb.
16853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
16953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("Data length is too large len %d room %d", len,
17053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   room);
17153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(st_gdata->rx_skb);
17253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	} else {
17353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* Packet header has non-zero payload length and
17453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * we have enough space in created skb. Lets read
17553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * payload data */
17653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->rx_state = ST_BT_W4_DATA;
17753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->rx_count = len;
17853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return len;
17953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
18053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
18153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* Change ST state to continue to process next
18253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * packet */
18353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->rx_state = ST_W4_PACKET_TYPE;
18453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->rx_skb = NULL;
18553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->rx_count = 0;
18653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
18753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return 0;
18853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
18953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
19036b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy/**
19136b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy * st_wakeup_ack - internal function for action when wake-up ack
19236b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	received
19353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
19453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic inline void st_wakeup_ack(struct st_data_s *st_gdata,
19553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned char cmd)
19653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
19753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	register struct sk_buff *waiting_skb;
19853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned long flags = 0;
19953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
20053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_irqsave(&st_gdata->lock, flags);
20153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* de-Q from waitQ and Q in txQ now that the
20253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * chip is awake
20353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
20453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	while ((waiting_skb = skb_dequeue(&st_gdata->tx_waitq)))
20553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		skb_queue_tail(&st_gdata->txq, waiting_skb);
20653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
20753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* state forwarded to ST LL */
20853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ll_sleep_state(st_gdata, (unsigned long)cmd);
20953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_unlock_irqrestore(&st_gdata->lock, flags);
21053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
21153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* wake up to send the recently copied skbs from waitQ */
21253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_tx_wakeup(st_gdata);
21353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
21453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
21536b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy/**
21636b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy * st_int_recv - ST's internal receive function.
21736b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	Decodes received RAW data and forwards to corresponding
21836b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	client drivers (Bluetooth,FM,GPS..etc).
21936b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	This can receive various types of packets,
22036b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	HCI-Events, ACL, SCO, 4 types of HCI-LL PM packets
22136b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	CH-8 packets from FM, CH-9 packets from GPS cores.
22253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
22353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid st_int_recv(void *disc_data,
22453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	const unsigned char *data, long count)
22553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
22653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	register char *ptr;
22753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct hci_event_hdr *eh;
22853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct hci_acl_hdr *ah;
22953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct hci_sco_hdr *sh;
23053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct fm_event_hdr *fm;
23153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct gps_event_hdr *gps;
23253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	register int len = 0, type = 0, dlen = 0;
23353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	static enum proto_type protoid = ST_MAX;
23453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
23553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
23653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	ptr = (char *)data;
23753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* tty_receive sent null ? */
23853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (unlikely(ptr == NULL) || (st_gdata == NULL)) {
23953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err(" received null from TTY ");
24053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return;
24153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
24253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
24353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	pr_info("count %ld rx_state %ld"
24453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		   "rx_count %ld", count, st_gdata->rx_state,
24553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		   st_gdata->rx_count);
24653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
24753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* Decode received bytes here */
24853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	while (count) {
24953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (st_gdata->rx_count) {
25053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			len = min_t(unsigned int, st_gdata->rx_count, count);
25153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			memcpy(skb_put(st_gdata->rx_skb, len), ptr, len);
25253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_count -= len;
25353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			count -= len;
25453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			ptr += len;
25553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
25653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			if (st_gdata->rx_count)
25753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
25853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
25953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Check ST RX state machine , where are we? */
26053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			switch (st_gdata->rx_state) {
26153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
26253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* Waiting for complete packet ? */
26353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			case ST_BT_W4_DATA:
264e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy				pr_debug("Complete pkt received");
26553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
26653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* Ask ST CORE to forward
26753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				 * the packet to protocol driver */
26853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_send_frame(protoid, st_gdata);
26953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
27053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_state = ST_W4_PACKET_TYPE;
27153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_skb = NULL;
27253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				protoid = ST_MAX;	/* is this required ? */
27353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
27453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
27553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* Waiting for Bluetooth event header ? */
27653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			case ST_BT_W4_EVENT_HDR:
27753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				eh = (struct hci_event_hdr *)st_gdata->rx_skb->
27853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				    data;
27953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
280e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy				pr_debug("Event header: evt 0x%2.2x"
28153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy					   "plen %d", eh->evt, eh->plen);
28253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
28353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_check_data_len(st_gdata, protoid, eh->plen);
28453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
28553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
28653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* Waiting for Bluetooth acl header ? */
28753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			case ST_BT_W4_ACL_HDR:
28853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				ah = (struct hci_acl_hdr *)st_gdata->rx_skb->
28953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				    data;
29053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				dlen = __le16_to_cpu(ah->dlen);
29153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
29253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_info("ACL header: dlen %d", dlen);
29353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
29453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_check_data_len(st_gdata, protoid, dlen);
29553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
29653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
29753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* Waiting for Bluetooth sco header ? */
29853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			case ST_BT_W4_SCO_HDR:
29953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				sh = (struct hci_sco_hdr *)st_gdata->rx_skb->
30053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				    data;
30153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
30253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_info("SCO header: dlen %d", sh->dlen);
30353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
30453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_check_data_len(st_gdata, protoid, sh->dlen);
30553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
30653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			case ST_FM_W4_EVENT_HDR:
30753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				fm = (struct fm_event_hdr *)st_gdata->rx_skb->
30853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				    data;
30953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_info("FM Header: ");
31053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_check_data_len(st_gdata, ST_FM, fm->plen);
31153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
31253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* TODO : Add GPS packet machine logic here */
31353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			case ST_GPS_W4_EVENT_HDR:
31453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* [0x09 pkt hdr][R/W byte][2 byte len] */
31553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				gps = (struct gps_event_hdr *)st_gdata->rx_skb->
31653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				     data;
31753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_info("GPS Header: ");
31853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_check_data_len(st_gdata, ST_GPS, gps->plen);
31953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				continue;
32053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			}	/* end of switch rx_state */
32153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
32253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
32353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* end of if rx_count */
32453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* Check first byte of packet and identify module
32553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * owner (BT/FM/GPS) */
32653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		switch (*ptr) {
32753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
32853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Bluetooth event packet? */
32953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case HCI_EVENT_PKT:
33053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("Event packet");
33153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_state = ST_BT_W4_EVENT_HDR;
33253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_count = HCI_EVENT_HDR_SIZE;
33353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			type = HCI_EVENT_PKT;
33453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			protoid = ST_BT;
33553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
33653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
33753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Bluetooth acl packet? */
33853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case HCI_ACLDATA_PKT:
33953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("ACL packet");
34053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_state = ST_BT_W4_ACL_HDR;
34153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_count = HCI_ACL_HDR_SIZE;
34253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			type = HCI_ACLDATA_PKT;
34353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			protoid = ST_BT;
34453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
34553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
34653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Bluetooth sco packet? */
34753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case HCI_SCODATA_PKT:
34853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("SCO packet");
34953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_state = ST_BT_W4_SCO_HDR;
35053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_count = HCI_SCO_HDR_SIZE;
35153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			type = HCI_SCODATA_PKT;
35253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			protoid = ST_BT;
35353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
35453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
35553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Channel 8(FM) packet? */
35653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_FM_CH8_PKT:
35753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("FM CH8 packet");
35853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			type = ST_FM_CH8_PKT;
35953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_state = ST_FM_W4_EVENT_HDR;
36053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_count = FM_EVENT_HDR_SIZE;
36153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			protoid = ST_FM;
36253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
36353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
36453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Channel 9(GPS) packet? */
36553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case 0x9:	/*ST_LL_GPS_CH9_PKT */
36653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("GPS CH9 packet");
36753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			type = 0x9;	/* ST_LL_GPS_CH9_PKT; */
36853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			protoid = ST_GPS;
36953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_state = ST_GPS_W4_EVENT_HDR;
37053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_count = 3;	/* GPS_EVENT_HDR_SIZE -1*/
37153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
37253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case LL_SLEEP_IND:
37353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case LL_SLEEP_ACK:
37453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case LL_WAKE_UP_IND:
37553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("PM packet");
37653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* this takes appropriate action based on
37753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			 * sleep state received --
37853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			 */
37953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_ll_sleep_state(st_gdata, *ptr);
38053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			ptr++;
38153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			count--;
38253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			continue;
38353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case LL_WAKE_UP_ACK:
38453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_info("PM packet");
38553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* wake up ack received */
38653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_wakeup_ack(st_gdata, *ptr);
38753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			ptr++;
38853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			count--;
38953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			continue;
39053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Unknow packet? */
39153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		default:
39253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("Unknown packet type %2.2x", (__u8) *ptr);
39353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			ptr++;
39453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			count--;
39553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			continue;
39653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		};
39753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		ptr++;
39853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		count--;
39953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
40053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		switch (protoid) {
40153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_BT:
40253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* Allocate new packet to hold received data */
40353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_skb =
40453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			    bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
40553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			if (!st_gdata->rx_skb) {
40653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_err("Can't allocate mem for new packet");
40753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_state = ST_W4_PACKET_TYPE;
40853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_count = 0;
40953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				return;
41053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			}
41153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			bt_cb(st_gdata->rx_skb)->pkt_type = type;
41253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
41353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_FM:	/* for FM */
41453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_skb =
41553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			    alloc_skb(FM_MAX_FRAME_SIZE, GFP_ATOMIC);
41653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			if (!st_gdata->rx_skb) {
41753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_err("Can't allocate mem for new packet");
41853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_state = ST_W4_PACKET_TYPE;
41953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_count = 0;
42053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				return;
42153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			}
42253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* place holder 0x08 */
42353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			skb_reserve(st_gdata->rx_skb, 1);
42453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_skb->cb[0] = ST_FM_CH8_PKT;
42553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
42653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_GPS:
42753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* for GPS */
42853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_skb =
42953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			    alloc_skb(100 /*GPS_MAX_FRAME_SIZE */ , GFP_ATOMIC);
43053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			if (!st_gdata->rx_skb) {
43153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_err("Can't allocate mem for new packet");
43253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_state = ST_W4_PACKET_TYPE;
43353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_gdata->rx_count = 0;
43453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				return;
43553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			}
43653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* place holder 0x09 */
43753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			skb_reserve(st_gdata->rx_skb, 1);
43853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_gdata->rx_skb->cb[0] = 0x09;	/*ST_GPS_CH9_PKT; */
43953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
44053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_MAX:
44153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
44253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
44353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
444e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("done %s", __func__);
44553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return;
44653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
44753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
44836b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy/**
44936b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy * st_int_dequeue - internal de-Q function.
45036b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	If the previous data set was not written
45136b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	completely, return that skb which has the pending data.
45236b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	In normal cases, return top of txq.
45353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
45453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystruct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
45553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
45653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct sk_buff *returning_skb;
45753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
458e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s", __func__);
45953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (st_gdata->tx_skb != NULL) {
46053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		returning_skb = st_gdata->tx_skb;
46153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->tx_skb = NULL;
46253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return returning_skb;
46353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
46453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return skb_dequeue(&st_gdata->txq);
46553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
46653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
46736b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy/**
46836b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy * st_int_enqueue - internal Q-ing function.
46936b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	Will either Q the skb to txq or the tx_waitq
47036b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	depending on the ST LL state.
47136b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	If the chip is asleep, then Q it onto waitq and
47236b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	wakeup the chip.
47336b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	txq and waitq needs protection since the other contexts
47436b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy *	may be sending data, waking up chip.
47553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
47653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)
47753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
47853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned long flags = 0;
47953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
480e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s", __func__);
48153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_irqsave(&st_gdata->lock, flags);
48253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
48353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	switch (st_ll_getstate(st_gdata)) {
48453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case ST_LL_AWAKE:
48553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_info("ST LL is AWAKE, sending normally");
48653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		skb_queue_tail(&st_gdata->txq, skb);
48753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
48853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case ST_LL_ASLEEP_TO_AWAKE:
48953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		skb_queue_tail(&st_gdata->tx_waitq, skb);
49053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
49136b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy	case ST_LL_AWAKE_TO_ASLEEP:
49253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("ST LL is illegal state(%ld),"
49353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   "purging received skb.", st_ll_getstate(st_gdata));
49453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(skb);
49553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
49653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case ST_LL_ASLEEP:
49753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		skb_queue_tail(&st_gdata->tx_waitq, skb);
49853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_ll_wakeup(st_gdata);
49953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
50053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	default:
50153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("ST LL is illegal state(%ld),"
50253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   "purging received skb.", st_ll_getstate(st_gdata));
50353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(skb);
50453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
50553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
50636b5aee46b5e4788758409829926c631de0a64a3Pavan Savoy
50753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_unlock_irqrestore(&st_gdata->lock, flags);
508e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("done %s", __func__);
50953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return;
51053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
51153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
51253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
51353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * internal wakeup function
51453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * called from either
51553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * - TTY layer when write's finished
51653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * - st_write (in context of the protocol stack)
51753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
51853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid st_tx_wakeup(struct st_data_s *st_data)
51953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
52053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct sk_buff *skb;
52153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned long flags;	/* for irq save flags */
522e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s", __func__);
52353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* check for sending & set flag sending here */
52453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (test_and_set_bit(ST_TX_SENDING, &st_data->tx_state)) {
52553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_info("ST already sending");
52653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* keep sending */
52753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		set_bit(ST_TX_WAKEUP, &st_data->tx_state);
52853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return;
52953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* TX_WAKEUP will be checked in another
53053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * context
53153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
53253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
53353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	do {			/* come back if st_tx_wakeup is set */
53453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* woke-up to write */
53553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		clear_bit(ST_TX_WAKEUP, &st_data->tx_state);
53653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		while ((skb = st_int_dequeue(st_data))) {
53753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			int len;
53853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			spin_lock_irqsave(&st_data->lock, flags);
53953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* enable wake-up from TTY */
54053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			set_bit(TTY_DO_WRITE_WAKEUP, &st_data->tty->flags);
54153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			len = st_int_write(st_data, skb->data, skb->len);
54253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			skb_pull(skb, len);
54353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* if skb->len = len as expected, skb->len=0 */
54453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			if (skb->len) {
54553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				/* would be the next skb to be sent */
54653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				st_data->tx_skb = skb;
54753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				spin_unlock_irqrestore(&st_data->lock, flags);
54853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				break;
54953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			}
55053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			kfree_skb(skb);
55153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			spin_unlock_irqrestore(&st_data->lock, flags);
55253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
55353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* if wake-up is set in another context- restart sending */
55453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	} while (test_bit(ST_TX_WAKEUP, &st_data->tx_state));
55553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
55653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* clear flag sending */
55753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	clear_bit(ST_TX_SENDING, &st_data->tx_state);
55853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
55953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
56053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/********************************************************************/
56153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* functions called from ST KIM
56253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy*/
563c1afac1502c4e519e6a1df08a5d9a2391d00388bPavan Savoyvoid kim_st_list_protocols(struct st_data_s *st_gdata, void *buf)
56453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
565c1afac1502c4e519e6a1df08a5d9a2391d00388bPavan Savoy	seq_printf(buf, "[%d]\nBT=%c\nFM=%c\nGPS=%c\n",
56636e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain			st_gdata->protos_registered,
56736e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain			st_gdata->list[ST_BT] != NULL ? 'R' : 'U',
56836e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain			st_gdata->list[ST_FM] != NULL ? 'R' : 'U',
56936e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain			st_gdata->list[ST_GPS] != NULL ? 'R' : 'U');
57053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
57153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
57253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/********************************************************************/
57353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
57453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * functions called from protocol stack drivers
57553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * to be EXPORT-ed
57653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
57753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoylong st_register(struct st_proto_s *new_proto)
57853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
57953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct st_data_s	*st_gdata;
580320920cba355146258da7de80bed0069c1dff24aPavan Savoy	long err = 0;
58153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned long flags = 0;
58253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
583dbd3a8709560365ff9b1e5eca263f608877a8a89Pavan Savoy	st_kim_ref(&st_gdata, 0);
58453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	pr_info("%s(%d) ", __func__, new_proto->type);
58553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (st_gdata == NULL || new_proto == NULL || new_proto->recv == NULL
58653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	    || new_proto->reg_complete_cb == NULL) {
58753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("gdata/new_proto/recv or reg_complete_cb not ready");
588320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -1;
58953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
59053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
59153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (new_proto->type < ST_BT || new_proto->type >= ST_MAX) {
59253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("protocol %d not supported", new_proto->type);
593320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -EPROTONOSUPPORT;
59453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
59553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
59653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (st_gdata->list[new_proto->type] != NULL) {
59753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("protocol %d already registered", new_proto->type);
598320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -EALREADY;
59953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
60053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
60153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* can be from process context only */
60253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_irqsave(&st_gdata->lock, flags);
60353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
60453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (test_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state)) {
60553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_info(" ST_REG_IN_PROGRESS:%d ", new_proto->type);
60653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* fw download in progress */
60753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
60853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
60953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->list[new_proto->type] = new_proto;
61036e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain		st_gdata->protos_registered++;
61153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		new_proto->write = st_write;
61253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
61353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		set_bit(ST_REG_PENDING, &st_gdata->st_state);
61453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		spin_unlock_irqrestore(&st_gdata->lock, flags);
615320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -EINPROGRESS;
61653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	} else if (st_gdata->protos_registered == ST_EMPTY) {
61753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_info(" protocol list empty :%d ", new_proto->type);
61853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
61953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_recv = st_kim_recv;
62053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
62153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* release lock previously held - re-locked below */
62253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		spin_unlock_irqrestore(&st_gdata->lock, flags);
62353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
62453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* enable the ST LL - to set default chip state */
62553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_ll_enable(st_gdata);
62653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* this may take a while to complete
62753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * since it involves BT fw download
62853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
62938d9df499dd3125465cf5aed3d3d6d5c26f0645dPavan Savoy		err = st_kim_start(st_gdata->kim_data);
630320920cba355146258da7de80bed0069c1dff24aPavan Savoy		if (err != 0) {
63153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
63253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			if ((st_gdata->protos_registered != ST_EMPTY) &&
63353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			    (test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
63453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				pr_err(" KIM failure complete callback ");
635320920cba355146258da7de80bed0069c1dff24aPavan Savoy				st_reg_complete(st_gdata, -1);
63653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			}
63753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
638320920cba355146258da7de80bed0069c1dff24aPavan Savoy			return -1;
63953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
64053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
64153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* the protocol might require other gpios to be toggled
64253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
64353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
64453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
64553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
64653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_recv = st_int_recv;
64753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
64853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* this is where all pending registration
64953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * are signalled to be complete by calling callback functions
65053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
65153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if ((st_gdata->protos_registered != ST_EMPTY) &&
65253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		    (test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
653e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy			pr_debug(" call reg complete callback ");
654320920cba355146258da7de80bed0069c1dff24aPavan Savoy			st_reg_complete(st_gdata, 0);
65553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
65653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		clear_bit(ST_REG_PENDING, &st_gdata->st_state);
65753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
65853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* check for already registered once more,
65953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 * since the above check is old
66053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		 */
66153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (st_gdata->list[new_proto->type] != NULL) {
66253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err(" proto %d already registered ",
66353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				   new_proto->type);
664320920cba355146258da7de80bed0069c1dff24aPavan Savoy			return -EALREADY;
66553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
66653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
66753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		spin_lock_irqsave(&st_gdata->lock, flags);
66853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->list[new_proto->type] = new_proto;
66936e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain		st_gdata->protos_registered++;
67053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		new_proto->write = st_write;
67153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		spin_unlock_irqrestore(&st_gdata->lock, flags);
67253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return err;
67353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
67453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* if fw is already downloaded & new stack registers protocol */
67553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	else {
67653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		switch (new_proto->type) {
67753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_BT:
67853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			/* do nothing */
67953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
68053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_FM:
68153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_GPS:
68253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			st_kim_chip_toggle(new_proto->type, KIM_GPIO_ACTIVE);
68353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			break;
68453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		case ST_MAX:
68553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		default:
68653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("%d protocol not supported",
68753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy				   new_proto->type);
6880b6e7220ed2be6e40bbda4eeb2b9c397008f2cefDan Carpenter			spin_unlock_irqrestore(&st_gdata->lock, flags);
6890b6e7220ed2be6e40bbda4eeb2b9c397008f2cefDan Carpenter			return -EPROTONOSUPPORT;
69053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
69153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->list[new_proto->type] = new_proto;
69236e574fed5578aeee0ce4c7359bc17cc0b8e0b57Naveen Jain		st_gdata->protos_registered++;
69353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		new_proto->write = st_write;
69453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
69553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* lock already held before entering else */
69653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		spin_unlock_irqrestore(&st_gdata->lock, flags);
69753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return err;
69853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
699e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("done %s(%d) ", __func__, new_proto->type);
70053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
70153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan SavoyEXPORT_SYMBOL_GPL(st_register);
70253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
70353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* to unregister a protocol -
70453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * to be called from protocol stack driver
70553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
70653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoylong st_unregister(enum proto_type type)
70753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
708320920cba355146258da7de80bed0069c1dff24aPavan Savoy	long err = 0;
70953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned long flags = 0;
71053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct st_data_s	*st_gdata;
71153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
712e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s: %d ", __func__, type);
71353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
714dbd3a8709560365ff9b1e5eca263f608877a8a89Pavan Savoy	st_kim_ref(&st_gdata, 0);
71553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (type < ST_BT || type >= ST_MAX) {
71653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err(" protocol %d not supported", type);
717320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -EPROTONOSUPPORT;
71853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
71953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
72053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_irqsave(&st_gdata->lock, flags);
72153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
72253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (st_gdata->list[type] == NULL) {
72353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err(" protocol %d not registered", type);
72453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		spin_unlock_irqrestore(&st_gdata->lock, flags);
725320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -EPROTONOSUPPORT;
72653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
72753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
72853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->protos_registered--;
72953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->list[type] = NULL;
73053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
73153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* kim ignores BT in the below function
73253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * and handles the rest, BT is toggled
73353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * only in kim_start and kim_stop
73453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
73553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_kim_chip_toggle(type, KIM_GPIO_INACTIVE);
73653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_unlock_irqrestore(&st_gdata->lock, flags);
73753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
73853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if ((st_gdata->protos_registered == ST_EMPTY) &&
73953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	    (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
74053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_info(" all protocols unregistered ");
74153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
74253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* stop traffic on tty */
74353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (st_gdata->tty) {
74453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			tty_ldisc_flush(st_gdata->tty);
74553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			stop_tty(st_gdata->tty);
74653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		}
74753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
74853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* all protocols now unregistered */
74938d9df499dd3125465cf5aed3d3d6d5c26f0645dPavan Savoy		st_kim_stop(st_gdata->kim_data);
75053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* disable ST LL */
75153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_ll_disable(st_gdata);
75253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
75353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return err;
75453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
75553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
75653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
75753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * called in protocol stack drivers
75853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * via the write function pointer
75953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
76053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoylong st_write(struct sk_buff *skb)
76153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
76253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct st_data_s *st_gdata;
76353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#ifdef DEBUG
76453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	enum proto_type protoid = ST_MAX;
76553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
76653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	long len;
76753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
768dbd3a8709560365ff9b1e5eca263f608877a8a89Pavan Savoy	st_kim_ref(&st_gdata, 0);
76953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (unlikely(skb == NULL || st_gdata == NULL
77053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		|| st_gdata->tty == NULL)) {
77153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("data/tty unavailable to perform write");
772320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -1;
77353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
77453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#ifdef DEBUG			/* open-up skb to read the 1st byte */
77553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	switch (skb->data[0]) {
77653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case HCI_COMMAND_PKT:
77753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case HCI_ACLDATA_PKT:
77853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case HCI_SCODATA_PKT:
77953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		protoid = ST_BT;
78053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
78153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case ST_FM_CH8_PKT:
78253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		protoid = ST_FM;
78353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
78453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	case 0x09:
78553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		protoid = ST_GPS;
78653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		break;
78753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
78853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (unlikely(st_gdata->list[protoid] == NULL)) {
78953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err(" protocol %d not registered, and writing? ",
79053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   protoid);
791320920cba355146258da7de80bed0069c1dff24aPavan Savoy		return -1;
79253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
79353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
794e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%d to be written", skb->len);
79553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	len = skb->len;
79653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
79753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* st_ll to decide where to enqueue the skb */
79853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_int_enqueue(st_gdata, skb);
79953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* wake up */
80053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_tx_wakeup(st_gdata);
80153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
80253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* return number of bytes written */
80353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return len;
80453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
80553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
80653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* for protocols making use of shared transport */
80753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan SavoyEXPORT_SYMBOL_GPL(st_unregister);
80853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
80953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/********************************************************************/
81053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/*
81153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * functions called from TTY layer
81253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
81353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic int st_tty_open(struct tty_struct *tty)
81453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
815320920cba355146258da7de80bed0069c1dff24aPavan Savoy	int err = 0;
81653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct st_data_s *st_gdata;
81753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	pr_info("%s ", __func__);
81853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
819dbd3a8709560365ff9b1e5eca263f608877a8a89Pavan Savoy	st_kim_ref(&st_gdata, 0);
82053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->tty = tty;
82153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty->disc_data = st_gdata;
82253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
82353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* don't do an wakeup for now */
82453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
82553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
82653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* mem already allocated
82753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
82853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty->receive_room = 65536;
82953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* Flush any pending characters in the driver and discipline. */
83053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty_ldisc_flush(tty);
83153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty_driver_flush_buffer(tty);
83253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/*
83353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * signal to UIM via KIM that -
83453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * installation of N_TI_WL ldisc is complete
83553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
83638d9df499dd3125465cf5aed3d3d6d5c26f0645dPavan Savoy	st_kim_complete(st_gdata->kim_data);
837e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("done %s", __func__);
83853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return err;
83953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
84053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
84153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic void st_tty_close(struct tty_struct *tty)
84253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
84353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned char i = ST_MAX;
84453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	unsigned long flags = 0;
84553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct	st_data_s *st_gdata = tty->disc_data;
84653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
84753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	pr_info("%s ", __func__);
84853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
84953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* TODO:
85053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * if a protocol has been registered & line discipline
85153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * un-installed for some reason - what should be done ?
85253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
85353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_irqsave(&st_gdata->lock, flags);
85453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	for (i = ST_BT; i < ST_MAX; i++) {
85553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (st_gdata->list[i] != NULL)
85653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("%d not un-registered", i);
85753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		st_gdata->list[i] = NULL;
85853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
859bb8f3c061f2e7282730059ae524ff19d47d70b17Pavan Savoy	st_gdata->protos_registered = 0;
86053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_unlock_irqrestore(&st_gdata->lock, flags);
86153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/*
86253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * signal to UIM via KIM that -
86353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * N_TI_WL ldisc is un-installed
86453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
86538d9df499dd3125465cf5aed3d3d6d5c26f0645dPavan Savoy	st_kim_complete(st_gdata->kim_data);
86653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->tty = NULL;
86753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* Flush any pending characters in the driver and discipline. */
86853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty_ldisc_flush(tty);
86953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty_driver_flush_buffer(tty);
87053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
87153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_irqsave(&st_gdata->lock, flags);
87253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* empty out txq and tx_waitq */
87353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	skb_queue_purge(&st_gdata->txq);
87453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	skb_queue_purge(&st_gdata->tx_waitq);
87553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* reset the TTY Rx states of ST */
87653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->rx_count = 0;
87753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->rx_state = ST_W4_PACKET_TYPE;
87853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	kfree_skb(st_gdata->rx_skb);
87953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->rx_skb = NULL;
88053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_unlock_irqrestore(&st_gdata->lock, flags);
88153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
882e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s: done ", __func__);
88353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
88453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
88553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic void st_tty_receive(struct tty_struct *tty, const unsigned char *data,
88653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   char *tty_flags, int count)
88753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
88853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
88953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#ifdef VERBOSE
890e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	print_hex_dump(KERN_DEBUG, ">in>", DUMP_PREFIX_NONE,
891e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy		16, 1, data, count, 0);
89253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
89353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
89453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/*
89553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * if fw download is in progress then route incoming data
89653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * to KIM for validation
89753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
89853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_recv(tty->disc_data, data, count);
899e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("done %s", __func__);
90053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
90153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
90253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/* wake-up function called in from the TTY layer
90353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy * inside the internal wakeup function will be called
90453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy */
90553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic void st_tty_wakeup(struct tty_struct *tty)
90653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
90753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct	st_data_s *st_gdata = tty->disc_data;
908e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s ", __func__);
90953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* don't do an wakeup for now */
91053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
91153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
91253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* call our internal wakeup */
91353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_tx_wakeup((void *)st_gdata);
91453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
91553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
91653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoystatic void st_tty_flush_buffer(struct tty_struct *tty)
91753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
91853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct	st_data_s *st_gdata = tty->disc_data;
919e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("%s ", __func__);
92053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
92153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	kfree_skb(st_gdata->tx_skb);
92253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->tx_skb = NULL;
92353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
92453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	tty->ops->flush_buffer(tty);
92553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return;
92653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
92753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
92853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy/********************************************************************/
92953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyint st_core_init(struct st_data_s **core_data)
93053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
93153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	struct st_data_s *st_gdata;
93253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	long err;
93353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	static struct tty_ldisc_ops *st_ldisc_ops;
93453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
93553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* populate and register to TTY line discipline */
93653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops = kzalloc(sizeof(*st_ldisc_ops), GFP_KERNEL);
93753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (!st_ldisc_ops) {
93853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("no mem to allocate");
93953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return -ENOMEM;
94053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
94153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
94253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->magic = TTY_LDISC_MAGIC;
94353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->name = "n_st";	/*"n_hci"; */
94453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->open = st_tty_open;
94553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->close = st_tty_close;
94653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->receive_buf = st_tty_receive;
94753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->write_wakeup = st_tty_wakeup;
94853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->flush_buffer = st_tty_flush_buffer;
94953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_ldisc_ops->owner = THIS_MODULE;
95053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
95153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	err = tty_register_ldisc(N_TI_WL, st_ldisc_ops);
95253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (err) {
95353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("error registering %d line discipline %ld",
95453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			   N_TI_WL, err);
95553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_ldisc_ops);
95653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return err;
95753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
958e6d9e64e36fbeddc7d5b640379bd13930075129bPavan Savoy	pr_debug("registered n_shared line discipline");
95953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
96053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata = kzalloc(sizeof(struct st_data_s), GFP_KERNEL);
96153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (!st_gdata) {
96253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("memory allocation failed");
96353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		err = tty_unregister_ldisc(N_TI_WL);
96453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (err)
96553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("unable to un-register ldisc %ld", err);
96653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_ldisc_ops);
96753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		err = -ENOMEM;
96853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return err;
96953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
97053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
97153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* Initialize ST TxQ and Tx waitQ queue head. All BT/FM/GPS module skb's
97253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 * will be pushed in this queue for actual transmission.
97353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	 */
97453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	skb_queue_head_init(&st_gdata->txq);
97553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	skb_queue_head_init(&st_gdata->tx_waitq);
97653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
97753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* Locking used in st_int_enqueue() to avoid multiple execution */
97853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	spin_lock_init(&st_gdata->lock);
97953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
98053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* ldisc_ops ref to be only used in __exit of module */
98153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	st_gdata->ldisc_ops = st_ldisc_ops;
98253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
98353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#if 0
98453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	err = st_kim_init();
98553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (err) {
98653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("error during kim initialization(%ld)", err);
98753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_gdata);
98853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		err = tty_unregister_ldisc(N_TI_WL);
98953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (err)
99053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("unable to un-register ldisc");
99153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_ldisc_ops);
99253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return -1;
99353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
99453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
99553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
99653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	err = st_ll_init(st_gdata);
99753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (err) {
99853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("error during st_ll initialization(%ld)", err);
99953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_gdata);
100053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		err = tty_unregister_ldisc(N_TI_WL);
100153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (err)
100253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("unable to un-register ldisc");
100353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_ldisc_ops);
100453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		return -1;
100553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
100653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	*core_data = st_gdata;
100753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	return 0;
100853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
100953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
101053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoyvoid st_core_exit(struct st_data_s *st_gdata)
101153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy{
101253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	long err;
101353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	/* internal module cleanup */
101453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	err = st_ll_deinit(st_gdata);
101553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (err)
101653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("error during deinit of ST LL %ld", err);
101753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#if 0
101853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	err = st_kim_deinit();
101953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (err)
102053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		pr_err("error during deinit of ST KIM %ld", err);
102153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy#endif
102253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	if (st_gdata != NULL) {
102353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* Free ST Tx Qs and skbs */
102453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		skb_queue_purge(&st_gdata->txq);
102553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		skb_queue_purge(&st_gdata->tx_waitq);
102653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(st_gdata->rx_skb);
102753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree_skb(st_gdata->tx_skb);
102853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* TTY ldisc cleanup */
102953618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		err = tty_unregister_ldisc(N_TI_WL);
103053618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		if (err)
103153618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy			pr_err("unable to un-register ldisc %ld", err);
103253618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_gdata->ldisc_ops);
103353618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		/* free the global data pointer */
103453618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy		kfree(st_gdata);
103553618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy	}
103653618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy}
103753618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
103853618cc1e51e1f406a467eca9d1dd2675f3ad88ePavan Savoy
1039