/* * Get the device ready for USB port or system standby and hibernation * * USB port and system standby are handled the same. * * When the system hibernates, the USB device is powered down and then * up, so we don't really have to do much here, as it will be seen as * a reconnect. Still for simplicity we consider this case the same as * suspend, so that the device has a chance to do notify the base * station (if connected). * * So at the end, the three cases require common handling. * * If at the time of this call the device's firmware is not loaded, * nothing has to be done. * * If the firmware is loaded, we need to: * * - tell the device to go into host interface power save mode, wait * for it to ack * * This is quite more interesting than it is; we need to execute a * command, but this time, we don't want the code in usb-{tx,rx}.c * to call the usb_autopm_get/put_interface() barriers as it'd * deadlock, so we need to decrement i2400mu->do_autopm, that acts * as a poor man's semaphore. Ugly, but it works. * * As well, the device might refuse going to sleep for whichever * reason. In this case we just fail. For system suspend/hibernate, * we *can't* fail. We look at usb_dev->auto_pm to see if the * suspend call comes from the USB stack or from the system and act * in consequence. * * - stop the notification endpoint polling */ static int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) { int result = 0; struct device *dev = &iface->dev; struct i2400mu *i2400mu = usb_get_intfdata(iface); #ifdef CONFIG_PM struct usb_device *usb_dev = i2400mu->usb_dev; #endif unsigned is_autosuspend = 0; struct i2400m *i2400m = &i2400mu->i2400m; #ifdef CONFIG_PM if (usb_dev->auto_pm > 0) is_autosuspend = 1; #endif d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); if (i2400m->updown == 0) goto no_firmware; if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { /* ugh -- the device is connected and this suspend * request is an autosuspend one (not a system standby * / hibernate). * * The only way the device can go to standby is if the * link with the base station is in IDLE mode; that * were the case, we'd be in status * I2400M_SS_CONNECTED_IDLE. But we are not. * * If we *tell* him to go power save now, it'll reset * as a precautionary measure, so if this is an * autosuspend thing, say no and it'll come back * later, when the link is IDLE */ result = -EBADF; d_printf(1, dev, "fw up, link up, not-idle, autosuspend: " "not entering powersave\n"); goto error_not_now; } d_printf(1, dev, "fw up: entering powersave\n"); atomic_dec(&i2400mu->do_autopm); result = i2400m_cmd_enter_powersave(i2400m); atomic_inc(&i2400mu->do_autopm); if (result < 0 && !is_autosuspend) { /* System suspend, can't fail */ dev_err(dev, "failed to suspend, will reset on resume\n"); result = 0; } if (result < 0) goto error_enter_powersave; i2400mu_notification_release(i2400mu); d_printf(1, dev, "powersave requested\n"); error_enter_powersave: error_not_now: no_firmware: d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", iface, pm_msg.event, result); return result; }
static int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) { int result = 0; struct device *dev = &iface->dev; struct i2400mu *i2400mu = usb_get_intfdata(iface); unsigned is_autosuspend = 0; struct i2400m *i2400m = &i2400mu->i2400m; #ifdef CONFIG_PM if (PMSG_IS_AUTO(pm_msg)) is_autosuspend = 1; #endif d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); rmb(); /* */ if (i2400m->updown == 0) goto no_firmware; if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) { /* */ result = -EBADF; d_printf(1, dev, "fw up, link up, not-idle, autosuspend: " "not entering powersave\n"); goto error_not_now; } d_printf(1, dev, "fw up: entering powersave\n"); atomic_dec(&i2400mu->do_autopm); result = i2400m_cmd_enter_powersave(i2400m); atomic_inc(&i2400mu->do_autopm); if (result < 0 && !is_autosuspend) { /* */ dev_err(dev, "failed to suspend, will reset on resume\n"); result = 0; } if (result < 0) goto error_enter_powersave; i2400mu_notification_release(i2400mu); d_printf(1, dev, "powersave requested\n"); error_enter_powersave: error_not_now: no_firmware: d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", iface, pm_msg.event, result); return result; }
static void i2400mu_bus_dev_stop(struct i2400m *i2400m) { struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m); struct device *dev = &i2400mu->usb_iface->dev; d_fnstart(3, dev, "(i2400m %p)\n", i2400m); i2400mu_notification_release(i2400mu); i2400mu_rx_release(i2400mu); i2400mu_tx_release(i2400mu); d_fnend(3, dev, "(i2400m %p) = void\n", i2400m); }