audk/EdkModulePkg/Bus/Usb/UsbBus/Dxe/usb.c

826 lines
19 KiB
C

/*++
Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
Usb.c
Abstract:
Parse usb device configurations.
Revision History
--*/
#include "usbbus.h"
//
// Here are some internal helper functions
//
STATIC
EFI_STATUS
GetExpectedDescriptor (
IN UINT8 *Buffer,
IN UINTN Length,
IN UINT8 DescType,
IN UINT8 DescLength,
OUT UINTN *ParsedBytes
);
STATIC
EFI_STATUS
ParseThisEndpoint (
IN ENDPOINT_DESC_LIST_ENTRY *EndpointEntry,
IN UINT8 *Buffer,
IN UINTN BufferLength,
OUT UINTN *ParsedBytes
);
STATIC
EFI_STATUS
ParseThisInterface (
IN INTERFACE_DESC_LIST_ENTRY *InterfaceEntry,
IN UINT8 *Buffer,
IN UINTN *BufferLen,
OUT UINTN *ParsedBytes
);
STATIC
EFI_STATUS
ParseThisConfig (
IN CONFIG_DESC_LIST_ENTRY *ConfigDescEntry,
IN UINT8 *Buffer,
IN UINTN Length
);
//
// Implementations
//
BOOLEAN
IsHub (
IN USB_IO_CONTROLLER_DEVICE *Dev
)
/*++
Routine Description:
Tell if a usb controller is a hub controller.
Arguments:
Dev - UsbIoController device structure.
Returns:
TRUE/FALSE
--*/
{
EFI_USB_INTERFACE_DESCRIPTOR Interface;
EFI_USB_IO_PROTOCOL *UsbIo;
EFI_USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
UINT8 Index;
if (Dev == NULL) {
return FALSE;
}
UsbIo = &Dev->UsbIo;
UsbIo->UsbGetInterfaceDescriptor (
UsbIo,
&Interface
);
//
// Check classcode
//
if (Interface.InterfaceClass != 0x09) {
return FALSE;
}
//
// Check protocol
//
if (Interface.InterfaceProtocol != 0x0) {
return FALSE;
}
for (Index = 0; Index < Interface.NumEndpoints; Index++) {
UsbIo->UsbGetEndpointDescriptor (
UsbIo,
Index,
&EndpointDescriptor
);
if ((EndpointDescriptor.EndpointAddress & 0x80) == 0) {
continue;
}
if (EndpointDescriptor.Attributes != 0x03) {
continue;
}
Dev->HubEndpointAddress = EndpointDescriptor.EndpointAddress;
return TRUE;
}
return FALSE;
}
EFI_STATUS
UsbGetStringtable (
IN USB_IO_DEVICE *Dev
)
/*++
Routine Description:
Get the string table stored in a usb device.
Arguments:
Dev - UsbIoController device structure.
Returns:
EFI_SUCCESS
EFI_UNSUPPORTED
EFI_OUT_OF_RESOURCES
--*/
{
EFI_STATUS Result;
UINT32 Status;
EFI_USB_SUPPORTED_LANGUAGES *LanguageTable;
UINT8 *Buffer;
UINT8 *ptr;
UINTN Index;
UINTN LangTableSize;
EFI_USB_IO_PROTOCOL *UsbIo;
UINT16 TempBuffer;
UsbIo = &(Dev->UsbController[0]->UsbIo);
//
// We get first 2 byte of langID table,
// so we can have the whole table length
//
Result = UsbGetString (
UsbIo,
0,
0,
&TempBuffer,
2,
&Status
);
if (EFI_ERROR (Result)) {
return EFI_UNSUPPORTED;
}
LanguageTable = (EFI_USB_SUPPORTED_LANGUAGES *) &TempBuffer;
if (LanguageTable->Length == 0) {
return EFI_UNSUPPORTED;
}
//
// If length is 2, then there is no string table
//
if (LanguageTable->Length == 2) {
return EFI_UNSUPPORTED;
}
Buffer = AllocateZeroPool (LanguageTable->Length);
if (Buffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Now we get the whole LangID table
//
Result = UsbGetString (
UsbIo,
0,
0,
Buffer,
LanguageTable->Length,
&Status
);
if (EFI_ERROR (Result)) {
gBS->FreePool (Buffer);
return EFI_UNSUPPORTED;
}
LanguageTable = (EFI_USB_SUPPORTED_LANGUAGES *) Buffer;
//
// ptr point to the LangID table
//
ptr = Buffer + 2;
LangTableSize = (LanguageTable->Length - 2) / 2;
for (Index = 0; Index < LangTableSize && Index < USB_MAXLANID; Index++) {
Dev->LangID[Index] = *((UINT16 *) ptr);
ptr += 2;
}
gBS->FreePool (Buffer);
LanguageTable = NULL;
return EFI_SUCCESS;
}
EFI_STATUS
UsbGetAllConfigurations (
IN USB_IO_DEVICE *UsbIoDevice
)
/*++
Routine Description:
This function is to parse all the configuration descriptor.
Arguments:
UsbIoDevice - USB_IO_DEVICE device structure.
Returns:
EFI_SUCCESS
EFI_DEVICE_ERROR
EFI_OUT_OF_RESOURCES
--*/
{
EFI_STATUS Result;
UINT32 Status;
UINTN Index;
UINTN TotalLength;
UINT8 *Buffer;
CONFIG_DESC_LIST_ENTRY *ConfigDescEntry;
EFI_USB_IO_PROTOCOL *UsbIo;
InitializeListHead (&UsbIoDevice->ConfigDescListHead);
UsbIo = &(UsbIoDevice->UsbController[0]->UsbIo);
for (Index = 0; Index < UsbIoDevice->DeviceDescriptor.NumConfigurations; Index++) {
ConfigDescEntry = NULL;
ConfigDescEntry = AllocateZeroPool (sizeof (CONFIG_DESC_LIST_ENTRY));
if (ConfigDescEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// 1st only get 1st 4 bytes config descriptor,
// so we can know the whole length
//
Result = UsbGetDescriptor (
UsbIo,
(UINT16) ((USB_DT_CONFIG << 8) | Index),
0,
4,
&ConfigDescEntry->CongfigDescriptor,
&Status
);
if (EFI_ERROR (Result)) {
DEBUG ((gUSBErrorLevel, "First get config descriptor error\n"));
gBS->FreePool (ConfigDescEntry);
return EFI_DEVICE_ERROR;
}
TotalLength = ConfigDescEntry->CongfigDescriptor.TotalLength;
Buffer = AllocateZeroPool (TotalLength);
if (Buffer == NULL) {
gBS->FreePool (ConfigDescEntry);
return EFI_OUT_OF_RESOURCES;
}
//
// Then we get the total descriptors for this configuration
//
Result = UsbGetDescriptor (
UsbIo,
(UINT16) ((USB_DT_CONFIG << 8) | Index),
0,
(UINT16) TotalLength,
Buffer,
&Status
);
if (EFI_ERROR (Result)) {
DEBUG ((gUSBErrorLevel, "Get whole config descriptor error\n"));
gBS->FreePool (ConfigDescEntry);
gBS->FreePool (Buffer);
return EFI_DEVICE_ERROR;
}
InitializeListHead (&ConfigDescEntry->InterfaceDescListHead);
//
// Parse this whole configuration
//
Result = ParseThisConfig (ConfigDescEntry, Buffer, TotalLength);
if (EFI_ERROR (Result)) {
//
// Ignore this configuration, parse next one
//
gBS->FreePool (ConfigDescEntry);
gBS->FreePool (Buffer);
continue;
}
InsertTailList (&UsbIoDevice->ConfigDescListHead, &ConfigDescEntry->Link);
gBS->FreePool (Buffer);
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
GetExpectedDescriptor (
IN UINT8 *Buffer,
IN UINTN Length,
IN UINT8 DescType,
IN UINT8 DescLength,
OUT UINTN *ParsedBytes
)
/*++
Routine Description:
Get the start position of next wanted descriptor.
Arguments:
Buffer - Buffer to parse
Length - Buffer length
DescType - Descriptor type
DescLength - Descriptor length
ParsedBytes - Parsed Bytes to return
Returns:
EFI_SUCCESS
EFI_DEVICE_ERROR
--*/
{
UINT16 DescriptorHeader;
UINT8 Len;
UINT8 *ptr;
UINTN Parsed;
Parsed = 0;
ptr = Buffer;
while (TRUE) {
//
// Buffer length should not less than Desc length
//
if (Length < DescLength) {
return EFI_DEVICE_ERROR;
}
//
// DescriptorHeader = *((UINT16 *)ptr), compatible with IPF
//
DescriptorHeader = (UINT16) ((*(ptr + 1) << 8) | *ptr);
Len = ptr[0];
//
// Check to see if it is a start of expected descriptor
//
if (DescriptorHeader == ((DescType << 8) | DescLength)) {
break;
}
if ((UINT8) (DescriptorHeader >> 8) == DescType) {
if (Len > DescLength) {
return EFI_DEVICE_ERROR;
}
}
//
// Descriptor length should be at least 2
// and should not exceed the buffer length
//
if (Len < 2) {
return EFI_DEVICE_ERROR;
}
if (Len > Length) {
return EFI_DEVICE_ERROR;
}
//
// Skip this mismatch descriptor
//
Length -= Len;
ptr += Len;
Parsed += Len;
}
*ParsedBytes = Parsed;
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
ParseThisEndpoint (
IN ENDPOINT_DESC_LIST_ENTRY *EndpointEntry,
IN UINT8 *Buffer,
IN UINTN BufferLength,
OUT UINTN *ParsedBytes
)
/*++
Routine Description:
Get the start position of next wanted endpoint descriptor.
Arguments:
EndpointEntry - ENDPOINT_DESC_LIST_ENTRY
Buffer - Buffer to parse
BufferLength - Buffer Length
ParsedBytes - Parsed Bytes to return
Returns:
EFI_SUCCESS
EFI_DEVICE_ERROR
--*/
{
UINT8 *ptr;
EFI_STATUS Status;
UINTN SkipBytes;
//
// Skip some data for this interface
//
Status = GetExpectedDescriptor (
Buffer,
BufferLength,
USB_DT_ENDPOINT,
sizeof (EFI_USB_ENDPOINT_DESCRIPTOR),
&SkipBytes
);
if (EFI_ERROR (Status)) {
return Status;
}
ptr = Buffer + SkipBytes;
*ParsedBytes = SkipBytes;
CopyMem (
&EndpointEntry->EndpointDescriptor,
ptr,
sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
);
*ParsedBytes += sizeof (EFI_USB_ENDPOINT_DESCRIPTOR);
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
ParseThisInterface (
IN INTERFACE_DESC_LIST_ENTRY *InterfaceEntry,
IN UINT8 *Buffer,
IN UINTN *BufferLen,
OUT UINTN *ParsedBytes
)
/*++
Routine Description:
Get the start position of next wanted interface descriptor.
Arguments:
InterfaceEntry - INTERFACE_DESC_LIST_ENTRY
Buffer - Buffer to parse
BufferLength - Buffer Length
ParsedBytes - Parsed Bytes to return
Returns:
EFI_SUCCESS
EFI_DEVICE_ERROR
--*/
{
UINT8 *ptr;
UINTN SkipBytes;
UINTN Index;
UINTN Length;
UINTN Parsed;
ENDPOINT_DESC_LIST_ENTRY *EndpointEntry;
EFI_STATUS Status;
Parsed = 0;
//
// Skip some data for this interface
//
Status = GetExpectedDescriptor (
Buffer,
*BufferLen,
USB_DT_INTERFACE,
sizeof (EFI_USB_INTERFACE_DESCRIPTOR),
&SkipBytes
);
if (EFI_ERROR (Status)) {
return Status;
}
ptr = Buffer + SkipBytes;
*ParsedBytes = SkipBytes;
//
// Copy the interface descriptor
//
CopyMem (
&InterfaceEntry->InterfaceDescriptor,
ptr,
sizeof (EFI_USB_INTERFACE_DESCRIPTOR)
);
ptr = Buffer + sizeof (EFI_USB_INTERFACE_DESCRIPTOR);
*ParsedBytes += sizeof (EFI_USB_INTERFACE_DESCRIPTOR);
InitializeListHead (&InterfaceEntry->EndpointDescListHead);
Length = *BufferLen - SkipBytes - sizeof (EFI_USB_INTERFACE_DESCRIPTOR);
for (Index = 0; Index < InterfaceEntry->InterfaceDescriptor.NumEndpoints; Index++) {
EndpointEntry = AllocateZeroPool (sizeof (ENDPOINT_DESC_LIST_ENTRY));
if (EndpointEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Parses all the endpoint descriptors within this interface.
//
Status = ParseThisEndpoint (EndpointEntry, ptr, Length, &Parsed);
if (EFI_ERROR (Status)) {
gBS->FreePool (EndpointEntry);
return Status;
}
InsertTailList (
&InterfaceEntry->EndpointDescListHead,
&EndpointEntry->Link
);
Length -= Parsed;
ptr += Parsed;
*ParsedBytes += Parsed;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
ParseThisConfig (
IN CONFIG_DESC_LIST_ENTRY *ConfigDescEntry,
IN UINT8 *Buffer,
IN UINTN Length
)
/*++
Routine Description:
Parse the current configuration descriptior.
Arguments:
ConfigDescEntry - CONFIG_DESC_LIST_ENTRY
Buffer - Buffer to parse
Length - Buffer Length
Returns
EFI_SUCCESS
EFI_DEVICE_ERROR
--*/
{
UINT8 *ptr;
UINT8 NumInterface;
UINTN Index;
INTERFACE_DESC_LIST_ENTRY *InterfaceEntry;
UINTN SkipBytes;
UINTN Parsed;
EFI_STATUS Status;
UINTN LengthLeft;
Parsed = 0;
//
// First skip the current config descriptor;
//
Status = GetExpectedDescriptor (
Buffer,
Length,
USB_DT_CONFIG,
sizeof (EFI_USB_CONFIG_DESCRIPTOR),
&SkipBytes
);
if (EFI_ERROR (Status)) {
return Status;
}
ptr = Buffer + SkipBytes;
CopyMem (
&ConfigDescEntry->CongfigDescriptor,
ptr,
sizeof (EFI_USB_CONFIG_DESCRIPTOR)
);
NumInterface = ConfigDescEntry->CongfigDescriptor.NumInterfaces;
//
// Skip size of Configuration Descriptor
//
ptr += sizeof (EFI_USB_CONFIG_DESCRIPTOR);
LengthLeft = Length - SkipBytes - sizeof (EFI_USB_CONFIG_DESCRIPTOR);
for (Index = 0; Index < NumInterface; Index++) {
//
// Parse all Interface
//
InterfaceEntry = AllocateZeroPool (sizeof (INTERFACE_DESC_LIST_ENTRY));
if (InterfaceEntry == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Status = ParseThisInterface (InterfaceEntry, ptr, &LengthLeft, &Parsed);
if (EFI_ERROR (Status)) {
gBS->FreePool (InterfaceEntry);
return Status;
}
InsertTailList (
&ConfigDescEntry->InterfaceDescListHead,
&InterfaceEntry->Link
);
//
// Parsed for next interface
//
LengthLeft -= Parsed;
ptr += Parsed;
}
//
// Parse for additional alt setting;
//
return EFI_SUCCESS;
}
EFI_STATUS
UsbSetConfiguration (
IN USB_IO_DEVICE *UsbIoDev,
IN UINTN ConfigurationValue
)
/*++
Routine Description:
Set the device to a configuration value.
Arguments:
UsbIoDev - USB_IO_DEVICE to be set configuration
ConfigrationValue - The configuration value to be set to that device
Returns:
EFI_SUCCESS
EFI_DEVICE_ERROR
--*/
{
LIST_ENTRY *NextEntry;
CONFIG_DESC_LIST_ENTRY *ConfigEntry;
UINT32 Status;
EFI_STATUS Result;
EFI_USB_IO_PROTOCOL *UsbIo;
UsbIo = &(UsbIoDev->UsbController[0]->UsbIo);
NextEntry = UsbIoDev->ConfigDescListHead.ForwardLink;
while (NextEntry != &UsbIoDev->ConfigDescListHead) {
//
// Get one entry
//
ConfigEntry = (CONFIG_DESC_LIST_ENTRY *) NextEntry;
if (ConfigEntry->CongfigDescriptor.ConfigurationValue == ConfigurationValue) {
//
// Find one, set to the active configuration
//
UsbIoDev->ActiveConfig = ConfigEntry;
break;
}
NextEntry = NextEntry->ForwardLink;
}
//
// Next Entry should not be null
//
Result = UsbSetDeviceConfiguration (
UsbIo,
(UINT16) ConfigurationValue,
&Status
);
return Result;
}
EFI_STATUS
UsbSetDefaultConfiguration (
IN USB_IO_DEVICE *UsbIoDev
)
/*++
Routine Description:
Set the device to a default configuration value.
Arguments:
UsbIoDev - USB_IO_DEVICE to be set configuration
Returns
EFI_SUCCESS
EFI_DEVICE_ERROR
--*/
{
CONFIG_DESC_LIST_ENTRY *ConfigEntry;
UINT16 ConfigValue;
LIST_ENTRY *NextEntry;
if (IsListEmpty (&UsbIoDev->ConfigDescListHead)) {
return EFI_DEVICE_ERROR;
}
NextEntry = UsbIoDev->ConfigDescListHead.ForwardLink;
ConfigEntry = (CONFIG_DESC_LIST_ENTRY *) NextEntry;
ConfigValue = ConfigEntry->CongfigDescriptor.ConfigurationValue;
return UsbSetConfiguration (UsbIoDev, ConfigValue);
}
VOID
UsbDestroyAllConfiguration (
IN USB_IO_DEVICE *UsbIoDevice
)
/*++
Routine Description:
Delete all configuration data when device is not used.
Arguments:
UsbIoDevice - USB_IO_DEVICE to be set configuration
Returns:
N/A
--*/
{
CONFIG_DESC_LIST_ENTRY *ConfigEntry;
INTERFACE_DESC_LIST_ENTRY *InterfaceEntry;
ENDPOINT_DESC_LIST_ENTRY *EndpointEntry;
LIST_ENTRY *NextEntry;
//
// Delete all configuration descriptor data
//
ConfigEntry = (CONFIG_DESC_LIST_ENTRY *) UsbIoDevice->ConfigDescListHead.ForwardLink;
while (ConfigEntry != (CONFIG_DESC_LIST_ENTRY *) &UsbIoDevice->ConfigDescListHead) {
//
// Delete all its interface descriptors
//
InterfaceEntry = (INTERFACE_DESC_LIST_ENTRY *) ConfigEntry->InterfaceDescListHead.ForwardLink;
while (InterfaceEntry != (INTERFACE_DESC_LIST_ENTRY *) &ConfigEntry->InterfaceDescListHead) {
//
// Delete all its endpoint descriptors
//
EndpointEntry = (ENDPOINT_DESC_LIST_ENTRY *) InterfaceEntry->EndpointDescListHead.ForwardLink;
while (EndpointEntry != (ENDPOINT_DESC_LIST_ENTRY *) &InterfaceEntry->EndpointDescListHead) {
NextEntry = ((LIST_ENTRY *) EndpointEntry)->ForwardLink;
RemoveEntryList ((LIST_ENTRY *) EndpointEntry);
gBS->FreePool (EndpointEntry);
EndpointEntry = (ENDPOINT_DESC_LIST_ENTRY *) NextEntry;
}
NextEntry = ((LIST_ENTRY *) InterfaceEntry)->ForwardLink;
RemoveEntryList ((LIST_ENTRY *) InterfaceEntry);
gBS->FreePool (InterfaceEntry);
InterfaceEntry = (INTERFACE_DESC_LIST_ENTRY *) NextEntry;
}
NextEntry = ((LIST_ENTRY *) ConfigEntry)->ForwardLink;
RemoveEntryList ((LIST_ENTRY *) ConfigEntry);
gBS->FreePool (ConfigEntry);
ConfigEntry = (CONFIG_DESC_LIST_ENTRY *) NextEntry;
}
}