Commit bfb88d6c authored by Pavan Savoy's avatar Pavan Savoy Committed by Greg Kroah-Hartman

drivers:misc: ti-st: protect registrations

Concurrent access to UART2/combo-interface by multiple protocol drivers such
as BT, FM and GPS caused issues during firmware download failure cases or
cases when the firmware download took longer than usual.

This was because of un-safe access to protos_registered & st_states.
Protecting this will also make the registration complete callback un-safe for
sleep.
Signed-off-by: default avatarPavan Savoy <pavan_savoy@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 1ff97647
...@@ -137,6 +137,8 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata) ...@@ -137,6 +137,8 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
* st_reg_complete - * st_reg_complete -
* to call registration complete callbacks * to call registration complete callbacks
* of all protocol stack drivers * 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) void st_reg_complete(struct st_data_s *st_gdata, char err)
{ {
...@@ -538,11 +540,12 @@ long st_register(struct st_proto_s *new_proto) ...@@ -538,11 +540,12 @@ long st_register(struct st_proto_s *new_proto)
set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); set_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
st_recv = st_kim_recv; 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 */ /* release lock previously held - re-locked below */
spin_unlock_irqrestore(&st_gdata->lock, flags); 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 /* this may take a while to complete
* since it involves BT fw download * since it involves BT fw download
*/ */
...@@ -553,10 +556,13 @@ long st_register(struct st_proto_s *new_proto) ...@@ -553,10 +556,13 @@ long st_register(struct st_proto_s *new_proto)
(test_bit(ST_REG_PENDING, &st_gdata->st_state))) { (test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
pr_err(" KIM failure complete callback "); pr_err(" KIM failure complete callback ");
st_reg_complete(st_gdata, err); st_reg_complete(st_gdata, err);
clear_bit(ST_REG_PENDING, &st_gdata->st_state);
} }
return -EINVAL; return -EINVAL;
} }
spin_lock_irqsave(&st_gdata->lock, flags);
clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state); clear_bit(ST_REG_IN_PROGRESS, &st_gdata->st_state);
st_recv = st_int_recv; st_recv = st_int_recv;
...@@ -576,10 +582,10 @@ long st_register(struct st_proto_s *new_proto) ...@@ -576,10 +582,10 @@ long st_register(struct st_proto_s *new_proto)
if (st_gdata->is_registered[new_proto->chnl_id] == true) { if (st_gdata->is_registered[new_proto->chnl_id] == true) {
pr_err(" proto %d already registered ", pr_err(" proto %d already registered ",
new_proto->chnl_id); new_proto->chnl_id);
spin_unlock_irqrestore(&st_gdata->lock, flags);
return -EALREADY; return -EALREADY;
} }
spin_lock_irqsave(&st_gdata->lock, flags);
add_channel_to_table(st_gdata, new_proto); add_channel_to_table(st_gdata, new_proto);
st_gdata->protos_registered++; st_gdata->protos_registered++;
new_proto->write = st_write; new_proto->write = st_write;
...@@ -619,7 +625,7 @@ long st_unregister(struct st_proto_s *proto) ...@@ -619,7 +625,7 @@ long st_unregister(struct st_proto_s *proto)
spin_lock_irqsave(&st_gdata->lock, flags); 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); pr_err(" chnl_id %d not registered", proto->chnl_id);
spin_unlock_irqrestore(&st_gdata->lock, flags); spin_unlock_irqrestore(&st_gdata->lock, flags);
return -EPROTONOSUPPORT; return -EPROTONOSUPPORT;
...@@ -629,6 +635,10 @@ long st_unregister(struct st_proto_s *proto) ...@@ -629,6 +635,10 @@ long st_unregister(struct st_proto_s *proto)
remove_channel_from_table(st_gdata, proto); remove_channel_from_table(st_gdata, proto);
spin_unlock_irqrestore(&st_gdata->lock, flags); 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) && if ((st_gdata->protos_registered == ST_EMPTY) &&
(!test_bit(ST_REG_PENDING, &st_gdata->st_state))) { (!test_bit(ST_REG_PENDING, &st_gdata->st_state))) {
pr_info(" all chnl_ids unregistered "); pr_info(" all chnl_ids unregistered ");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment