Passengers on the bus should note that there are several thieves on the next stop. You must take care of your wallet and items you carry with you.
-- A reporter from Northeast China heard a prompt from the bus conductor in Huludao. at this moment, I also need to remind you in advance that, for uhci, we can end it now if we want to end it. If we don't want it, it's hard to continue moving forward. if we continue, we will focus on the power management, just as we are concerned about in the hub driver. since this part of the code is rather abstract, we use KDB to interpret the code by doing experiments. if you are not interested in power management, you can leave this step. in this world, wensi three thousand is not as good as its chest. Peking University people's congress is not as great as it is. so you don't have to study this unimportant code as boring as I do, because even if you read it, you won't be worth 3 million as much as Tang Wei's sister. On the contrary, if you are an IT person, so please be ready to kick at any time. assume that I have loaded the uhci-HCD module and inserted the USB flash disk. In this case, check the information provided by debugfs in sysfs: localhost :~ # Cat/sys/kernel/debug/uhci/0000/: 00/: 1D. 0root-hub state: Running fsbr: 0hc status usbcmd = 00c1 maxp64 CF Rs usbstat = 0000 usbint = 000f usbfrnum = (1) 9f4 flbaseadd = 1cac59f4 sof = 40 STAT1 = 0095 enabled connected stat2 = 0080 most recent frame: ee49 (585) last ISO frame: ee49 (585) periodic load Table 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Total: 0, # INT: 0, # ISO: 0. One row prints the root hub status, which corresponds to the uhci-> rh_state member. Currently, we see running. we can compare the information of the Host Controller suspended by another root hub: localhost :~ # Cat/sys/kernel/debug/uhci/0000/: 00/: 1D. 1root-hub state: suspended fsbr: 0hc status USB cmd = 0048 maxp32 CF egsm USB stat = 0020 hchalted USB Int = 0002 USB frnum = (0) 0e8 flbaseadd = 1db810e8 sof = 40 STAT1 = 0080 stat2 = 0080 most recent frame: 103a (58) last ISO frame: 103a (58) periodic load Table 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Total: 0, # INT: 0, # ISO: 0. here we can see Root The hub status is suincluded. at this time, we enter KDB and set some breakpoints. For example, I set suspend_rh. then exit KDB and unplug the USB flash drive. at this time, the KDB prompt will jump out Because suspend_rh will be executed. the well-known BT command in KDB shows that the uhci_hub_status_data function is called to call suspend_rh, and the function that calls uhci_hub_status_data is rh_timer_func. so let's take a closer look at this uhci_hub_status_data function. 184 static int uhci_hub_status_data (struct usb_hcd * HCD, char * BUF) 185 {186 struct uhci_hcd * uhci = hcd_to_uhci (HCD); 187 unsigned lon G flags; 188 int status = 0; 189 190 spin_lock_irqsave (& uhci-> lock, flags); 191 192 uhci_scan_schedule (uhci); 193 If (! Test_bit (outputs, & HCD-> flags) | uhci-> dead) 194 goto done; 195 uhci_check_ports (uhci); 196 197 status = get_hub_status_data (uhci, Buf ); 198 199 switch (uhci-> rh_state) {200 case uhci_rh_suspending: 201 Case uhci_rh_susponded: 202/* If Port change, ask to be resumed */203 If (Status) 204 usb_hcd_resume_root_hub (HCD); 205 break; 206 207 case uhci_rh_auto_stopped: 208/* If Port ch Ange, auto start */209 If (Status) 210 wakeup_rh (uhci); 211 break; 212 213 case uhci_rh_running: 214/* Are any devices attached? */215 if (! Any_ports_active (uhci) {216 uhci-> rh_state = uhci_rh_running_nodevs; 217 uhci-> auto_stop_time = jiffies + Hz; 218} 219 break; 220 221 case later: 222/* auto-stop if nothing connected for 1 second */223 If (any_ports_active (uhci) 224 uhci-> rh_state = uhci_rh_running; 225 else if (time_after_eq (jiffies, uhci-> auto_stop_time) 226 suspend_rh (uhci, uhci_rh_auto_stopped); 227 break; 228 229 default: 230 break; 231} 232 233 done: 234 spin_unlock_irqrestore (& uhci-> lock, flags); 235 return status; 236} row 225, time_after_eq line, and the 222 lines of these annotations tell us that when I pull the USB flash drive out, suspend_rh will be called in one second. obviously, when we enter this function, Rh-> state is equal to uhci_rh_running_nodevs. this function comes from drivers/USB/host/uhci-hcd.c: 259 static void suspend_rh (struct uhci_hcd * uhci, Enum uhci_rh_state new_state) 260 _ Releases (uhci-> lock) 261 _ acquires (Uhci-> lock) 262 {263 int auto_stop; 264 int int_enable, egsm_enable; 265 266 auto_stop = (new_state = running); 267 dev_dbg (& uhci_to_hcd (uhci)-> self. root_hub-> Dev, 268 "% S % s/n", _ FUNCTION __, 269 (auto_stop? "(Auto-Stop )":"")); 270 271/* if we get a suspend request when we're already auto-stopped 272 * then there's nothing to do. 273 */274 if (uhci-> rh_state = uhci_rh_auto_stopped) {275 uhci-> rh_state = new_state; 276 return; 277} 278 279/* enable resume-detect interrupts if they work. 280 * then enter global suspend mode if _ it _ works, still configured. 281 */282 egsm_enable = usb1__egsm; 283 uhci-> working_rd = 1; 284 int_enable = usbintr_resume; 285 If (remote_wakeup_is_broken (uhci) 286 egsm_enable = 0; 287 if (callback (uhci) |! Egsm_enable | 288! Device_may_wakeup (289 & uhci_to_hcd (uhci)-> self. root_hub-> Dev) 290 uhci-> working_rd = int_enable = 0; 291 292 outw (int_enable, uhci-> io_addr + usbintr); 293 outw (egsm_enable | usb1__cf, uhci-> io_addr + usbcmd), 294 MB (), 295 udelay (5 ); 296 297/* If we're re auto-stopping then no devices have been attached 298 * for a while, so there shouldn't be any active URBS and the 299 * controller shoulstop After a few microseconds. Otherwise 300 * we will give the Controller one frame to stop. 301 */302 if (! Auto_stop &&! (Inw (uhci-> io_addr + usbsts) & usbsts_hch) {303 uhci-> rh_state = running; 304 spin_unlock_irq (& uhci-> lock); 305 msleep (1 ); 306 spin_lock_irq (& uhci-> lock); 307 if (uhci-> dead) 308 return; 309} 310 if (! (Inw (uhci-> io_addr + usbsts) & usbsts_hch) 311 dev_warn (& uhci_to_hcd (uhci)-> self. root_hub-> Dev, 312 "Controller not stopped yet! /N "); 313 314 bytes (uhci); 315 316 uhci-> rh_state = new_state; 317 uhci-> is_stopped = uhci_is_stopped; 318 uhci_to_hcd (uhci)-> poll_rh =! Int_enable; 319 320 uhci_scan_schedule (uhci); 321 uhci_fsbr_off (uhci); 322} note that the second parameter we pass in is uhci_rh_auto_stopped, while new_state is the form parameter. therefore, the auto_stop of the first row is 1. the following describes several important macros involved. in the first usb1__egsm, bit3.egsm in the uhci command Register indicates enter global suspend mode, which is described in uhci spec as follows:
Enter global suspend mode (egsm ).1 = Host Controller enters the global suspend mode. no USB transactions occurs during this time. the host controller is able to receive resume signals from USB and interrupt the system. software resets this bit to 0 to come out of global suspend mode. software writes this bit to 0 at the same time that force global resume (bit 4) is written to 0 or after writing bit 4 to 0. software must also ensure that the run/stop bit (bit 0) is cleared prior to setting this bit. the second one is usb1__cf, which is in the uhci command register bit6.
Configure flag (CF ).HCd software sets this bit as the last action in its process of processing ing the host controller. this bit has no effect on the hardware. it is provided only as a semaphore service for software. third, usbintr_resume, which corresponds to the interrupt enable register of uhci. The full name is resume interrupt enable. here we set this bit, which indicates that when the resume occurs, an interruption will occur. then we set usb1__egsm and usb1__cf in the command register on line 1, which basically declared that we were suspended. the fourth usbsts_hch corresponds to bit5 in uhci's Status Register and is named hchalted.. If this parameter is set, the host controller is declared to have gone on strike. here we can see that auto_stop is the value assigned in row 266. In our context, it is true, so although the State Register hchalted has not been set, the IF condition is not met, therefore, Row 3 will not be executed. then, Row 3 obtains the current frame number. set uhci-> rh_state to uhci_rh_auto_stopped.317 and set uhci-> is_stopped to uhci_is_stopped.uhci_is_stopped. The value of uhci-> rh_state in row 316 is 9999, in fact, we determine whether uhci-> is_stopped is zero or not. for example, we set uhci-> is_stopped to 0 in start_rh. in uhci_set_next_interrupt, we will determine whether it sets HCD's poll_rh for row 0.318, and set int_enable to usbintr _ for row 284 _ Resume. If the if condition of row 287 is met, int_enable is set to 0. however, even in the literal sense, we can understand that poll_rh indicates polling for the root hub. If the interrupt is enabled, no polling is required. If the interrupt is disabled, the polling is supported. run uhci_scan_schedule and uhci_fsbr_off. the former is to deal with those after scheduling, the latter we did not post, from drivers/USB/host/uhci-q.c: 59 static void uhci_fsbr_off (struct uhci_hcd * uhci) 60 {61 struct uhci_qh * Lqh; 62 63/* remove the link from the last async QH to the terminating 64 * skeleton Qh. */65 uhci-> fsbr_is_on = 0; 66 Lqh = List_entry (uhci-> skel_async_qh-> node. prev, 67 struct uhci_qh, node); 68 Lqh-> link = uhci_ptr_term; 69} is actually the opposite of the original uhci_fsbr_on. originally, we directed the link of the last node of async QH to skel_term_qh. Now we still need to restore its true color so that it points to uhci_ptr_term as it was originally. at the same time, we set fsbr_is_on to 0, and the suspension of the root hub will be completed. however, if you exit KDB by running the go command, you will find that you enter KDB again. because suspend_rh will be called again. this time the situation is different from the previous one. this time you use the BT command to check the call relationship. You will find that the call to suspend_rh is uhci_rh_suspend, and the call to uhci_rh_suspend is HC. D_bus_suspend: the call to hcd_bus_suspend is hub_suspend.hub_suspend. We are no stranger to it. We launched a grand function in the hub driver. when we keep tracing back, we will know that the whole call is triggered by usb_autosuspend_work, that is, the autosuspend mechanism triggers the call of this series of functions. autosuspend/autoresume is actually implemented by USB core. We mentioned enough in the hub driver. Here we start from the old friend hub_suspend, 1919 static int hub_suspend (struct usb_interface * INTF, pm_message_t MSG) 1920 {1921 struct usb_hub * hub = usb_get_intfdata (INTF); 1922 struct usb_device * hdev = Hub-> hdev; 1923 unsigned port1; 1924 int status = 0; 1925 1926/* fail if children aren't already suincluded */1927 for (port1 = 1; port1 <= hdev-> maxchild; port1 ++) {1928 struct usb_device * udev; 1929 1930 udev = hdev-> Children [port1-1]; 1931 if (udev & MSG. event = pm_event_suspend & 1932 # ifdef config_usb_suspend 1933 udev-> state! = Usb_state_suincluded 1934 # else 1935 udev-> Dev. Power. power_state.event 1936 = pm_event_on 1937 # endif 1938) {1939 if (! Hdev-> auto_pm) 1940 dev_dbg (& INTF-> Dev, "port % d nyet suincluded/N", 1941 port1); 1942 return-ebusy; 1943} 1944} 1945 1946 dev_dbg (& INTF-> Dev, "% s/n", _ function __); 1947 1948/* Stop khubd and related activity */1949 hub_quiesce (hub); 1950 1951 1952/* "Global suspend" of the downstream HC-to-USB interface */If (! Hdev-> parent) {1953 status = hcd_bus_suspend (hdev-> Bus); 1954 if (status! = 0) {1955 dev_dbg (& hdev-> Dev, "'global' suspend % d/N", status); 1956 hub_activate (hub ); 1957} 1958} 1959 return status; 1960} Obviously, the 1953 rows will be executed, that is, hcd_bus_suspend will be executed. this function is from drivers/USB/CORE/HCD. c: 1258 int hcd_bus_suspend (struct usb_bus * Bus) 1259 {1260 struct usb_hcd * HCD; 1261 int status; 1262 1263 HCD = container_of (bus, struct usb_hcd, self ); 1264 if (! HCd-> driver-> bus_suspend) 1265 return-enoent; 1266 HCD-> state = hc_state_quiescing; 1267 status = HCD-> driver-> bus_suspend (HCD ); 1268 if (status = 0) 1269 HCD-> state = hc_state_suincluded; 1270 else 1271 dev_dbg (& bus-> root_hub-> Dev, "% s fail, err % d/N ", 1272" Suspend ", status); 1273 return status; 1274} the HCD-> state is set to hc_state_quiescing, and then bus_suspend of the HCD driver is called, for uhci, this is uhci_rh_suspend. come Self Drivers/USB/host/uhci-hcd.c: 713 static int uhci_rh_suspend (struct usb_hcd * HCD) 714 {715 struct uhci_hcd * uhci = hcd_to_uhci (HCD); 716 int rc = 0; 717 718 spin_lock_irq (& uhci-> lock); 719 if (! Test_bit (hcd_flag_hw_accessible, & HCD-> flags) 720 rc =-eshudown; 721 else if (! Uhci-> dead) 722 suspend_rh (uhci, uhci_rh_suincluded); 723 spin_unlock_irq (& uhci-> lock); 724 return RC; 725} hcd_flag_hw_accessible, indicates whether the hardware is accessible, that is, whether the hardware is down. since we have all come to suspend_rh, it is clear that this flag is set. looking back, we set it in usb_add_hcd. on the other hand, uhci-> dead is also 0, and no one has set it. in this way, we can enter suspend_rh again, but the parameters are different this time. The second parameter is uhci_rh_suspend, instead of the current uhci_rh_auto_stopped. however, we set uhci-> rh_state to uhci_rh_auto_stopped when executing suspend_rh. After entering suspend_rh again, we will find some differences. first line 1, auto_stop is no longer 1 this time. however, this time the if condition of the 274 rows is met, so we set uhci-> rh_state again and set it to the uhci_rh_suspend passed in here, And the suspend_rh function returns this result. now let's take a look at the output information of debugfs and we will find that localhost :~ # Cat/sys/kernel/debug/uhci/0000/: 00/: 1D. 0root-hub state: suspended fsbr: 0hc status USB cmd = 0048 maxp32 CF egsm USB stat = 0020 hchalted USB Int = 0002 USB frnum = (1) 050 flbaseadd = 1cac5050 sof = 40 STAT1 = 0080 stat2 = 0080 most recent frame: 4d414 (20) last ISO frame: 4d414 (20) periodic load Table 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Total: 0, # INT: 0, # ISO: 0. Nothing else has changed, but the status of the root hub has changed from running when a USB flash disk was inserted to sushortded.