From f80b5e99c7dac5a9a0d72496cec5075a12cd1476 Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 21 Nov 2008 20:40:09 -0200 Subject: rfkill: preserve state across suspend The rfkill class API requires that the driver connected to a class call rfkill_force_state() on resume to update the real state of the rfkill controller, OR that it provides a get_state() hook. This means there is potentially a hidden call in the resume code flow that changes rfkill->state (i.e. rfkill_force_state()), so the previous state of the transmitter was being lost. The simplest and most future-proof way to fix this is to explicitly store the pre-sleep state on the rfkill structure, and restore from that on resume. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Matthew Garrett Cc: Alan Jenkins Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net/rfkill/rfkill.c') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index ec26eae8004..5ad411d3e8f 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -565,10 +565,15 @@ static void rfkill_release(struct device *dev) #ifdef CONFIG_PM static int rfkill_suspend(struct device *dev, pm_message_t state) { + struct rfkill *rfkill = to_rfkill(dev); + /* mark class device as suspended */ if (dev->power.power_state.event != state.event) dev->power.power_state = state; + /* store state for the resume handler */ + rfkill->state_for_resume = rfkill->state; + return 0; } @@ -590,7 +595,7 @@ static int rfkill_resume(struct device *dev) rfkill_toggle_radio(rfkill, rfkill_epo_lock_active ? RFKILL_STATE_SOFT_BLOCKED : - rfkill->state, + rfkill->state_for_resume, 1); mutex_unlock(&rfkill->mutex); -- cgit v1.2.3 From 24689c8590be6bc0486af665c5ceda6aeff4d53a Mon Sep 17 00:00:00 2001 From: Henrique de Moraes Holschuh Date: Fri, 21 Nov 2008 20:40:10 -0200 Subject: rfkill: always call get_state() hook on resume We "optimize" away the get_state() hook call on rfkill_toggle_radio when doing a forced state change. This means the resume path is not calling get_state() as it should. Call it manually on the resume handler, as we don't want to mess with the EPO path by removing the optimization. This has the added benefit of making it explicit that rfkill->state could have been modified before we hit the rfkill_toggle_radio() call in the class resume handler. Signed-off-by: Henrique de Moraes Holschuh Acked-by: Ivo van Doorn Cc: Matthew Garrett Cc: Alan Jenkins Signed-off-by: John W. Linville --- net/rfkill/rfkill.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'net/rfkill/rfkill.c') diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index 5ad411d3e8f..051d2c9ea66 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -580,12 +580,22 @@ static int rfkill_suspend(struct device *dev, pm_message_t state) static int rfkill_resume(struct device *dev) { struct rfkill *rfkill = to_rfkill(dev); + enum rfkill_state newstate; if (dev->power.power_state.event != PM_EVENT_ON) { mutex_lock(&rfkill->mutex); dev->power.power_state.event = PM_EVENT_ON; + /* + * rfkill->state could have been modified before we got + * called, and won't be updated by rfkill_toggle_radio() + * in force mode. Sync it FIRST. + */ + if (rfkill->get_state && + !rfkill->get_state(rfkill->data, &newstate)) + rfkill->state = newstate; + /* * If we are under EPO, kick transmitter offline, * otherwise restore to pre-suspend state. -- cgit v1.2.3