I wrote:
> The tricky bit is to find out how to inform the ar6k stack that the
> HIF has been disconnected.
That bit seems to work now. I've attached a patch that shows how.
However, it's not quite done yet ...
> After that, there's also the issue of faking card insertion on
> resume, but that should be easy in comparison.
I should have known better than to taunt Murphy. Suspend works great,
but afterwards, s3cmci can't communicate with the AR6k module:
s3c2440-sdi s3c2440-sdi: CMD[ERR -110] #124 op:8 arg:0x000001aa flags:0x082f5
retries:0 Status:nothing to complete
WLAN_RESET is 1, so that side is good. This also happens if I don't
shut down the ar6k WLAN stack, so this is not the stack sending some
particularly vicious command. Maybe we have to go through the full
module reset ritual, or it's s3cmci acting up again. We'll see.
- Werner
Index: ktrack/drivers/ar6000/hif/hif2.c
===================================================================
--- ktrack.orig/drivers/ar6000/hif/hif2.c 2008-11-18 05:30:40.000000000 -0200
+++ ktrack/drivers/ar6000/hif/hif2.c 2008-11-18 06:01:44.000000000 -0200
@@ -110,6 +110,7 @@
static HTC_CALLBACKS htcCallbacks;
+static int in_shutdown; /* prevent recursion through HIFShutDownDevice */
/* ----- Request processing ------------------------------------------------ */
@@ -521,16 +522,22 @@
HIF_DEVICE *hif = sdio_get_drvdata(func);
int ret;
-#if 0
- /*
- * Funny, Atheros' HIF does this call, but this just puts us in a
- * recursion through HTCShutDown/HIFShutDown if unloading the
- * module.
- */
- ret = htcCallbacks.deviceRemovedHandler(hif->htc_handle, A_OK);
- if (ret != A_OK)
- dev_err(dev, "deviceRemovedHandler: %d\n", ret);
-#endif
+ dev_dbg(dev, "sdio_ar6000_remove\n");
+ if (!in_shutdown) {
+ /*
+ * Funny, Atheros' HIF does this call, but this just puts us in
+ * a recursion through HTCShutDown/HIFShutDown if unloading the
+ * module.
+ *
+ * However, we need it for suspend/resume. See the comment at
+ * HIFShutDown, below.
+ */
+ in_shutdown++;
+ ret = htcCallbacks.deviceRemovedHandler(hif->htc_handle, A_OK);
+ in_shutdown--;
+ if (ret != A_OK)
+ dev_err(dev, "deviceRemovedHandler: %d\n", ret);
+ }
wait_queue_empty(hif);
ret = kthread_stop(hif->io_task);
if (ret)
@@ -591,8 +598,41 @@
}
+/*
+ * We have three possible call chains here:
+ *
+ * System shutdown/reboot:
+ *
+ * kernel_restart_prepare ...> device_shutdown ... > s3cmci_shutdown ...>
+ * sdio_bus_remove -> sdio_ar6000_remove @@@
+ *
+ * Module removal:
+ *
+ * sys_delete_module -> ar6000_cleanup_module -> HTCShutDown ->
+ * HIFShutDownDevice -> sdio_unregister_driver ...> sdio_bus_remove ->
+ * sdio_ar6000_remove
+ *
+ * In this case, HIFShutDownDevice must call sdio_unregister_driver to
+ * notify the driver about its removal. sdio_ar6000_remove must not call
+ * deviceRemovedHandler, because that would loop back into HIFShutDownDevice.
+ *
+ * Suspend:
+ *
+ * device_suspend ...> s3cmci_suspend ...> sdio_bus_remove ->
+ * sdio_ar6000_remove -> deviceRemovedHandler (HTCTargetRemovedHandler) ->
+ * HIFShutDownDevice
+ *
+ * We must call deviceRemovedHandler to inform the ar6k stack that the device
+ * has been removed. Since HTCTargetRemovedHandler calls back into
+ * HIFShutDownDevice, we must also prevent the call to
+ * sdio_unregister_driver, or we'd end up recursing into the SDIO stack,
+ * eventually deadlocking somewhere.
+ */
+
void HIFShutDownDevice(HIF_DEVICE *hif)
{
/* Beware, HTCShutDown calls us with hif == NULL ! */
- sdio_unregister_driver(&sdio_ar6000_driver);
+ if (!in_shutdown++)
+ sdio_unregister_driver(&sdio_ar6000_driver);
+ in_shutdown--;
}