1. 简介
做USB 这么长时间以来,一直埋头在各种gadget, android framework以及芯片bug,很少涉及到usb core层,今天有机会,正好看了suspend/resume, 下面就以一个具体的实例来回顾下usb suspend/resume。
从上图可以看出,这个实例用的是 Synopsys的usb控制器(dwc),下面来具体介绍下suspen/resume
2. suspend
usb suspend从device开始,然后到host,针对device或者host,则是先suspend每一个interface,然后才是device或host本身,接下来我们看下这个实例的suspend过程
2.1. suspend U盘
usb_dev_suspend --> usb_suspend --> usb_suspend_both
从usb_suspend_both的code中可以看出我们前面提到了一个原则 “先suspend interface, 然后是设备本身"
[cpp] view plaincopy
![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)
1169 {
1170 int status = 0;
1171 int i = 0, n = 0;
1172 struct usb_interface *intf;
1173
1174 if (udev->state == USB_STATE_NOTATTACHED ||
1175 udev->state == USB_STATE_SUSPENDED)
1176 goto done;
1177
1178 /* Suspend all the interfaces and then udev itself */
1179 if (udev->actconfig) {
1180 n = udev->actconfig->desc.bNumInterfaces;
1181 printk("%s interface:%d\n", __func__, n);
1182 for (i = n - 1; i >= 0; --i) {
1183 intf = udev->actconfig->interface[i];
1184 status = usb_suspend_interface(udev, intf, msg);
1185
1186 /* Ignore errors during system sleep transitions */
1187 if (!PMSG_IS_AUTO(msg))
1188 status = 0;
1189 if (status != 0)
1190 break;
1191 }
1192 }
1193 if (status == 0) {
1194 status = usb_suspend_device(udev, msg);
1195
1196 /*
1197 * Ignore errors from non-root-hub devices during
1198 * system sleep transitions. For the most part,
1199 * these devices should go to low power anyway when
1200 * the entire bus is suspended.
1201 */
1202 if (udev->parent && !PMSG_IS_AUTO(msg))
1203 status = 0;
1204 }
1205
1206 /* If the suspend failed, resume interfaces that did get suspended */
1207 if (status != 0) {
1208 msg.event ^= (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
1209 while (++i < n) {
1210 intf = udev->actconfig->interface[i];
1211 usb_resume_interface(udev, intf, msg, 0);
1212 }
1213
1214 /* If the suspend succeeded then prevent any more URB submissions
1215 * and flush any outstanding URBs.
1216 */
1217 } else {
1218 udev->can_submit = 0;
1219 for (i = 0; i < 16; ++i) {
1220 usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
1221 usb_hcd_flush_endpoint(udev, udev->ep_in[i]);
1222 }
1223 }
1224
1225 done:
1226 dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
1227 return status;
1228 }
U盘只有一个interface:mass_storage. 所以这里suspend interface会调用到 usb_stor_suspend(drivers/usb/storage/usb.c),接下来就是suspend U盘本身,这里会调用到
generic_suspend
[objc] view plaincopy
![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)
198 {
199 int rc;
200
201 /* Normal USB devices suspend through their upstream port.
202 * Root hubs don't have upstream ports to suspend,
203 * so we have to shut down their downstream HC-to-USB
204 * interfaces manually by doing a bus (or "global") suspend.
205 */
206 if (!udev->parent)
207 rc = hcd_bus_suspend(udev, msg);
208
209 /* Non-root devices don't need to do anything for FREEZE or PRETHAW */
210 else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
211 rc = 0;
212 else
213 rc = usb_port_suspend(udev, msg);
214
215 return rc;
216 }
因为这个是外设,所以具体调用的是usb_port_suspend,这个函数的主要作用就是发送usb request SET_FEATURE PORT_SUSPEND给设备。
2.2 suspend OTG Host
和device相同,还是从usb_suspend_both开始,只不过HOST只有一个interface: hub,所以这里会调用到hub_suspend
hub_suspend主要作用是停止urb以及一些delay work. 接下来就是suspend HOST本身,与device相同,这里调用的还是generic_suspend,
只不过由于是root hub,所以调用的是hcd_bus_suspend
[cpp] view plaincopy
![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)
1960 {
1961 struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
1962 int status;
1963 int old_state = hcd->state;
1964
1965 dev_dbg(&rhdev->dev, "bus %ssuspend, wakeup %d\n",
1966 (PMSG_IS_AUTO(msg) ? "auto-" : ""),
1967 rhdev->do_remote_wakeup);
1968 if (HCD_DEAD(hcd)) {
1969 dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "suspend");
1970 return 0;
1971 }
1972
1973 if (!hcd->driver->bus_suspend) {
1974 status = -ENOENT;
1975 } else {
1976 clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
1977 hcd->state = HC_STATE_QUIESCING;
1978 status = hcd->driver->bus_suspend(hcd);
1979 }
这里主要起作用的是 status = hcd->driver->bus_suspend(hcd),这个函数指针会调用到HOST控制器注册的bus_suspend函数,针对这个实例为
static int dwc_otg_bus_suspend(struct usb_hcd *hcd)
3. resume
resume是suspend的一个反过程,从Host到device,从Host/device本身然后再到各个interface.
3.1 resume OTG Host
从usb_resume_both开始
[cpp] view plaincopy
![在CODE上查看代码片](https://code.csdn.net/assets/CODE_ico.png)
1249 {
1250 int status = 0;
1251 int i;
1252 struct usb_interface *intf;
1253
1254 if (udev->state == USB_STATE_NOTATTACHED) {
1255 status = -ENODEV;
1256 goto done;
1257 }
1258 udev->can_submit = 1;
1259
1260 /* Resume the device */
1261 if (udev->state == USB_STATE_SUSPENDED || udev->reset_resume)
1262 status = usb_resume_device(udev, msg);
1263
1264 /* Resume the interfaces */
1265 if (status == 0 && udev->actconfig) {
1266 for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
1267 intf = udev->actconfig->interface[i];
1268 usb_resume_interface(udev, intf, msg,
1269 udev->reset_resume);
1270 }
1271 }
1272 usb_mark_last_busy(udev);
1273
1274 done:
1275 dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
1276 if (!status)
1277 udev->reset_resume = 0;
1278 return status;
1279 }
和suspend对应,这里先resume Host本身,调用dwc_otg_bus_resume。接下来resume interface,调用hub_resume
hub_resume会判断每一个port的port status,然后做对应的处理(这里先略过,需要参照协议)
3.2. resume U盘
从usb_resume_both开始,先resume U盘本身,调用usb_port_resume, 这里会判断port状态,并且下usb request CLEAR_FEATURE PORT_SUSPEND,
通过返回的状态判定是做resume/reset_resume/disconnect。这里先不展开,需要结合协议来看。
接下来会调用usb_stor_resume去resume interface。