# Pass the first enumeration

Certain events that can occur while using the USB (such as a successful enumeration or an error) are important for the later application. Therefore, the first step is to define a structure that holds the callbacks for certain events:

```
typedef struct
{
	void (*ConfigurationChanged)(const uint8_t Configuration);
	void (*ControlRequest)(const uint8_t bRequest, const uint8_t bmRequestType, const uint16_t wValue);
	void (*Error)();
} USB_DeviceCallbacks_t;
```

These callbacks are now declared in the main program and the structure is created:

```
void USB_Event_OnError(void);
void USB_Event_ConfigurationChanged(const uint8_t Configuration);
const USB_DeviceCallbacks_t Events_USB =
{
	.Error = USB_Event_OnError,
	.ConfigurationChanged = USB_Event_ConfigurationChanged,
};
```

Next the *USB\_Init* function will be updated to save the callbacks:

```
extern USB_DeviceCallbacks_t _USBEvents;
void USB_Init(const USB_DeviceCallbacks_t* Events)
{
	USBController_Init(USB_MODE_DEVICE, USB_SPEED_LOW);
	_USBEvents = *Events;
	_DeviceState = USB_STATE_RESET;
}
```

Now the USB interrupt is adapted to call the error callback if the configuration of the control endpoint fails:

```
ISR(USB_GEN_vect)
{
	...
	if(USBController_CheckForInterrupt(USB_EOR_INTERRUPT))
	{
		USBController_ClearInterruptFlag(USB_EOR_INTERRUPT);
		_DeviceState = USB_STATE_RESET;
		if(Endpoint_Configure(0, ENDPOINT_TYPE_CONTROL, Endpoint_ControlSize, 0))
		{
			PORTD &= ~(0x01 << 0x04);
			PORTD |= (0x01 << 0x05);
		}
		else
		{
			if(_USBEvents.Error != NULL)
			{
				_USBEvents.Error();
			}
		}
	}
}
```

{% hint style="info" %}
The variable *\_USBEvents* is available externally for other source files, thanks to the **extern** keyword. So the callbacks can be used anywhere in the driver code.
{% endhint %}

With USB, every communication host is initiated by, i.e. USB devices are not able to send data independently. The USB host must therefore periodically poll the individual bus participants for new data (this method is also called pollen) or use interrupts (not used in this example).&#x20;

The query should take place in the main function of the application:

```
int main(void)
{
	USB_Init(&Events_USB);
	sei();
	while(1) 
	{
	    USB_Poll();
	}
}
```

The configuration or parameterization of the USB device by the host is carried out using so-called standard requests, which are sent via *SETUP* packets.&#x20;

{% embed url="<http://www.usbmadesimple.co.uk/ums_4.htm>" %}

The host can use these requests to query certain information (such as the descriptors) from the device or to make certain configurations on the device.

The *USB\_Poll* function uses the state machine to check whether the microcontroller is still connected to the USB.

```
void USB_Poll(void)
{
	if(_DeviceState == USB_STATE_UNATTACHED)
	{
		return;
	}
	uint8_t CurrEndpoint = UENUM;
	UENUM = ENDPOINT_CONTROL_ADDRESS;
	if(UEINTX & (0x01 << RXSTPI))
	{
		USBDevice_ControlRequest();
	}
	UENUM = CurrEndpoint;
}
```

When the microcontroller is connected to the bus, the currently active endpoint is read from the *UENUM* register and saved, switched to the control endpoint and checked whether a *SETUP* packet has been received by querying the *RXSTPI* bit in the *UEINTX* register of the endpoint becomes. If no *SETUP* packet has been received, the software switches back to the previous end point and exits the function.

Otherwise, the software jumps to the *USBDevice\_ControlRequest* function, in which the received *SETUP* package is read in and processed further. In this function, the received requests are evaluated. For the sake of simplicity, I limit the number of evaluated requests in this simple example to the minimum required requests:

* *SET\_ADDRESS* – Is required to set the address that the host assigns during the enumeration
* *GET\_DESCRIPTOR* – Is needed to send the different descriptors to the host
* *SET\_CONFIGURATION* – Is required to recognize the end of the enumeration

All communication takes place via the *IN* and *OUT* control end points. Data is thus read (e.g. the address that the host sent to the device) and data is written (e.g. the descriptors that the device sends to the host). The sequence of a write or read process is described in the data sheet of the USB device controller.

![](https://2801891837-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvA_nizlqrtUSmfwyso%2F-Ly-w39ygNI8_grrVac_%2F-Ly-xe0BoTtCHvmtB85J%2Fimage.png?alt=media\&token=5c82ae90-637f-40a8-adec-e151b00801fd)

At the beginning of each control request, the *RXSTPI* bit is set by the USB controller. This signals the CPU that a new *SETUP* package has arrived and can be read out:

```
uint8_t* RequestHeader = (uint8_t*)& _ControlRequest;
for(uint8_t i = 0x00; i < sizeof(USB_SetupPacket_t); i++)
{
	*(RequestHeader++) = UEDATX;
}
```

For this purpose, a pointer to the static variable *\_ControlRequest* is created and then the individual data bytes are read from the control endpoint and written to the memory of the variable *\_ControlRequest*.

{% hint style="info" %}
So that the structure is filled correctly, it must be declared with the attribute **\_\_attribute\_\_((packed))**. Otherwise, the compiler may add additional bytes for a padding, which means that the data is not saved correctly when iterating over the memory area using a pointer to the structure.
{% endhint %}

After the data has been read, the *RXSTPI* bit must be cleared:

```
UEINTX &= ~(0x01 << RXSTPI);
```

Deleting the *RXSTPI* bit signals the end of the *SETUP* stage and the system switches to the next stage. Depending on the type of request, the next stage is the *DATA* or *STATUS* stage. The type of request can be determined using the *bRequest* field of the *\_ControlRequest* structure and queried using a switch statement:

```
void USBDevice_ControlRequest(void)
{
	uint8_t* RequestHeader = (uint8_t*)& _ControlRequest;
	for(uint8_t i = 0x00; i < sizeof(USB_SetupPacket_t); i++)
	{
		*(RequestHeader++) = UEDATX;
	}
	UEINTX &= ~(0x01 << RXSTPI);
	switch(_ControlRequest.bRequest)
	{
		case REQUEST_SET_ADDRESS:
		{
			break;
		}
		case REQUEST_GET_DESCRIPTOR:
		{
			break;
		}
		case REQUEST_SET_CONFIGURATION:
		{
			break;	
		}
	}
}
```

At the beginning, the USB host sends a *GET\_DESCRIPTOR* request to the connected device so that the host can read in the different descriptors to obtain information about the size of the control endpoint.

{% hint style="info" %}
It makes sense to evaluate the field *bmRequestType* of the request, in addition to the standard requests, class-specific requests can also be used, whereby the different USB classes extend the function of a request with class-specific functions (e.g. the HID class).
{% endhint %}

In the next step, the field bmRequestType will be evaluated, which is filled with **0x80** for a standard request *GET\_DESCRIPTOR* request. The following information results from this value:

* Device to host (*IN*-Transfer)
* Standard request
* Recipient: Device

```
if(_ControlRequest.bmRequestType == (REQUEST_DIRECTION_DEVICE_TO_HOST | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
{
	...
}
```

The following functions are defined for the other fields of the request:

| **Field** | **Description**                                 |
| --------- | ----------------------------------------------- |
| *wValue*  | Descriptor type and index                       |
| *wIndex*  | Null oder language id (for a string descriptor) |
| *wLength* | Descriptor size                                 |
| *Data*    | Descriptor data                                 |

Now a function is needed to load the corresponding descriptor from the program memory based on the parameters *wValue* and *wIndex*. In the second step, *wLength* data bytes of the descriptor must be transmitted to the host. The function for loading the descriptors can e.g. look like this:

```
const void* USB_GetDescriptor(const uint16_t wValue, const uint16_t wIndex, uint16_t* Size)
{
	uint8_t DescriptorType = (wValue >> 0x08);
	uint8_t DescriptorNumber = (wValue & 0xFF);
	switch(DescriptorType)
	{
		case DESCRIPTOR_TYPE_DEVICE:
		{
			*Size = sizeof(USB_DeviceDescriptor_t);
			return &DeviceDescriptor;
		}
		case DESCRIPTOR_TYPE_CONFIGURATION:
		{
			*Size = sizeof(USB_Configuration_t);
			return &ConfigurationDescriptor;
		}
		case DESCRIPTOR_TYPE_STRING:
		{
			switch(DescriptorNumber)
			{
				case STRING_ID_LANGUAGE:
				{
					*Size = pgm_read_byte(&LANGID.bLength);
					return &LANGID;
				}
				case STRING_ID_MANUFACTURER:
				{
					*Size = pgm_read_byte(&ManufacturerString.bLength);
					return &ManufacturerString;
				}
				case STRING_ID_PRODUCT:
				{
					*Size = pgm_read_byte(&ProductString.bLength);
					return &ProductString;
				}
				case STRING_ID_SERIAL:
				{
					*Size = pgm_read_byte(&SerialString.bLength);
					return &SerialString;
				}
			}
		}
	}
	*Size = 0x00;
	return NULL;
}
```

The *USB\_GetDescriptor* function expects the values ​​of *wValue* and *wIndex* from the host's control request as a parameter, as well as a pointer to a memory area for the length of the descriptor, and returns a pointer to the memory area of ​​the descriptor. To do this, the type and index of the descriptor are first extracted from the parameter *wValue* and then the address and the size of the descriptor are determined using a switch statement.

{% hint style="info" %}
The length of he device and configuration descriptors can be determined by using the **sizeof()** function and the structure of the descriptor. This does not work with the string descriptor, because **sizeof()** only returns the size of the pointer to the first element for an array. In this case, the size of the descriptor must therefore be read from the *bLength* field of the descriptor in the program memory using **pgm\_read\_byte**.
{% endhint %}

The created function is called when processing the *GET\_DESCRIPTOR* request and the received parameters for *wValue* and *wIndex* are passed:

```
if(_ControlRequest.bmRequestType == (REQUEST_DIRECTION_DEVICE_TO_HOST | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
{
	const void* Descriptor;
	uint16_t DescriptorSize;
	Descriptor = USB_GetDescriptor(_ControlRequest.wValue, _ControlRequest.wIndex, &DescriptorSize);
	if((DescriptorSize == 0x00) && (_USBEvents.Error != NULL))
	{
		_USBEvents.Error();
	}
```

Now the descriptor has to be sent to the host. It should be noted that the host uses the *wLength* parameter to specify how many data bytes it wants to read from the descriptor. The function for sending the data looks like this:

```
Endpoint_CS_State_t USB_DeviceStream_ControlIN(const void* Buffer, const uint16_t Length, const uint16_t RequestedLength)
{
	uint8_t* Buffer_Temp = (uint8_t*)Buffer;
	uint16_t Length_Temp = Length;
	uint8_t LastPacketFull = 0x00;
	if(Length > RequestedLength)
	{
		Length_Temp = RequestedLength;
	}
	while(Length_Temp)
	{
		Endpoint_CS_State_t State = USB_DeviceStream_GetControlEndpointState();
		if(State != ENDPOINT_CS_NO_ERROR)
		{
			return State;
		}
		else if(UEINTX & (0x01 << RXOUTI))
		{
			break;
		}
		if(UEINTX & (0x01 << TXINI))
		{
			while(Length_Temp && (UEBCX < Endpoint_ControlSize))
			{
				UEDATX = pgm_read_byte(Buffer_Temp++);
				Length_Temp--;
			}
			UEINTX &= ~((0x01 << TXINI) | (0x01 << FIFOCON));
		}
	}
	while(!(UEINTX & (0x01 << TXINI)));
	UEINTX &= ~((0x01 << TXINI) | (0x01 << FIFOCON));
	while(!(UEINTX & (0x01 << RXOUTI)))
	{
		Endpoint_CS_State_t State = USBStream_GetControlEndpointState();
		if(State != ENDPOINT_CS_NO_ERROR)
		{
			return State;
		}
	}
	UEINTX &= ~((0x01 << RXOUTI) | (0x01 << FIFOCON));
	return ENDPOINT_CS_NO_ERROR;
}
```

At the beginning, the function checks whether the message to be transmitted is longer than the number of data bytes that the host requested. The data is then transferred in the subsequent while loop. First, the status of the control endpoint is checked:

```
static Endpoint_CS_State_t USB_DeviceStream_GetControlEndpointState(void)
{
	if(_DeviceState == USB_STATE_UNATTACHED)
	{
		return ENDPOINT_CS_DISCONNECT;
	}
	else if(__DeviceState == USB_STATE_SUSPEND)
	{
		return ENDPOINT_CS_SUSPEND;
	}
	else if(UEINTX & (0x01 << RXSTPI))
	{
		return ENDPOINT_CS_ABORT_FROM_HOST;
	}
	
	return ENDPOINT_CS_NO_ERROR;
}
...
Endpoint_CS_State_t State = USB_DeviceStream_GetControlEndpointState();
if(State != ENDPOINT_CS_NO_ERROR)
{
	return State;
}
else if(UEINTX & (0x01 << RXOUTI))
{
	break;
}
```

If the status of the USB device changes, or the host has written a new *SETUP* or data packet to the *OUT* endpoint, the transfer stops and a corresponding error is returned.

The data transmission is then implemented in accordance with the flow diagram shown. First, the *TXINI* bit is used to check whether the *IN* endpoint has already received a new *IN* transaction, i.e. whether the *DATA* stage has started.

![](https://www.kampis-elektroecke.de/wp-content/uploads/2019/12/USB_Device_Request2.gif)

If a corresponding *IN* transaction was recognized, the endpoint is filled with data. As soon as the endpoint is filled with data, a transfer towards the host is initiated and the end point is emptied and waited for the end of the transfer:

```
if(UEINTX & (0x01 << TXINI))
{
	while(Length_Temp && (UEBCX < ENDPOINT_CONTROL_SIZE))
	{
		UEDATX = pgm_read_byte(Buffer_Temp++);
		Length_Temp--;
	}
	UEINTX &= ~((0x01 << TXINI) | (0x01 << FIFOCON));
	while(!(UEINTX & (0x01 << TXINI));
}
```

As soon as all data has been transferred, the *STATUS* stage begins. The first command of the *STATUS* stage is always acknowledged by the USB controller with a NAK. The data sheet specifies four steps that must be processed in this case:

* Set *transmit ready*
* Wait for *transmit* or *receive complete*
* If *receive complete*: Clear flag and exit
* If *transmit complete*: Continue

This procedure must now be implemented in the program, whereby in this case the evaluation of *Transmit Complete* can be omitted:

```
UEINTX &= ~((0x01 << TXINI) | (0x01 << FIFOCON));
while(!(UEINTX & (0x01 << RXOUTI)))
{
	Endpoint_CS_State_t State = USB_DeviceStream_GetControlEndpointState();
	if(State != ENDPOINT_CS_NO_ERROR)
	{
		return State;
	}
}
UEINTX &= ~((0x01 << RXOUTI) | (0x01 << FIFOCON));
```

While the software is waiting for the *Receive Complete*, the status of the endpoint is constantly checked and an error returned if necessary. The finished function can now be built into the processing of the *GET\_DESCRIPTOR* request:

```
case REQUEST_GET_DESCRIPTOR:
{
	if(__ControlRequest.bmRequestType == (REQUEST_DIRECTION_DEVICE_TO_HOST | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
	{
		const void* Descriptor;
		uint16_t DescriptorSize;
		Descriptor = USB_GetDescriptor(__ControlRequest.wValue, __ControlRequest.wIndex, &DescriptorSize);
		if((DescriptorSize == 0x00) && (__USBEvents.Error != NULL))
		{
			__USBEvents.Error();
		}
		USB_DeviceStream_ControlIN(Descriptor, DescriptorSize, __ControlRequest.wLength);
	}
	break;
}
```

As soon as the host has received and evaluated the device descriptor, it defines an address for the USB device and transmits it with a *SET\_ADDRESS* request. With this request the bit pattern for the field *bmRequestType* **0x00**, or

* Host to device (*OUT*-Transfer)
* Standard request
* Recipient: Device

The host sends the assigned device address to the connected device via the *wValue* field. All other fields are filled with zero or do not exist. The transmitted address must be read out by the CPU and copied into the *UADD* register. This is done in the following steps:

1. Read the address from the message
2. Copy the address into the *UADD* register, but keep *ADDEN* cleared
3. Transmit a IN message with a length of **0** bytes
4. Set *ADDEN*

```
case REQUEST_SET_ADDRESS:
{
	if(__ControlRequest.bmRequestType == (REQUEST_DIRECTION_HOST_TO_DEVICE | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
	{
		uint8_t Address = (__ControlRequest.wValue & 0x7F);
		UDADDR = (Address & 0x7F);
		Endpoint_HandleSTATUS(__ControlRequest.bmRequestType);
		UDADDR |= (0x01 << ADDEN);
		__DeviceState = USB_STATE_ADDRESSED;
	}
}
```

The *Endpoint\_HandleSTATUS* function takes over the *STATUS* stage of communication either by sending an *IN* packet with a data length of 0 bytes (if it was an *OUT* request) or by waiting for an *OUT* packet and including it (if it is an *IN* request):

```
void Endpoint_HandleSTATUS(const USB_RequestDirection_t Direction)
{
	if(Direction & REQUEST_DIRECTION_DEVICE_TO_HOST)
	{
		while(!(Endpoint_OUTReceived()))
		{
			if(__DeviceState == USB_STATE_UNATTACHED)
			{
				return;
			}
		}
		Endpoint_AckOUT();
	}
	else
	{
		Endpoint_FlushIN();
		while(!(Endpoint_INReady()))
		{
			if(__DeviceState == USB_STATE_UNATTACHED)
			{
				return;
			}
		}
	}
}
```

So that the USB controller is not permanently stuck in the loop in the event of a disconnection from the bus, the status of the machine is also checked and the loop is exited if necessary. Finally, the status of the machine is set to *USB\_STATE\_ADDRESSED*, which means that this request has also been processed successfully.

The last necessary request is the *SET\_CONFIGURATION* request, in which the host selects a device configuration and activates the selected configuration in the USB device. Here the bit pattern for the field *bmRequestType* is **0x00**, or

* Host to device (*OUT*-Transfer)
* Standard request
* Recipient: Device

As with the *SET\_ADDRESS* request, this request only has the *wValue* field as an information carrier, which is filled with the index of the configuration selected by the host.

{% hint style="info" %}
The host selects the configuration using the *bConfigurationValue* field in the configuration descriptor. This field must not be zero, since the device must then change back to the state after addressing in accordance with the specification.
{% endhint %}

As soon as the configuration index has been received, the CPU should also check whether a corresponding configuration is available. For this purpose, the corresponding configuration descriptor is read out with the help of the *USB\_GetDescriptor* function and it is checked whether it is present.

```
case REQUEST_SET_CONFIGURATION:
{
	if(__ControlRequest.bmRequestType == (REQUEST_DIRECTION_HOST_TO_DEVICE | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
	{
		__Configuration = (uint8_t)__ControlRequest.wValue & 0xFF;
		if(__Configuration > 0x00)
		{
			USB_DeviceDescriptor_t* Descriptor;
			Endpoint_HandleSTATUS(__ControlRequest.bmRequestType);
			if((USB_GetDescriptor((DESCRIPTOR_TYPE_CONFIGURATION << 0x08) | (__Configuration - 0x01), __ControlRequest.wIndex, (void*)&Descriptor) == 0x00) && (__USBEvents.Error != NULL))
			{
				__USBEvents.Error();
			}
			else
			{
				__DeviceState = USB_STATE_CONFIGURED;
				if(__USBEvents.ConfigurationChanged != NULL)
				{
					__USBEvents.ConfigurationChanged(__Configuration);
				}
			}
		}
		else
		{
			__DeviceState = USB_STATE_CONFIGURED;
		}
	}
}
```

When the configuration has been successfully completed, the device is ready for use. The *ConfigurationChanged* event is called. This event can be used by the application to e.g. configure additional endpoints.

The complete function *USBDevice\_ControlRequest* now looks like this:

```
void USBDevice_ControlRequest(void)
{
	uint8_t* RequestHeader = (uint8_t*)&__ControlRequest;
	for(uint8_t i = 0x00; i < sizeof(USB_SetupPacket_t); i++)
	{
		*(RequestHeader++) = UEDATX;
	}
	UEINTX &= ~(0x01 << RXSTPI);
	switch(__ControlRequest.bRequest)
	{
		case REQUEST_SET_ADDRESS:
		{
			if(__ControlRequest.bmRequestType == (REQUEST_DIRECTION_HOST_TO_DEVICE | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
			{
				uint8_t Address = (__ControlRequest.wValue & 0x7F);
				UDADDR = (Address & 0x7F);
				Endpoint_HandleSTATUS(__ControlRequest.bmRequestType);
				UDADDR |= (0x01 << ADDEN);
				__DeviceState = USB_STATE_ADDRESSED;
			}
			break;
		}
		case REQUEST_GET_DESCRIPTOR:
		{
			if(__ControlRequest.bmRequestType == (REQUEST_DIRECTION_DEVICE_TO_HOST | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
			{
				const void* Descriptor;
				uint16_t DescriptorSize;
				Descriptor = USB_GetDescriptor(__ControlRequest.wValue, __ControlRequest.wIndex, &DescriptorSize);
				if((DescriptorSize == 0x00) && (__USBEvents.Error != NULL))
				{
					__USBEvents.Error();
				}
				USB_DeviceStream_ControlIN(Descriptor, DescriptorSize, __ControlRequest.wLength);
			}
			break;
		}
		case REQUEST_SET_CONFIGURATION:
		{
			if(__ControlRequest.bmRequestType == (REQUEST_DIRECTION_HOST_TO_DEVICE | REQUEST_TYPE_STANDARD | REQUEST_RECIPIENT_DEVICE))
			{
				__Configuration = (uint8_t)__ControlRequest.wValue & 0xFF;
				if(__Configuration > 0x00)
				{
					USB_DeviceDescriptor_t* Descriptor;
					Endpoint_HandleSTATUS(__ControlRequest.bmRequestType);
					if((USB_GetDescriptor((DESCRIPTOR_TYPE_CONFIGURATION << 0x08) | (__Configuration - 0x01), __ControlRequest.wIndex, (void*)&Descriptor) == 0x00) && (__USBEvents.Error != NULL))
					{
						__USBEvents.Error();
					}
					else
					{
						__DeviceState = USB_STATE_CONFIGURED;
						if(__USBEvents.ConfigurationChanged != NULL)
						{
							__USBEvents.ConfigurationChanged(__Configuration);
						}
					}
				}
				else
				{
					__DeviceState = USB_STATE_CONFIGURED;
				}
			}
			break;	
		}
	}
}
```

Now the program is copied to the microcontroller and the board is connected to the computer via USB. As soon as the program has started, the device logs on to the computer. With a look in the device manager you can check whether the registration was successful.

![](https://2801891837-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LvA_nizlqrtUSmfwyso%2F-Ly012OWbxgIKGd5zM4p%2F-Ly023wGTASRFqsY_yJW%2Fimage.png?alt=media\&token=fe48f486-4934-404b-84de-8151691d16cf)

On a Windows PC, the USBView program can be used to display all information, such as the device configuration, the USB address, etc.

{% embed url="<https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/usbview>" %}

This is particularly useful for finding and recognizing errors in the enumeration.

```
[Port4] 
Is Port User Connectable:         yes
Is Port Debug Capable:            no
Companion Port Number:            4
Companion Hub Symbolic Link Name: USB#VID_05E3&PID_0617#6&1f56a0e&0&1#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
Protocols Supported:
 USB 1.1:                         yes
 USB 2.0:                         yes
 USB 3.0:                         no
       ---===>Device Information<===---
String Descriptor for index 2 not available while device is in low power state.
ConnectionStatus:                  
Current Config Value:              0x00  -> Device Bus Speed: Low
Device Address:                    0x1A
Open Pipes:                           0
*!*ERROR:  No open pipes!
          ===>Device Descriptor<===
bLength:                           0x12
bDescriptorType:                   0x01
bcdUSB:                          0x0011
bDeviceClass:                      0xFF  -> This is a Vendor Specific Device
bDeviceSubClass:                   0x00
bDeviceProtocol:                   0x00
bMaxPacketSize0:                   0x08 = (8) Bytes
idVendor:                        0x0123 = Vendor ID not listed with USB.org
idProduct:                       0x4567
bcdDevice:                       0x0001
iManufacturer:                     0x01
String Descriptor for index 1 not available while device is in low power state.
iProduct:                          0x02
String Descriptor for index 2 not available while device is in low power state.
iSerialNumber:                     0x03
String Descriptor for index 3 not available while device is in low power state.
bNumConfigurations:                0x01
       ---===>Full Configuration Descriptor<===---
          ===>Configuration Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x02
wTotalLength:                    0x0020  -> Validated
bNumInterfaces:                    0x01
bConfigurationValue:               0x01
iConfiguration:                    0x00
bmAttributes:                      0xC0  -> Self Powered
MaxPower:                          0x32 = 100 mA
          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x00
bAlternateSetting:                 0x00
bNumEndpoints:                     0x02
bInterfaceClass:                   0xFF  -> Interface Class Unknown to USBView
bInterfaceSubClass:                0x00
bInterfaceProtocol:                0x00
iInterface:                        0x00
          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0008
bInterval:                         0x0A
          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x02  -> Direction: OUT - EndpointID: 2
bmAttributes:                      0x03  -> Interrupt Transfer Type
wMaxPacketSize:                  0x0008
bInterval:                         0x0A
```

The host has thus successfully recognized a new device and queried all descriptors and assigned a bus address. An error is currently being output because the host has not found a suitable driver.
