Лекция
Это продолжение увлекательной статьи про usb.
...
http://microsin.ru/content/view/1084/44/.
Стандартные запросы
Секция 9.4 спецификации USB подробно описывают запросы "Standard Device", которые необходимо реализовать для каждого устройства USB. Стандарт предоставляет одну таблицу, группирующую запросы. Приняв во внимание, что большинство firmware анализирует пакет setup packet по получателю, мы можем разбить запросы на основе получателя для более простой обработки и реализации.
Стандартные запросы к устройству
В настоящий момент имеется 8 стандартных запросов к устройству, все они указаны в таблице ниже.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
1000 0000b |
GET_STATUS (0x00) |
0 |
0 |
2 |
Статус устройства |
0000 0000b |
CLEAR_FEATURE (0x01) |
Селектор фичи |
0 |
0 |
Нет |
0000 0000b |
SET_FEATURE (0x03) |
Селектор фичи |
0 |
0 |
Нет |
0000 0000b |
SET_ADDRESS (0x05) |
Адрес устройства |
0 |
0 |
Нет |
1000 0000b |
GET_DESCRIPTOR (0x06) |
Тип дескриптора и индекс |
0 или ID языка |
Длина дескриптора |
Дескриптор |
0000 0000b |
SET_DESCRIPTOR (0x07) |
Тип дескриптора и индекс |
0 или ID языка |
Длина дескриптора |
Дескриптор |
1000 0000b |
GET_CONFIGURATION (0x08) |
0 |
0 |
1 |
Значение конфигурации |
0000 0000b |
SET_CONFIGURATION (0x09) |
Configuration Value |
0 |
0 |
Нет |
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Зарезервировано |
Remote Wakeup |
Self Powered |
Стандартные запросы к интерфейсу
Спецификация в настоящий момент задает 5 стандартных запросов к интерфейсу, показанные в таблице ниже. Что интересно - только два запроса делают что-нибудь понятное.
bmRequestType |
bRequest |
wValue |
wIndex |
wLength |
Data |
1000 0001b |
GET_STATUS (0x00) |
0 |
Интерфейс |
2 |
Состояние интерфейса |
0000 0001b |
CLEAR_FEATURE (0x01) |
Селектор фичи |
Интерфейс |
0 |
Нет |
0000 0001b |
SET_FEATURE (0x03) |
Селектор фичи |
Интерфейс |
0 |
Нет |
1000 0001b |
GET_INTERFACE (0x0A) |
0 |
Интерфейс |
1 |
Альтернативный интерфейс |
0000 0001b |
SET_INTERFACE (0x11) |
Альтернативная установка |
Интерфейс |
0 |
Нет |
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Зарезервировано |
Номер интерфейса |
Стандартные запросы к конечной точке
Запросы Standard Endpoint бывают 4-х видов, перечисленных в таблице ниже.
bmRequestType |
bRequest |
wValue |
Windex |
wLength |
Data |
1000 0010b |
GET_STATUS (0x00) |
0 |
Конечная точка |
2 |
Состояние конечной точки |
0000 0010b |
CLEAR_FEATURE (0x01) |
Селектор фичи |
Конечная точка |
0 |
Нет |
0000 0010b |
SET_FEATURE (0x03) |
Селектор фичи |
Конечная точка |
0 |
Нет |
1000 0010b |
SYNCH_FRAME (0x12) |
0 |
Конечная точка |
2 |
Номер фрейма |
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Зарезервировано |
Dir |
Зарезервировано |
Номер конечной точки |
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
Зарезервировано |
Halt |
Энумерация
Энумерация – процесс определения факта, что устройство действительно подключено к шине USB и каких параметров это требует – потребляемая мощность, количество и тип конечной точки (или точек), класс устройства и т. д. В процессе энумерации хост назначает устройству адрес и разрешает конфигурацию, позволяющую устройству передавать данные по шине. Обычный процесс энумерации хорошо описан в секции 9.1.2 спецификации USB. Однако когда USB firmware пишется впервые, полезно знать не общий процесс энумерации, как он описан в стандарте, а то, как хост отвечает в процессе энумерации.
Общий процесс энумерации под операционной системой Windows включает в себя следующие шаги:
1. Хост или хаб детектирует подключение нового устройства с помощью pull-up резисторов, которое устройство подключает к паре сигнальных проводов данных (D+ и D-). Хост делает задержку как минимум 100 мс, что позволяет вставить коннектор полностью и застабилизировать питание устройства.
2. Хост выдает на шину сброс, который выводит устройство в состояние по умолчанию. Устройство может теперь ответить на заданный по умолчанию нулевой адрес.
3. Хост MS Windows запрашивает первые 64 байта дескриптора устройства (Device Descriptor).
4. После приема первых 8 байт дескриптора устройства, хост немедленно выдает новый сброс шины.
5. Теперь хост выдает команду Set Address, чем переводит устройство в адресуемое состояние.
6. Хост запрашивает все 18 байт дескриптора устройства.
7. Затем он запрашивает 9 байт дескриптора конфигурации (Configuration Descriptor), чтобы определить полный ее размер.
8. Хост запрашивает 255 байт дескриптора конфигурации.
9. Хост запрашивает все строковые дескрипторы (String Descriptors), если они имеются.
По окончании шага 9 Windows запросит драйвер для Вашего устройства. Обычно он снова запросит все дескрипторы, перед тем как выдаст запрос Set Configuration.
Вышеописанный процесс энумерации работает одинаково в Windows 2000, Windows XP and Windows 98 SE.
При написании firmware в первый раз шаг 4 часто вводит новичков в затруднение. Хост запрашивает первые 64 байта дескриптора устройства, а затем когда хост сбрасывает Ваше устройство после получения первых 8 байт, естественно думать, что что-то не так с Вашим дескриптором устройства, или что firmware неправильно обрабатывает запрос. Однако если Вы осуществили команду Set Address, то она сработает, и затем будут запрошены полные 18 байт дескриптора устройства.
Обычно если что-то неправильно с дескриптором или с тем, как он был отправлен, хост попытается прочитать его 3 раза с большой паузой между запросами. После третьей неудачной попытки хост «сдается» и сообщает об ошибке с Вашим устройством.
Firmware - PIC16F876, управляющий PDIUSBD11
Мы начнем наши примеры с устройства фирмы Philips PDIUSBD11 (I2C Serial USB Device), подключенного к микроконтроллеру PIC16F876 компании MicroChip (показан на схеме) или Microchip PIC16F877 (чип в бОльшем корпусе на 40 ножек). Несмотря на то, что у Microchip есть два low speed USB микроконтроллера PIC16C745 и PIC16C765, у них память программ только OTP, и нет поддержки внутрисхемной отладки - In-Circuit Debugging (ICD), а это никак не способствует нормальной разработке firmware. В настоящий момент микросхема Philips PDIUSBD11, подключенная к PIC16F876, дает те же возможности - Flash и внутрисхемную отладку.
Схема необходимого железа показана выше на рисунке. Пример проходит энумерацию и позволяет прочитать аналоговое напряжение с пяти мультиплексированных входов ADC микроконтроллера PIC16F876. Код совместим с PIC16F877, позволяющим максимум 8 аналоговых каналов. Светодиод LED, подключенный к ножке порта RB3, горит, когда устройство сконфигурировано. Регулятор 3.3V не показан, но он необходим для PDIUSBD11. Если Вы запускаете этот пример схемы от внешнего источника питания, то можете использовать обычный регулятор 78L033 на 3.3V, однако если Вы хотите запустить устройство в режиме Bus Powered USB device (устройство, питаемое от шины USB), то Вам нужен регулятор с низким падением напряжения.
Отладка может быть осуществлена путем соединения TXD (ножка 17) к драйверу RS-232 и подключения к компьютеру на скорости 115,200 бит/сек. Операторы printf добавлены для отображения процесса энумерации.
Код написан на C и скомпилирован компилятор Hi-Tech PICC Compiler . Имеется для загрузки демонстрационная версия компилятора PICC demo version (7.86 PL4), которая работает 30 дней. Скомпилированный HEX-файл имеется в архиве. Он скомпилирован для использования с ICD (или без него).
#include < pic.h > #include < stdio.h > #include < string.h > #include "usbfull.h"
const USB_DEVICE_DESCRIPTOR DeviceDescriptor = { sizeof(USB_DEVICE_DESCRIPTOR), /* bLength */ TYPE_DEVICE_DESCRIPTOR, /* bDescriptorType */ 0x0110, /* bcdUSB USB Version 1.1 */ 0, /* bDeviceClass */ 0, /* bDeviceSubclass */ 0, /* bDeviceProtocol */ 8, /* bMaxPacketSize 8 Bytes */ 0x04B4, /* idVendor (Cypress Semi) */ 0x0002, /* idProduct (USB Thermometer Example) */ 0x0000, /* bcdDevice */ 1, /* iManufacturer String Index */ 0, /* iProduct String Index */ 0, /* iSerialNumber String Index */ 1 /* bNumberConfigurations */ };
Все структуры заданы в заголовочном файле (header, файл с расширением *.h). Мы получили этот пример на основе примера от Cypress - USB термометр, который Вы можете использовать вместе с USB Driver для Cypress USB Starter Kit . Новый generic-драйвер написан для поддержки этого и других примеров, которые могут также скоро появиться. Только одна строка представлена для отображения производителя. Это иллюстрирует, как реализовать строковые дескрипторы без переполнения всей кодовой памяти устройства. Описание дескриптора устройства и его полей могут быть найдено здесь.
const USB_CONFIG_DATA ConfigurationDescriptor = { { /* configuration descriptor */ sizeof(USB_CONFIGURATION_DESCRIPTOR), /* bLength */ TYPE_CONFIGURATION_DESCRIPTOR, /* bDescriptorType */ sizeof(USB_CONFIG_DATA), /* wTotalLength */ 1, /* bNumInterfaces */ 1, /* bConfigurationValue */ 0, /* iConfiguration String Index */ 0x80, /* bmAttributes Bus Powered, No Remote Wakeup */ 0x32 /* bMaxPower, 100mA */ }, { /* interface descriptor */ sizeof(USB_INTERFACE_DESCRIPTOR), /* bLength */ TYPE_INTERFACE_DESCRIPTOR, /* bDescriptorType */ 0, /* bInterface Number */ 0, /* bAlternateSetting */ 2, /* bNumEndpoints */ 0xFF, /* bInterfaceClass (Vendor specific) */ 0xFF, /* bInterfaceSubClass */ 0xFF, /* bInterfaceProtocol */ 0 /* iInterface String Index */ }, { /* endpoint descriptor */ sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */ TYPE_ENDPOINT_DESCRIPTOR, /* bDescriptorType */ 0x01, /* bEndpoint Address EP1 OUT */ 0x02, /* bmAttributes - Interrupt */ 0x0008, /* wMaxPacketSize */ 0x00 /* bInterval */ }, { /* endpoint descriptor */ sizeof(USB_ENDPOINT_DESCRIPTOR), /* bLength */ TYPE_ENDPOINT_DESCRIPTOR, /* bDescriptorType */ 0x81, /* bEndpoint Address EP1 IN */ 0x02, /* bmAttributes - Interrupt */ 0x0008, /* wMaxPacketSize */ 0x00 /* bInterval */ } };
Описание дескриптора конфигурации и их полей можно найти выше. Мы сделали 2 дескриптора конечной точки в добавок к заданному по умолчанию каналу. Конечная точка EP1 OUT - 8 байт максимум Bulk OUT Endpoint, и конечная точка EP1 IN - 8 байт максимум Bulk IN Endpoint. Наш пример читает данные из конечной точки Bulk OUT и помещает их в кольцевой буфер из 80 байт. Посылка пакета IN к конечной точке EP1 читает 8-байтовые куски памяти из этого кольцевого буфера.
LANGID_DESCRIPTOR LANGID_Descriptor = { /* LANGID String Descriptor Zero */ sizeof(LANGID_DESCRIPTOR), /* bLenght */ TYPE_STRING_DESCRIPTOR, /* bDescriptorType */ 0x0409 /* LANGID US English */ }; const MANUFACTURER_DESCRIPTOR Manufacturer_Descriptor = { /* ManufacturerString 1 */ sizeof(MANUFACTURER_DESCRIPTOR), /* bLenght */ TYPE_STRING_DESCRIPTOR, /* bDescriptorType */ "B\0e\0y\0o\0n\0d\0 \0L\0o\0g\0i\0c\0" /* ManufacturerString in UNICODE */ };
Строковый дескриптор с индексом 0 сделан для поддержки требований LANGID для строковых дескрипторов USB. Это указывает, что все описатели находятся на американском английском языке. Дескриптор производителя (Manufacturer Descriptor) может быть небольшим обманом, поскольку размер символьного массива фиксированно задан в заголовке и не динамичный.
#define MAX_BUFFER_SIZE 80 bank1 unsigned char circularbuffer[MAX_BUFFER_SIZE]; unsigned char inpointer; unsigned char outpointer; unsigned char *pSendBuffer; unsigned char BytesToSend; unsigned char CtlTransferInProgress; unsigned char DeviceAddress; unsigned char DeviceConfigured; #define PROGRESS_IDLE 0 #define PROGRESS_ADDRESS 3 void main (void) { TRISB = 0x03; /* Int & Suspend Inputs */ RB3 = 1; /* Устройство не сконфигурировано (LED) */ RB2 = 0; /* Сброс PDIUSBD11 */ InitUART(); printf("Initialising\n\r"); I2C_Init(); RB2 = 1; /* Вывод PDIUSBD11 из сброса */ ADCON1 = 0x80; /* Управление ADC - все 8 каналов разрешены, */ /* поддержка обновления для 16F877 */ USB_Init(); printf("PDIUSBD11 готова к соединению\n\r"); while(1) { if (!RB0) { D11GetIRQ(); /* Если IRQ в нуле, то на PDIUSBD11 имеется событие прерывания */ } } }
Функция main зависит от конкретного примера. Она отвечает за инициализацию входов и выходов портов ввода/вывода, инициализацию интерфейса I2C, ADC и PDIUSBD11. Как только все сконфигурировано, обработчик D11GetIRQ обрабатывает запросы на прерывание PDIUSBD11.
void D11GetIRQ(void) { unsigned short Irq; unsigned char Buffer ; /* Чтение регистра прерывания, чтобы определить его источник */ D11CmdDataRead(D11_READ_INTERRUPT_REGISTER, (unsigned char*)&Irq, 2); if (Irq) printf("Irq = 0x%X: ",Irq);
Функция USB_Init инициализирует PDIUSBD11. Эта процедура инициализации опущена в даташите Philips на PDIUSBD11, но доступна в FAQ. Последняя команда разрешает мягкое подключение pull-up резистора на сигнале D+, что указывает хосту на подключение устройства USB на полной скорости (full speed), и представляет появление устройства USB на шине.
void USB_Init(void) { unsigned char Buffer ; /* Запрет функции хаба в PDIUSBD11 */ Buffer = 0x00; D11CmdDataWrite(D11_SET_HUB_ADDRESS, Buffer, 1); /* Установка адреса в 0 (по умолчанию) и разрешение функционирования */ Buffer = 0x80; D11CmdDataWrite(D11_SET_ADDRESS_ENABLE, Buffer, 1); /* Разрешение работы стандартных конечных точек */ Buffer = 0x02; D11CmdDataWrite(D11_SET_ENDPOINT_ENABLE, Buffer, 1); /* Установка режима - разрешение SoftConnect */ Buffer = 0x97; /* встроенная функция, SoftConnect, Clk Run, No LazyClk, Remote Wakeup */ Buffer = 0x0B; /* CLKOut = 4MHz */ D11CmdDataWrite(D11_SET_MODE, Buffer, 2); }
Main() осуществляет вызовы D11GetIRQ в цикле. Эта функция читает регистр прерывания PDIUSBD11, если какие-нибудь прерывания находятся в ожидании обработки. В этом случае они обрабатываются, иначе цикл продолжается. Другие устройства USB могут иметь несколько векторов прерывания, назначенных на каждую конечную точку. В этом случае каждый обработчик прерывания ISR будет обрабатывать отдельное прерывание, что позволяет убрать операторы if.
if (Irq & D11_INT_BUS_RESET) { printf("Bus Reset\n\r"); USB_Init(); } if (Irq & D11_INT_EP0_OUT) { printf("EP0_Out: "); Process_EP0_OUT_Interrupt(); } if (Irq & D11_INT_EP0_IN) { printf("EP0_In: \n\r"); if (CtlTransferInProgress == PROGRESS_ADDRESS) { D11CmdDataWrite(D11_SET_ADDRESS_ENABLE,&DeviceAddress,1); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_IN, Buffer, 1); CtlTransferInProgress = PROGRESS_IDLE; } else { D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_IN, Buffer, 1); WriteBufferToEndPoint(); } }
Условные операторы работают вниз в порядке приоритета. Наивысший приоритет у прерывания bus reset (сброс шины). Оно просто вызывает функцию USB_Init, которая переинициализирует функцию USB. Следующий наивысший приоритет – канал по умолчанию, основанный на конечной точке ноль - EP0 OUT и EP0 IN. Здесь обрабатываются энумерация и все управляющие запросы. Мы вызываем другую функцию для обработки запросов EP0_OUT.
Когда запрос сделан хостом, и он хочет принять данные, PIC16F876 отправит чипу PDIUSBD11 пакет из 8 байт. Поскольку шина USB управляется хостом, то микросхема PDIUSBD11 не может отправить данные когда пожелает, она буферизирует данные и ждет токена IN, который будет отправлен хостом. Когда PDIUSBD11 примет токен IN, она генерирует прерывание. В этот момент нужно загрузить новый пакет данных для отправки, что и происходит с помощью дополнительной функции WriteBufferToEndpoint().
Секция CtlTransferInProgress == PROGRESS_ADDRESS обрабатывает установку адреса устройства. Это мы обсудим позднее.
if (Irq & D11_INT_EP1_OUT) { printf("EP1_OUT\n\r"); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP1_OUT, Buffer, 1); bytes = D11ReadEndpoint(D11_ENDPOINT_EP1_OUT, Buffer); for (count = 0; count < bytes; count++) { circularbuffer[inpointer++] = Buffer[count]; if (inpointer >= MAX_BUFFER_SIZE) inpointer = 0; } loadfromcircularbuffer(); //Kick Start } if (Irq & D11_INT_EP1_IN) { printf("EP1_IN\n\r"); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP1_IN, Buffer, 1); loadfromcircularbuffer(); }
Конечные точки EP1 OUT и EP1 IN реализованы для чтения и записи данных bulk в и из кольцевого буфера. Настройка позволяет использовать код в соединении с примером BulkUSB из Windows DDK. Кольцевой буфер задан ранее в коде как 80 байт, по длине занимающий весь bank1 PIC16F876 RAM.
if (Irq & D11_INT_EP2_OUT) { printf("EP2_OUT\n\r"); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP2_OUT, Buffer, 1); Buffer = 0x01; /* Stall конечной точки */ D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP2_OUT, Buffer, 1); } if (Irq & D11_INT_EP2_IN) { printf("EP2_IN\n\r"); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP2_IN, Buffer, 1); Buffer = 0x01; /* Stall конечной точки */ D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP2_IN, Buffer, 1); } if (Irq & D11_INT_EP3_OUT) { printf("EP3_OUT\n\r"); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP3_OUT, Buffer, 1); Buffer = 0x01; /* Stall конечной точки */ D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP3_OUT, Buffer, 1); } if (Irq & D11_INT_EP3_IN) { printf("EP3_IN\n\r"); D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP3_IN, Buffer, 1); Buffer = 0x01; /* Stall конечной точки */ D11CmdDataWrite(D11_SET_ENDPOINT_STATUS + D11_ENDPOINT_EP3_IN, Buffer, 1); }
Конечные точки 2 и 3 в настоящий момент не используются, поэтому мы их приостанавливаем (stall), если будут приняты любые данные. PDIUSBD11 имеет команду Set Endpoint Enable, которую можно использовать для разрешения или запрещения обычных конечных точек (т. е. любых конечных точек, отличных от нулевой, составляющей по умолчанию канал управления). Мы можем использовать эту команду, чтобы отключить обычные конечные точки, которые мы не хотим использовать. В настоящий момент код предоставляет фундамент для дальнейшего усовершенствования.
void Process_EP0_OUT_Interrupt(void) { unsigned long a; unsigned char Buffer ; USB_SETUP_REQUEST SetupPacket; /* Проверка принятого пакета на Setup или Data, вместе с очисткой IRQ */ D11CmdDataRead(D11_READ_LAST_TRANSACTION + D11_ENDPOINT_EP0_OUT, &SetupPacket, 1); if (SetupPacket.bmRequestType & D11_LAST_TRAN_SETUP) {
Первое, что мы должны проделать – определить, является ли пакет принятый на EP0 Out пакетом данных или пакетом настройки (Setup Packet). Setup Packet содержит запрос, такой как Get Descriptor, где пакет данных содержит данные для предыдущего запроса. Нам повезло, что большинство запросов не отправляют пакеты данных от хоста в устройство. Запрос, который это делает, является запросом SET_DESCRIPTOR, но он редко осуществляется.
/* Это пакет установки (Setup Packet) - чтение пакета */ D11ReadEndpoint(D11_ENDPOINT_EP0_OUT, &SetupPacket); /* Подтверждение Setup Packet в конечную точку EP0_OUT & очистка буфера */ D11CmdDataWrite(D11_ACK_SETUP, NULL, 0); D11CmdDataWrite(D11_CLEAR_BUFFER, NULL, 0); /* Acknowlegde Setup Packet в конечную точку EP0_IN */ D11CmdDataWrite(D11_ENDPOINT_EP0_IN, NULL, 0); D11CmdDataWrite(D11_ACK_SETUP, NULL, 0); /* Парсинг bmRequestType */ switch (SetupPacket.bmRequestType & 0x7F) {
Как мы уже рассматривали в писании управляющих передач (Control Transfers), пакет setup не может получать в ответ NAK или STALL. Когда микросхема PDIUSBD11 получает пакет Setup, она очищает (flush) буфер EP0 IN и запрещает команды Validate Buffer и Clear Buffer. Это гарантирует, что пакет setup будет подтвержден микроконтроллером путем отправки команды подтверждения настройки (Acknowledge Setup) в обе конечные точки EP0 IN и EP0 OUT до того, как эффективна команда Validate или Clear Buffer. Прием пакета setup также выводит управляющую контрольную точку 0 из состояния STALL, если она была остановлена.
Как только пакет прочитался в память и пакет setup подтвержден, мы начинаем анализировать запрос, начиная с определения типа запроса. Пока нас не интересует направление передачи, и мы операцией AND отключаем этот бит. Три запроса, на которые все устройства должны ответить - Standard Device Request, Standard Interface Request и Standard Endpoint Request. Мы реализовали наш функционал (чтение входов ADC) в запросе вендора (Vendor Request) – мы добавили оператор выбора (case) в запросы Standard Vendor. Если наше устройство поддерживает спецификацию стандартного класса USB, нам также необходимо добавить операторы case для Class Device Request, Class Interface Request и/или Class Endpoint Request.
case STANDARD_DEVICE_REQUEST: printf("Standard Device Request "); switch (SetupPacket.bRequest) { case GET_STATUS: /* Получение Status Request устройства, который должен быть возвращен, состояния Remote Wakeup и Self Powered */ Buffer = 0x01; Buffer = 0x00; D11WriteEndpoint(D11_ENDPOINT_EP0_IN, Buffer, 2); break; case CLEAR_FEATURE: case SET_FEATURE: /* Мы не поддерживаем DEVICE_REMOTE_WAKEUP или TEST_MODE */ ErrorStallControlEndPoint(); break;
Запрос Get Status используется для сообщении о состоянии устройства в смысле питается ли устройство от шины или имеет собственный источник питания, и поддерживает ли устройство функцию удаленного пробуждения хоста (remote wakeup). В нашем устройстве мы сообщаем, что устройство имеет собственный источник питания (self powered, оно не питается от шины USB), и устройство не поддерживает remote wakeup.
На запросы Device Feature это устройство ответит, что не поддерживает ни DEVICE_REMOTE_WAKEUP, ни TEST_MODE, и вернет ошибку запроса USB Request Error.
case SET_ADDRESS: printf("Set Address\n\r"); DeviceAddress = SetupPacket.wValue | 0x80; D11WriteEndpoint(D11_ENDPOINT_EP0_IN, NULL, 0); CtlTransferInProgress = PROGRESS_ADDRESS; break;
Только команда Set Address выполняет обработку после стадии status. Все другие команды должны завершить обработку перед стадией status. Адрес устройства читается, по операции OR на него накладывается константа 0x80, и результат сохраняется в переменной DeviceAddress. Операция OR 0x80 – специфична для микросхемы PDIUSBD11, у нее старший бит адреса задает,
продолжение следует...
Часть 1 Все о USB , Программирование USB интерфейса и работа с USB периферии для программистов
Часть 2 Цоколевки разъема и кабеля - Все о USB , Программирование
Часть 3 Глава 4: Типы конечных точек (Endpoint Types ) - Все
Часть 4 Глава 5: Дескрипторы USB - Все о USB , Программирование
Часть 5 Глава 7 : обычный (Generic) драйвер USB - Все о
Часть 6 Термины - Все о USB , Программирование USB интерфейса и
Комментарии
Оставить комментарий
Операционные системы и системное программировние
Термины: Операционные системы и системное программировние