diff options
Diffstat (limited to 'drivers/misc/ti-st/st_core.c')
| -rw-r--r-- | drivers/misc/ti-st/st_core.c | 79 |
1 files changed, 54 insertions, 25 deletions
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 486117f72c9..1972d57aadb 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -22,7 +22,6 @@ #define pr_fmt(fmt) "(stc): " fmt #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/tty.h> #include <linux/seq_file.h> @@ -30,11 +29,13 @@ #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); /********************************************************************/ static void add_channel_to_table(struct st_data_s *st_gdata, @@ -43,13 +44,15 @@ static void add_channel_to_table(struct st_data_s *st_gdata, 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->list[proto->chnl_id] = NULL; */ + st_gdata->is_registered[proto->chnl_id] = false; } /* @@ -98,13 +101,13 @@ int st_int_write(struct st_data_s *st_gdata, * push the skb received to relevant * protocol stacks */ -void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) +static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) { pr_debug(" %s(prot:%d) ", __func__, chnl_id); if (unlikely (st_gdata == NULL || st_gdata->rx_skb == NULL - || st_gdata->list[chnl_id] == NULL)) { + || 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); @@ -135,20 +138,23 @@ void st_send_frame(unsigned char chnl_id, 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_CHANNELS; i++) { - if (likely(st_gdata != NULL && st_gdata->list[i] != NULL && - st_gdata->list[i]->reg_complete_cb != NULL)) { + 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->list[i] = NULL; + st_gdata->is_registered[i] = false; } } } @@ -233,7 +239,8 @@ void st_int_recv(void *disc_data, char *ptr; struct st_proto_s *proto; unsigned short payload_len = 0; - int len = 0, type = 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; @@ -335,9 +342,20 @@ void st_int_recv(void *disc_data, /* Unknow packet? */ default: type = *ptr; + if (st_gdata->list[type] == NULL) { + pr_err("chip/interface misbehavior dropping" + " frame starting with 0x%02x", 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 */ @@ -351,6 +369,7 @@ void st_int_recv(void *disc_data, ptr++; count--; } +done: spin_unlock_irqrestore(&st_gdata->lock, flags); pr_debug("done %s", __func__); return; @@ -362,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; @@ -384,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; @@ -475,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[0x04] != NULL ? 'R' : 'U', - st_gdata->list[0x08] != NULL ? 'R' : 'U', - st_gdata->list[0x09] != 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'); } /********************************************************************/ @@ -492,7 +511,6 @@ 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->chnl_id); 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"); @@ -504,7 +522,7 @@ long st_register(struct st_proto_s *new_proto) return -EPROTONOSUPPORT; } - if (st_gdata->list[new_proto->chnl_id] != NULL) { + if (st_gdata->is_registered[new_proto->chnl_id] == true) { pr_err("chnl_id %d already registered", new_proto->chnl_id); return -EALREADY; } @@ -528,11 +546,12 @@ long st_register(struct st_proto_s *new_proto) 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 */ @@ -542,11 +561,16 @@ 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 "); + 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 -EINVAL; } + spin_lock_irqsave(&st_gdata->lock, flags); + clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); st_recv = st_int_recv; @@ -563,13 +587,13 @@ 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->chnl_id] != NULL) { + if (st_gdata->is_registered[new_proto->chnl_id] == true) { pr_err(" proto %d already registered ", new_proto->chnl_id); + spin_unlock_irqrestore(&st_gdata->lock, flags); return -EALREADY; } - spin_lock_irqsave(&st_gdata->lock, flags); add_channel_to_table(st_gdata, new_proto); st_gdata->protos_registered++; new_proto->write = st_write; @@ -602,14 +626,14 @@ long st_unregister(struct st_proto_s *proto) pr_debug("%s: %d ", __func__, proto->chnl_id); st_kim_ref(&st_gdata, 0); - if (proto->chnl_id >= ST_MAX_CHANNELS) { + 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[proto->chnl_id] == NULL) { + 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; @@ -619,6 +643,10 @@ long st_unregister(struct st_proto_s *proto) 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 chnl_ids unregistered "); @@ -714,9 +742,10 @@ static void st_tty_close(struct tty_struct *tty) */ spin_lock_irqsave(&st_gdata->lock, flags); for (i = ST_BT; i < ST_MAX_CHANNELS; i++) { - if (st_gdata->list[i] != NULL) + 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); @@ -782,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; } |
