in ti-st/st_core.c [228:390]
void st_int_recv(void *disc_data,
const unsigned char *data, long count)
{
char *ptr;
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 ? */
if (unlikely(ptr == NULL) || (st_gdata == NULL)) {
pr_err(" received null from TTY ");
return;
}
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) {
len = min_t(unsigned int, st_gdata->rx_count, count);
skb_put_data(st_gdata->rx_skb, ptr, len);
st_gdata->rx_count -= len;
count -= len;
ptr += len;
if (st_gdata->rx_count)
continue;
/* Check ST RX state machine , where are we? */
switch (st_gdata->rx_state) {
/* 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(st_gdata->rx_chnl, st_gdata);
st_gdata->rx_state = ST_W4_PACKET_TYPE;
st_gdata->rx_skb = NULL;
continue;
/* 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 */
}
/* end of if rx_count */
/*
* Check first byte of packet and identify module
* owner (BT/FM/GPS)
*/
switch (*ptr) {
case LL_SLEEP_IND:
case LL_SLEEP_ACK:
case LL_WAKE_UP_IND:
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_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:
type = *ptr;
/*
* Default case means non-HCILL packets,
* possibilities are packets for:
* (a) valid protocol - Supported Protocols within
* the ST_MAX_CHANNELS.
* (b) registered protocol - Checked by
* "st_gdata->list[type] == NULL)" are supported
* protocols only.
* Rules out any invalid protocol and
* unregistered protocols with channel ID < 16.
*/
if ((type >= ST_MAX_CHANNELS) ||
(st_gdata->list[type] == NULL)) {
pr_err("chip/interface misbehavior: "
"dropping frame starting "
"with 0x%02x\n", type);
goto done;
}
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;
}
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;
}