/** Process the events on the port. @param HubIf The HUB that has the device connected. @param Port The port index of the hub (started with zero). @retval EFI_SUCCESS The device is enumerated (added or removed). @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device. @retval Others Failed to enumerate the device. **/ EFI_STATUS UsbEnumeratePort ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { USB_HUB_API *HubApi; USB_DEVICE *Child; EFI_USB_PORT_STATUS PortState; EFI_STATUS Status; Child = NULL; HubApi = HubIf->HubApi; // // Host learns of the new device by polling the hub for port changes. // Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: failed to get state of port %d\n", Port)); DBG("UsbEnumeratePort: failed to get state of port %d\n", Port); return Status; } // // Only handle connection/enable/overcurrent/reset change. // Usb super speed hub may report other changes, such as warm reset change. Ignore them. // if ((PortState.PortChangeStatus & (USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_OVERCURRENT | USB_PORT_STAT_C_RESET)) == 0) { return EFI_SUCCESS; } // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n", //Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf)); DBG( "UsbEnumeratePort: port %d state - %02x, change - %02x on %p\n", Port, PortState.PortStatus, PortState.PortChangeStatus, HubIf); // // This driver only process two kinds of events now: over current and // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET. // ENABLE/RESET is used to reset port. SUSPEND isn't supported. // if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) { // // Case1: // Both OverCurrent and OverCurrentChange set, means over current occurs, // which probably is caused by short circuit. It has to wait system hardware // to perform recovery. // // DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: Critical Over Current\n", Port)); DBG("UsbEnumeratePort: Critical Over Current\n", Port); return EFI_DEVICE_ERROR; } // // Case2: // Only OverCurrentChange set, means system has been recoveried from // over current. As a result, all ports are nearly power-off, so // it's necessary to detach and enumerate all ports again. // // DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port)); DBG("UsbEnumeratePort: 2.0 device Recovery Over Current\n", Port); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) { // // Case3: // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart // on 2.0 roothub does. When over-current has influence on 1.1 device, the port // would be disabled, so it's also necessary to detach and enumerate again. // // DEBUG (( EFI_D_ERROR, "UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port)); DBG("UsbEnumeratePort: 1.1 device Recovery Over Current\n", Port); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) { // // Case4: // Device connected or disconnected normally. // // DEBUG ((EFI_D_ERROR, "UsbEnumeratePort: Device Connect/Discount Normally\n", Port)); DBG("UsbEnumeratePort: Device Connect/Discount Normally\n"); } // // Following as the above cases, it's safety to remove and create again. // Child = UsbFindChild (HubIf, Port); if (Child != NULL) { // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf)); DBG("UsbEnumeratePort: device at port %d removed from root hub %p\n", Port, HubIf); UsbRemoveDevice (Child); } if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { // // Now, new device connected, enumerate and configure the device // // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: new device connected at port %d\n", Port)); DBG("UsbEnumeratePort: new device connected at port %d\n", Port); Status = UsbEnumerateNewDev (HubIf, Port); } else { // DEBUG (( EFI_D_INFO, "UsbEnumeratePort: device disconnected event on port %d\n", Port)); DBG("UsbEnumeratePort: device disconnected event on port %d status=0x%x\n", Port, PortState.PortStatus); } HubApi->ClearPortChange (HubIf, Port); DBG("ClearPortChange\n"); return Status; }
STATIC EFI_STATUS UsbEnumeratePort ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) /*++ Routine Description: Process the events on the port. Arguments: HubIf - The HUB that has the device connected Port - The port index of the hub (started with zero) Returns: EFI_SUCCESS - The device is enumerated (added or removed) EFI_OUT_OF_RESOURCES - Failed to allocate resource for the device Others - Failed to enumerate the device --*/ { USB_HUB_API *HubApi; USB_DEVICE *Child; EFI_USB_PORT_STATUS PortState; EFI_STATUS Status; Child = NULL; HubApi = HubIf->HubApi; // // Host learns of the new device by polling the hub for port changes. // Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumeratePort: failed to get state of port %d\n", Port)); return Status; } if (PortState.PortChangeStatus == 0) { return EFI_SUCCESS; } USB_DEBUG (("UsbEnumeratePort: port %d state - %x, change - %x\n", Port, PortState.PortStatus, PortState.PortChangeStatus)); // // This driver only process two kinds of events now: over current and // connect/disconnect. Other three events are: ENABLE, SUSPEND, RESET. // ENABLE/RESET is used to reset port. SUSPEND isn't supported. // if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_OVERCURRENT)) { if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_OVERCURRENT)) { // // Case1: // Both OverCurrent and OverCurrentChange set, means over current occurs, // which probably is caused by short circuit. It has to wait system hardware // to perform recovery. // USB_DEBUG (("UsbEnumeratePort: Critical Over Current\n", Port)); return EFI_DEVICE_ERROR; } // // Case2: // Only OverCurrentChange set, means system has been recoveried from // over current. As a result, all ports are nearly power-off, so // it's necessary to detach and enumerate all ports again. // USB_DEBUG (("UsbEnumeratePort: 2.0 Device Recovery Over Current\n", Port)); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_ENABLE)) { // // Case3: // 1.1 roothub port reg doesn't reflect over-current state, while its counterpart // on 2.0 roothub does. When over-current has influence on 1.1 device, the port // would be disabled, so it's also necessary to detach and enumerate again. // USB_DEBUG (("UsbEnumeratePort: 1.1 Device Recovery Over Current\n", Port)); } if (USB_BIT_IS_SET (PortState.PortChangeStatus, USB_PORT_STAT_C_CONNECTION)) { // // Case4: // Device connected or disconnected normally. // USB_DEBUG (("UsbEnumeratePort: Device Connect/Discount Normally\n", Port)); } // // Following as the above cases, it's safety to remove and create again. // Child = UsbFindChild (HubIf, Port); if (Child != NULL) { USB_DEBUG (("UsbEnumeratePort: device at port %d removed from system\n", Port)); UsbRemoveDevice (Child); } if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { // // Now, new device connected, enumerate and configure the device // USB_DEBUG (("UsbEnumeratePort: new device connected at port %d\n", Port)); Status = UsbEnumerateNewDev (HubIf, Port); } else { USB_DEBUG (("UsbEnumeratePort: device disconnected event on port %d\n", Port)); } HubApi->ClearPortChange (HubIf, Port); return Status; }
/** Enumerate and configure the new device on the port of this HUB interface. @param HubIf The HUB that has the device connected. @param Port The port index of the hub (started with zero). @retval EFI_SUCCESS The device is enumerated (added or removed). @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the device. @retval Others Failed to enumerate the device. **/ EFI_STATUS UsbEnumerateNewDev ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) { USB_BUS *Bus; USB_HUB_API *HubApi; USB_DEVICE *Child; USB_DEVICE *Parent; EFI_USB_PORT_STATUS PortState; UINTN Address; UINT8 Config; EFI_STATUS Status; Parent = HubIf->Device; Bus = Parent->Bus; HubApi = HubIf->HubApi; Address = Bus->MaxDevices; DBG("USB_WAIT_PORT_STABLE_STALL\n"); gBS->Stall (USB_WAIT_PORT_STABLE_STALL); //100ms // // Hub resets the device for at least 10 milliseconds. // Host learns device speed. If device is of low/full speed // and the hub is a EHCI root hub, ResetPort will release // the device to its companion UHCI and return an error. // Status = HubApi->ResetPort (HubIf, Port); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status)); return Status; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: hub port %d is reset\n", Port)); Child = UsbCreateDevice (HubIf, Port); if (Child == NULL) { return EFI_OUT_OF_RESOURCES; } // // OK, now identify the device speed. After reset, hub // fully knows the actual device speed. // DBG("GetPortStatus\n"); Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get speed of port %d\n", Port)); goto ON_ERROR; } if (!USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_CONNECTION)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: No device presented at port %d\n", Port)); goto ON_ERROR; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_SUPER_SPEED)){ Child->Speed = EFI_USB_SPEED_SUPER; Child->MaxPacket0 = 512; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { Child->Speed = EFI_USB_SPEED_HIGH; Child->MaxPacket0 = 64; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { Child->Speed = EFI_USB_SPEED_LOW; Child->MaxPacket0 = 8; } else { Child->Speed = EFI_USB_SPEED_FULL; Child->MaxPacket0 = 8; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); DBG("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed); if (((Child->Speed == EFI_USB_SPEED_LOW) || (Child->Speed == EFI_USB_SPEED_FULL)) && (Parent->Speed == EFI_USB_SPEED_HIGH)) { // // If the child is a low or full speed device, it is necessary to // set the transaction translator. Port TT is 1-based. // This is quite simple: // 1. if parent is of high speed, then parent is our translator // 2. otherwise use parent's translator. // Child->Translator.TranslatorHubAddress = Parent->Address; Child->Translator.TranslatorPortNumber = (UINT8) (Port + 1); } else { Child->Translator = Parent->Translator; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device uses translator (%d, %d)\n", // Child->Translator.TranslatorHubAddress, // Child->Translator.TranslatorPortNumber)); DBG("UsbEnumerateNewDev: device uses translator (%d, %d)\n", Child->Translator.TranslatorHubAddress, Child->Translator.TranslatorPortNumber); // // After port is reset, hub establishes a signal path between // the device and host (DEFALUT state). Device's registers are // reset, use default address 0 (host enumerates one device at // a time) , and ready to respond to control transfer at EP 0. // // // Host assigns an address to the device. Device completes the // status stage with default address, then switches to new address. // ADDRESS state. Address zero is reserved for root hub. // // ASSERT (Bus->MaxDevices <= 256); for (Address = 1; Address < Bus->MaxDevices; Address++) { if (Bus->Devices[Address] == NULL) { break; } } if (Address >= Bus->MaxDevices) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: address pool is full for port %d\n", Port)); DBG("UsbEnumerateNewDev: address pool is full for port %d\n", Port); Status = EFI_ACCESS_DENIED; goto ON_ERROR; } Status = UsbSetAddress (Child, (UINT8)Address); Child->Address = (UINT8)Address; Bus->Devices[Address] = Child; if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set device address - %r\n", Status)); goto ON_ERROR; } gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); // DEBUG ((EFI_D_INFO, "UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); DBG("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address); // // Host sends a Get_Descriptor request to learn the max packet // size of default pipe (only part of the device's descriptor). // Status = UsbGetMaxPacketSize0 (Child); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); DBG("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status); goto ON_ERROR; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); // // Host learns about the device's abilities by requesting device's // entire descriptions. // Status = UsbBuildDescTable (Child); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status)); DBG("UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status); goto ON_ERROR; } // // Select a default configuration: UEFI must set the configuration // before the driver can connect to the device. // Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue; Status = UsbSetConfig (Child, Config); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status)); DBG("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status); goto ON_ERROR; } // DEBUG (( EFI_D_INFO, "UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address)); DBG("UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address); // // Host assigns and loads a device driver. // Status = UsbSelectConfig (Child, Config); if (EFI_ERROR (Status)) { // DEBUG ((EFI_D_ERROR, "UsbEnumerateNewDev: failed to create interfaces - %r\n", Status)); DBG("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status); goto ON_ERROR; } // // Report Status Code to indicate USB device has been detected by hotplug // REPORT_STATUS_CODE_WITH_DEVICE_PATH ( EFI_PROGRESS_CODE, (EFI_IO_BUS_USB | EFI_IOB_PC_HOTPLUG), Bus->DevicePath ); return EFI_SUCCESS; ON_ERROR: // // If reach here, it means the enumeration process on a given port is interrupted due to error. // The s/w resources, including the assigned address(Address) and the allocated usb device data // structure(Bus->Devices[Address]), will NOT be freed here. These resources will be freed when // the device is unplugged from the port or DriverBindingStop() is invoked. // // This way is used to co-work with the lower layer EDKII UHCI/EHCI/XHCI host controller driver. // It's mainly because to keep UEFI spec unchanged EDKII XHCI driver have to maintain a state machine // to keep track of the mapping between actual address and request address. If the request address // (Address) is freed here, the Address value will be used by next enumerated device. Then EDKII XHCI // host controller driver will have wrong information, which will cause further transaction error. // // EDKII UHCI/EHCI doesn't get impacted as it's make sense to reserve s/w resource till it gets unplugged. // /* if (Address != Bus->MaxDevices) { Bus->Devices[Address] = NULL; } if (Child != NULL) { UsbFreeDevice (Child); } */ return Status; }
STATIC EFI_STATUS UsbEnumerateNewDev ( IN USB_INTERFACE *HubIf, IN UINT8 Port ) /*++ Routine Description: Enumerate and configure the new device on the port of this HUB interface. Arguments: HubIf - The HUB that has the device connected Port - The port index of the hub (started with zero) Returns: EFI_SUCCESS - The device is enumerated (added or removed) EFI_OUT_OF_RESOURCES - Failed to allocate resource for the device Others - Failed to enumerate the device --*/ { USB_BUS *Bus; USB_HUB_API *HubApi; USB_DEVICE *Child; USB_DEVICE *Parent; EFI_USB_PORT_STATUS PortState; UINT8 Address; UINT8 Config; EFI_STATUS Status; Address = USB_MAX_DEVICES; Parent = HubIf->Device; Bus = Parent->Bus; HubApi = HubIf->HubApi; gBS->Stall (USB_WAIT_PORT_STABLE_STALL); // // Hub resets the device for at least 10 milliseconds. // Host learns device speed. If device is of low/full speed // and the hub is a EHCI root hub, ResetPort will release // the device to its companion UHCI and return an error. // Status = HubApi->ResetPort (HubIf, Port); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to reset port %d - %r\n", Port, Status)); return Status; } USB_DEBUG (("UsbEnumerateNewDev: hub port %d is reset\n", Port)); Child = UsbCreateDevice (HubIf, Port); if (Child == NULL) { return EFI_OUT_OF_RESOURCES; } // // OK, now identify the device speed. After reset, hub // fully knows the actual device speed. // Status = HubApi->GetPortStatus (HubIf, Port, &PortState); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to get speed of port %d\n", Port)); goto ON_ERROR; } if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_LOW_SPEED)) { Child->Speed = EFI_USB_SPEED_LOW; } else if (USB_BIT_IS_SET (PortState.PortStatus, USB_PORT_STAT_HIGH_SPEED)) { Child->Speed = EFI_USB_SPEED_HIGH; } else { Child->Speed = EFI_USB_SPEED_FULL; } USB_DEBUG (("UsbEnumerateNewDev: device is of %d speed\n", Child->Speed)); if (Child->Speed != EFI_USB_SPEED_HIGH) { // // If the child isn't a high speed device, it is necessary to // set the transaction translator. Port TT is 1-based. // This is quite simple: // 1. if parent is of high speed, then parent is our translator // 2. otherwise use parent's translator. // if (Parent->Speed == EFI_USB_SPEED_HIGH) { Child->Translator.TranslatorHubAddress = Parent->Address; Child->Translator.TranslatorPortNumber = Port + 1; } else { Child->Translator = Parent->Translator; } USB_DEBUG (("UsbEnumerateNewDev: device uses translator (%d, %d)\n", Child->Translator.TranslatorHubAddress, Child->Translator.TranslatorPortNumber)); } // // After port is reset, hub establishes a signal path between // the device and host (DEFALUT state). Device¡¯s registers are // reset, use default address 0 (host enumerates one device at // a time) , and ready to respond to control transfer at EP 0. // // // Host sends a Get_Descriptor request to learn the max packet // size of default pipe (only part of the device¡¯s descriptor). // Status = UsbGetMaxPacketSize0 (Child); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to get max packet for EP 0 - %r\n", Status)); goto ON_ERROR; } USB_DEBUG (("UsbEnumerateNewDev: max packet size for EP 0 is %d\n", Child->MaxPacket0)); // // Host assigns an address to the device. Device completes the // status stage with default address, then switches to new address. // ADDRESS state. Address zero is reserved for root hub. // for (Address = 1; Address < USB_MAX_DEVICES; Address++) { if (Bus->Devices[Address] == NULL) { break; } } if (Address == USB_MAX_DEVICES) { USB_ERROR (("UsbEnumerateNewDev: address pool is full for port %d\n", Port)); Status = EFI_ACCESS_DENIED; goto ON_ERROR; } Bus->Devices[Address] = Child; Status = UsbSetAddress (Child, Address); Child->Address = Address; if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to set device address - %r\n", Status)); goto ON_ERROR; } gBS->Stall (USB_SET_DEVICE_ADDRESS_STALL); USB_DEBUG (("UsbEnumerateNewDev: device is now ADDRESSED at %d\n", Address)); // // Host learns about the device¡¯s abilities by requesting device's // entire descriptions. // Status = UsbBuildDescTable (Child); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to build descriptor table - %r\n", Status)); goto ON_ERROR; } // // Select a default configuration: UEFI must set the configuration // before the driver can connect to the device. // Config = Child->DevDesc->Configs[0]->Desc.ConfigurationValue; Status = UsbSetConfig (Child, Config); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to set configure %d - %r\n", Config, Status)); goto ON_ERROR; } USB_DEBUG (("UsbEnumerateNewDev: device %d is now in CONFIGED state\n", Address)); // // Host assigns and loads a device driver. // Status = UsbSelectConfig (Child, Config); if (EFI_ERROR (Status)) { USB_ERROR (("UsbEnumerateNewDev: failed to create interfaces - %r\n", Status)); goto ON_ERROR; } return EFI_SUCCESS; ON_ERROR: if (Address != USB_MAX_DEVICES) { Bus->Devices[Address] = NULL; } if (Child != NULL) { UsbFreeDevice (Child); } return Status; }