Usb bulk endpoint read error

codebot LibUSB. Документация libusb описание. Эта документация даст обзор работе API v0.1 libusb и какое имеет это отношение к USB. Работа быстро развивается в новую версию libusb, чтобы стать v1.0,в которой будет переработанны API и собирается устаревший v0.1. Вы можете желать проверить сайт libusb, чтобы видеть — стабильное и рекомендуемое. Эта документация допускает что […]

Содержание

  1. codebot
  2. LibUSB. Документация
  3. Usb bulk read error
  4. xFSTK Downloader producing «USB bulk read fails» error
  5. Usb bulk read error
  6. xFSTK Downloader producing «USB bulk read fails» error
  7. codebot
  8. LibUSB. Документация
  9. Usb bulk read error

codebot

LibUSB. Документация

libusb описание.

Эта документация даст обзор работе API v0.1 libusb и какое имеет это отношение к USB. Работа быстро развивается в новую версию libusb, чтобы стать v1.0,в которой будет переработанны API и собирается устаревший v0.1. Вы можете желать проверить сайт libusb, чтобы видеть — стабильное и рекомендуемое.

Эта документация допускает что вы имеете хорошее понимание USB и как это работает. Если у вас нет хорошего понимания USB, рекомендовано, чтобы вы почитали спецификацию USB v2.0.

libusb работает с USB 1.1, тем не менее в перспективах libusb, USB 2.0 не будет значительно изменен для libusb.

2. Поддержка современных ОС

    Linux (2.2, 2.4 and on)
    FreeBSD, NetBSD and OpenBSD
    Darwin/MacOS X

Это внешний API для использования в приложений.
API — сравнительно небольшой и разработанн, чтобы иметь закрытые аналогий в спецификации USB. v0.1 API по большей части был хакнут и kludged вместе без значительной преднамеренности и в результате, пропускается довольно много особенностей. В v1.0 собираемся исправить это.

Устройства и интерфейсы
libusb API связывают открытое устройство со специфическим интерфейсом. Это означает, что если Вы хотите потребовать многочисленные интерфейсы на устройстве, вы должны открывать долгое время устройства, чтобы получать один usb_dev_handle для каждого интерфейса с которого вы хотите передавать. Не забывайте вызывать usb_claim_interface.

Тайм-ауты
Тайм-аут в libusb всегда определены в течение миллисекунд.

Типы Данных
libusb использует как абстрагировавшее так и не абстрагированные структуры, чтобы поддерживать мобильность.

Синхронный
Все функции в libusb v0.1 синхронные, это означает, что функции блокируют и ждут операцию, чтобы завершиться или тайм-аут перед обратным выполнением в вызывающее приложение. Асинхронная операция будет поддержана в v1.0, но не v0.1.

Источник

Usb bulk read error

Success! Subscription added.

Success! Subscription removed.

Sorry, you must verify to complete this action. Please click the verification link in your email. You may re-send via your profile.

  • Intel Communities
  • Product Support Forums
  • Embedded Products
  • Embedded Intel® Core™ Processors
  • Re: xFSTK Downloader producing «USB bulk read fails» error

xFSTK Downloader producing «USB bulk read fails» error

  • Subscribe to RSS Feed
  • Mark Topic as New
  • Mark Topic as Read
  • Float this Topic for Current User
  • Bookmark
  • Subscribe
  • Mute
  • Printer Friendly Page
  • Mark as New
  • Bookmark
  • Subscribe
  • Mute
  • Subscribe to RSS Feed
  • Permalink
  • Print
  • Report Inappropriate Content

I am using xFSTK Downloader for my Zenfone 6, using CloverviewPlus provisions. Apparently it is giving «USB bulk read fails» error and fail to complete.

23:20:25 — XFSTK-LOG—USBSN:7FB7B021FE47F499—usb_bulk_read() fails

23:20:25 — XFSTK-LOG—USBSN:7FB7B021FE47F499—Windriver Error: 0x20000015, Timeout expired

23:20:30 — XFSTK-LOG—USBSN:7FB7B021FE47F499—usb_bulk_read() fails

23:20:30 — XFSTK-LOG—USBSN:7FB7B021FE47F499—Windriver Error: 0x20000015, Timeout expired

So far my search in the internet shows all of them having this problem. Can someone help to look into it.

Источник

Usb bulk read error

Success! Subscription added.

Success! Subscription removed.

Sorry, you must verify to complete this action. Please click the verification link in your email. You may re-send via your profile.

  • Intel Communities
  • Product Support Forums
  • Embedded Products
  • Embedded Intel® Core™ Processors
  • xFSTK Downloader producing «USB bulk read fails» error

xFSTK Downloader producing «USB bulk read fails» error

  • Subscribe to RSS Feed
  • Mark Topic as New
  • Mark Topic as Read
  • Float this Topic for Current User
  • Bookmark
  • Subscribe
  • Mute
  • Printer Friendly Page
  • Mark as New
  • Bookmark
  • Subscribe
  • Mute
  • Subscribe to RSS Feed
  • Permalink
  • Print
  • Report Inappropriate Content

I am using xFSTK Downloader for my Zenfone 6, using CloverviewPlus provisions. Apparently it is giving «USB bulk read fails» error and fail to complete.

23:20:25 — XFSTK-LOG—USBSN:7FB7B021FE47F499—usb_bulk_read() fails

23:20:25 — XFSTK-LOG—USBSN:7FB7B021FE47F499—Windriver Error: 0x20000015, Timeout expired

23:20:30 — XFSTK-LOG—USBSN:7FB7B021FE47F499—usb_bulk_read() fails

23:20:30 — XFSTK-LOG—USBSN:7FB7B021FE47F499—Windriver Error: 0x20000015, Timeout expired

So far my search in the internet shows all of them having this problem. Can someone help to look into it.

Источник

codebot

LibUSB. Документация

libusb описание.

Эта документация даст обзор работе API v0.1 libusb и какое имеет это отношение к USB. Работа быстро развивается в новую версию libusb, чтобы стать v1.0,в которой будет переработанны API и собирается устаревший v0.1. Вы можете желать проверить сайт libusb, чтобы видеть — стабильное и рекомендуемое.

Эта документация допускает что вы имеете хорошее понимание USB и как это работает. Если у вас нет хорошего понимания USB, рекомендовано, чтобы вы почитали спецификацию USB v2.0.

libusb работает с USB 1.1, тем не менее в перспективах libusb, USB 2.0 не будет значительно изменен для libusb.

2. Поддержка современных ОС

    Linux (2.2, 2.4 and on)
    FreeBSD, NetBSD and OpenBSD
    Darwin/MacOS X

Это внешний API для использования в приложений.
API — сравнительно небольшой и разработанн, чтобы иметь закрытые аналогий в спецификации USB. v0.1 API по большей части был хакнут и kludged вместе без значительной преднамеренности и в результате, пропускается довольно много особенностей. В v1.0 собираемся исправить это.

Устройства и интерфейсы
libusb API связывают открытое устройство со специфическим интерфейсом. Это означает, что если Вы хотите потребовать многочисленные интерфейсы на устройстве, вы должны открывать долгое время устройства, чтобы получать один usb_dev_handle для каждого интерфейса с которого вы хотите передавать. Не забывайте вызывать usb_claim_interface.

Тайм-ауты
Тайм-аут в libusb всегда определены в течение миллисекунд.

Типы Данных
libusb использует как абстрагировавшее так и не абстрагированные структуры, чтобы поддерживать мобильность.

Синхронный
Все функции в libusb v0.1 синхронные, это означает, что функции блокируют и ждут операцию, чтобы завершиться или тайм-аут перед обратным выполнением в вызывающее приложение. Асинхронная операция будет поддержана в v1.0, но не v0.1.

Источник

Usb bulk read error

Для проверки некой гипотезы понадобилось мне использовать libusb-win32.
конкретнее, есть устройство с одной конфигурацией, одним интерфейсом и двумя end-points: 0x2 и 0x81.
Оба ep работают в режиме bulk.
Пытаюсь читать с девайса данные. Приходят только первые два байта. Хотя usb_bulk_read возвращает различное число прочитанных байт.
Посмотрел с помощью SnoopyPro трафик родных драйверов, там есть только чтение, больше там никаких запросов нет. и причем, родные драйвера вычитывают все правильно. Подскажите плиз, куда копать.

PS: С USB дела не имел. Читать много-страничную спеку на USB только ради того, что бы проверить одну мысль, крайне не хочется. Можно ли что-нибудь сделать в этих условиях?

От: Sergei I. Gorelkin
Дата: 28.02.07 19:15
Оценка:

Здравствуйте, Lonely Dog, Вы писали:

<>

LD> Хотя usb_bulk_read возвращает различное число прочитанных байт.

В смысле — число прочитанных байт? Она же вроде код ошибки возвращает, если я ничего не путаю.

usb_claim_inteface() перед чтением вызывается?

От: Lonely Dog
Дата: 01.03.07 07:14
Оценка:

Name
usb_bulk_read — Read data from a bulk endpoint
Description

int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

usb_bulk_read performs a bulk read request to the endpoint specified by ep. Returns number of bytes read on success or
SIG>usb_claim_inteface() перед чтением вызывается?
Да. Код вот такой:

Функция open_dev просто перебирает все устройства и ищет устройство с нужными VID и PID.

От: Lonely Dog
Дата: 01.03.07 10:25
Оценка:

Здравствуйте, Lonely Dog, Вы писали:

Вопрос закрыт. Нашел в сети библиотеку LibFtdi, позволяющую работать с FTDI RS232 через libusb.
С ней все работает.

От: Sergei I. Gorelkin
Дата: 01.03.07 10:45
Оценка:

Здравствуйте, Lonely Dog, Вы писали:

SIG>>В смысле — число прочитанных байт? Она же вроде код ошибки возвращает, если я ничего не путаю.
LD>Описание usb_bulk_read из документации:

Упсс. Действительно, смысл возвращаемого значения зависит от знака. Этот момент я из вида упустил.

SIG>>usb_claim_inteface() перед чтением вызывается?
LD>Да. Код вот такой:

— Возможно, вызов usb_set_altinterface не нужен. Во всяком случае, устройство с единственной Alternate setting имеет право этот запрос не поддерживать.
— По скольку байт должно передавать устройство? Тут возможны два нюанса:
1. Если запрашиваем больше байт, чем устройство реально отдает, получается так называемый short transfer. Это вполне допустимо, но обрабатывает ли такое libusb — неизвестно, надо курить исходники.
2. Если наоборот, устройство хочет передать больше байт, чем мы запрашиваем — оно запросто может не успеть. Буфер можно увеличить до LIBUSB_MAX_READ_WRITE, но если этого мало, надо переходить на асинхронное чтение, ставя сразу несколько буферов в очередь.

Далее. В качестве доки по USB могу порекомендовать http://www.beyondlogic.org/usbnutshell/usb1.htm — всего семь страниц. Вместо libusb-win32 можно попробовать драйвер с www.thesycon.de — демо-версии вполне хватает для проверки гипотез; имеется тестовое приложение, с которым, возможно, даже не придется ничего самостоятельно писать.

От: .h
Дата: 01.03.07 12:05
Оценка:

Здравствуйте, Sergei I. Gorelkin, Вы писали:

SIG>Далее. В качестве доки по USB могу порекомендовать http://www.beyondlogic.org/usbnutshell/usb1.htm — всего семь страниц. Вместо libusb-win32 можно попробовать драйвер с www.thesycon.de — демо-версии вполне хватает для проверки гипотез; имеется тестовое приложение, с которым, возможно, даже не придется ничего самостоятельно писать.

Добавлю, что еще гипотезы можно попроверять с помощью Windriver.
Ну а в Vista, естественно, добавили user mode usb library: WinUSB.

Источник

Еще более низкий уровень (avr-vusb)
USB на регистрах: STM32L1 / STM32F1
USB на регистрах: interrupt endpoint на примере HID
USB на регистрах: isochronous endpoint на примере Audio device

В прошлый раз мы познакомились с общими принципами организации USB и собрали простое устройство, иллюстрирующее работу конечной точки типа Control. Пришло время изучать следующий тип — Bulk. Конечные точки такого типа предназначены для обмена большими объемами информации, причем чувствительной к надежности, но не скорости обмена.

Классические примеры — запоминающие устройства и переходники вроде USB-COM. Но переходники требуют еще наличия конечной точки типа Interrupt, которую мы пока «не проходили», так что остановимся на эмуляции флешки. Точнее, двух флешек одновременно.

Рекомендую параллельно сравнивать написанное с исходным кодом.

Дескриптор

static const uint8_t USB_ConfigDescriptor[] = {
  ARRLEN34(
  ARRLEN1(
    bLENGTH, // bLength: Configuration Descriptor size
    USB_DESCR_CONFIG,    //bDescriptorType: Configuration
    wTOTALLENGTH, //wTotalLength
    1, // bNumInterfaces
    1, // bConfigurationValue: Configuration value
    0, // iConfiguration: Index of string descriptor describing the configuration
    0x80, // bmAttributes: bus powered
    0x32, // MaxPower 100 mA
  )
  ARRLEN1(
    bLENGTH, //bLength
    USB_DESCR_INTERFACE, //bDescriptorType
    0, //bInterfaceNumber
    0, // bAlternateSetting
    2, // bNumEndpoints
    MSDCLASS_MSD, // bInterfaceClass: 
    MSDSUBCLASS_SCSI, // bInterfaceSubClass: 
    MSDPROTOCOL_BULKONLY, // bInterfaceProtocol: 
    0x00, // iInterface
  )
  ARRLEN1(
    bLENGTH, //bLength
    USB_DESCR_ENDPOINT, //bDescriptorType
    ENDP_NUM | 0x80,  //Endpoint address
    USB_ENDP_BULK,   //Bulk endpoint type
    USB_U16(ENDP_SIZE), //endpoint size
    0x00,   //Polling interval in milliseconds (ignored)
  )
  ARRLEN1(
    bLENGTH, //bLength
    USB_DESCR_ENDPOINT, //bDescriptorType
    ENDP_NUM,  //Endpoint address
    USB_ENDP_BULK,   //Bulk endpoint type
    USB_U16(ENDP_SIZE), //endpoint size
    0x00,   //Polling interval in milliseconds (ignored)
  )
  )
};

Здесь мы видим сначала заголовок дескриптора с полной длиной и другими неинтересными параметрами. Потом идет описание единственного интерфейса запоминающего устройства, в котором важно правильно указать поля Class, Subclass и Protocol — именно они отвечают за правильную идентификацию устройства в системе. Также важное поле bNumEndpoints, которое показывает сколько конечных точек нашему интерфейсу принадлежит. В нашем случае их две: на чтение и на запись. И тут же идут их описания, в которых внимание нужно уделить номеру (в номере конечной точки типа IN также выставлен 7-й бит, что прописано в дескрипторе как OR с 0x80) и размеру. Организация конечных точек в STM32 позволяет один номер точки использовать как на передачу, так и на прием. Существует еще альтернативный режим, в котором направление у точки одно, а буфер «комплементарной» точки используется для двойной буферизации. По идее, это может повысить скорость, но мы так делать пока не будем и воспользуемся более простым способом — приемник и передатчик, 0x01 и 0x81. А вот поле частоты опроса роли не играет вообще: пока данные для передачи есть, хост будет нашу точку дергать так часто, как только сможет, а когда данные закончатся — оставит в покое.

Еще пару слов надо сказать про размер конечных точек. Согласно стандарту [1], он должен быть равен 8, 16, 32 или 64 байта. Правда, покупные флешки как-то умудряются использовать и 512-байтные… В любом случае, делать полноценную флешку на контроллере общего назначения не самая удачная идея, так что оставим 64 байта. Да и места под буферы у нас немного.

Приведенные в этом примере константы для Class / Subclass / Protocol не являются единственно возможными. Скажем, можно попытаться эмулировать флоппик (Subclass = 0x04 вместо нашего 0x06). И оно при подключении даже показывает красивую иконку дисекты. Правда, не в винде — очевидно, она использует какие-то специфичные запросы и не верит, что бывают флоппики, их не поддерживающие. Но до специфичных запросов мы еще доберемся. Еще, если поменять Protocol, можно воспользоваться для обмена не только Bulk-точками, но и Interrupt. Но опять же, Interrupt мы не проходили, да и реальные флешки таким тоже не пользуются.

Помните, у нас в DeviceDescriptor (который почти ни за что не отвечает, поэтому не меняется и поэтому же здесь не приведен) есть поле iSerialNumber? Так вот, на этом поле растут грабли! Стандарт предписывает последние 12 символов использовать для идентификации экземпляра устройства. Соответственно, «хвост» этой строки должен представлять собой последовательность шестнадцатеричных цифр (‘0’-‘9’, ‘A’-‘F’), закодированных в 16-битной кодировке. Есть подозрение, что перед ними можно оставить осмысленный текст. А практика показала, что и количество «цифр» может быть меньше 12-и.

Скажем, в моем примере вся строка состоит из единственного символа u»1» и, кажется, работает. Но вот подставлять туда не-шестнадцатеричные символы все же не стоит: некоторые версии Windows такого пугаются и не хотят с устройством работать.

SETUP запросы

Несмотря на то, что обмен данными идет только через Bulk-точки, кое-какая информация передается и через ep0 по соответствующим запросам. Нам понадобится всего два таких запроса — USBCLASS_MSC_RESET и USBCLASS_MSC_GET_MAX_LUN, причем первый (ресет) мы пока проигнорируем. А вот второй стоит рассмотреть подробнее. Дело в том, что запоминающее устройство по логике авторов стандарта состоит из независимых логических блоков (адресуемых по logical unit number, LUN), с каждым из которых можно общаться независимо. Дальше мы увидим, что в протокол обмена всегда входит поле bLUN, именно за это отвечающее. Всего в одном устройстве их может быть до 15 штук. Правда, никто не запрещает сделать составное устройство, где по 15 «носителей» будет в каждом. В общем, важная это штука, обрабатываем обязательно. Тем более что в качестве ответа на этот запрос достаточно вернуть всего один байт с номером последнего unit’а. Важно! Не количество, а именно номер. То есть если устройство у нас всего одно с lun=0, то и вернуть надо 0, а не 1.

Принцип обмена bbb

BBB (bulk/bulk/bulk) или, что тоже самое, BOT (bulk only transport) — протокол обмена [1], при котором используется единственный тип конечной точки. Через нее передаются команды, через нее же передаются данные и через нее же успешность команд контролируется. Собственно всю логику обмена я сейчас и описал. Перейдем к подробностям:

Передача команд осуществляется всегда от хоста к устройству, то есть через конечную точку типа OUT, и представляет собой структуру следующего вида:

struct usb_msc_cbw{
  uint32_t dSignature;
  uint32_t dTag;
  uint32_t dDataLength;
  uint8_t  bmFlags;
  uint8_t  bLUN;
  uint8_t  bCBLength;
  uint8_t  CB[16];
}__attribute__((packed));

поле

dSignature

— волшебное чиселко, равное 0x43425355 (или же 4 символа »USBC»), передающееся хостом для синхронизации. Благодаря им устройство могло более-менее достоверно отличить начало команды от простого потока данных. Дальше идет

dTag

— порядковый номер команды чтобы если хост не дождался завершения команды и послал другую, смог отличить ответы. Это число надо будет куда-нибудь сохранить, а потом хосту вернуть.

Следующее поле,

dDataLength

, ограничивает количество байтов ответа. То есть наша посылка не может быть больше, чем dDataLength байт. Поле

bmFlags

для нас бесполезно, оно по большей части состоит из устаревших настроек. А вот

bLUN

это то, о чем я говорил раньше — номер «носителя», с которым хочет пообщаться хост. Если носитель у вас единственный, оно всегда будет равно нулю. Но в данном примере мы сделаем их два, так что этот самый LUN придется активно читать.

bCBLength

— снова бесполезное поле, которое показывает размер дополнительных данных… как будто мы его и так не знаем. И наконец,

CB[]

— данные, специфичные для конкретного запроса. Их мы будем рассматривать только применительно собственно к запросам. Хотя нет, не совсем так. Поле CB[0] собственно за запрос отвечает, поэтому его мы будем читать и по нему же определять как на данный запрос реагировать.

А реакция может заключаться либо в чтении данных размером dDataLength от хоста, либо запись данных того же объема. Формат этих данных зависит от принятой команды, так что пока не будем углубляться.

И наконец идет подтверждение — специальная структура следующего вида:

struct usb_msc_csw{
  uint32_t dSignature;
  uint32_t dTag;
  uint32_t dDataResidue;
  uint8_t  bStatus;
}__attribute__((packed));

Поле

dSignature

, как и в случае запроса, является магическим чиселком, но другим: 0x53425355 (оно же строка u»USBS»). А вот поле

dTag

должно в точности совпадать с полученным нами при начале обмена. Смысл следующего поля,

dDataResidue

, я не слишком понял. Вроде бы оно содержит количество данных, которое мы хотели передать хосту, но в dDataLength не влезло, но не похоже чтобы значение там на что-то влияло. Пожалуй, самое важное поле здесь —

bStatus

. Если что-то пошло не так, по нему хост может увидеть, что команда завершилась ошибкой и надо что-то делать.

В результате первая посылка всегда идет от хоста, вторая либо от хоста, либо от нас, и третья — всегда от нас.

Реализация этого алгоритма может показаться неочевидной, поэтому рассмотрю ее подробнее:

static void msc_ep1_in(uint8_t epnum);

static void msc_ep1_out(uint8_t epnum){
  int left = sizeof(usb_msc_cbw_t) - msc_cbw_count;
  if(left > 0){ //чтение команды
    int sz = usb_ep_read(ENDP_NUM, (uint16_t*)&(((uint8_t*)&msc_cbw)[msc_cbw_count]) );
    msc_cbw_count += sz;
    if(msc_cbw_count == sizeof(usb_msc_cbw_t)){ //команда прочитана полностью
      scsi_command();
    }else return;
  }else if(bytescount < bytestoread){ //если разнести условие, произойдет повторное чтение буфера EP1_OUT, который был прочитан раньше, но size не сброшен (все равно этим железо занимается)
    uint8_t lun = msc_cbw.bLUN;
    int sz;
    if(lun == 0){
      sz = usb_ep_read(ENDP_NUM, (uint16_t*)&buffer[0]);
    }else{
      sz = usb_ep_read(ENDP_NUM, (uint16_t*)&rambuf[ start_lba*512 + bytescount ]);
      cur_count += sz;
    }
    bytescount += sz;
  }
  if(bytescount < bytestoread)return;
  msc_ep1_in(ENDP_NUM | 0x80);
}

static void msc_ep1_in(uint8_t epnum){
  if(! usb_ep_ready(epnum) )return;
  
  if(bytescount < bytestowrite){
    uint32_t left = bytestowrite - bytescount;
    if(left > ENDP_SIZE)left = ENDP_SIZE;
    if(block_count == 0){
      usb_ep_write(ENDP_NUM, &buffer[bytescount], left);
    }else{
      uint8_t lun = msc_cbw.bLUN;
      usb_ep_write(ENDP_NUM, &storage[lun].buf[start_lba*512 + bytescount], left);
      cur_count += left;
    }
    bytescount += left;
  }else{
    int32_t left = sizeof(msc_csw) - msc_csw_count;
    if(left > 0){
      if(left > ENDP_SIZE)left = ENDP_SIZE;
      usb_ep_write(ENDP_NUM, (uint8_t*)&(((uint8_t*)&msc_csw)[msc_csw_count]), left);
      msc_csw_count += left;
    }else if(left == 0){
      msc_cbw_count = 0;
      msc_csw_count = 0;
      bytestoread = 0;
      bytestowrite = 0;
      bytescount = 0;
      
      block_count = 0;
      cur_count = 0;
    }
  }
}

Как уже было сказано, обмен начинается с того, что в конечную точку OUT приходит посылка от хоста. Проблема в том, что размер посылки составляет 31 байт, а размер конечной точки может быть и 8 байт, так что стоит предусмотреть прием по частям. К счастью, пока мы не ответим на один запрос, другого нам слать не будут (если не вылетим по таймауту, конечно), поэтому для хранения запроса и ответа заведем глобальные переменные

usb_msc_cbw_t msc_cbw; //принимаемый от хоста запрос
uint8_t msc_cbw_count = 0; //сколько байтов уже принято

usb_msc_csw_t msc_csw = { //передаваемый хосту отчет
  .dSignature = 0x53425355,
};
uint8_t msc_csw_count = 0; //сколько байтов уже передано

И до тех пор пока количество принятых байтов не сравняется с размером запроса, читать будем именно туда. Если последний байт принят «в нашу смену», то не спешим выходить, а сразу запускаем обработчик команд scsi_command() и даже пересылку ответа (мы ведь помним, что для начала IN транзакции первый пакет надо передать вручную). Но вот запросы на чтение обрабатывать сразу не выйдет, ведь в буфере приема у нас «хвост» команды, а вовсе не данные.

Поэтому scsi_command() только выставляет количество данных (bytestoread). В частности, может выставить в 0 чтобы показать что чтение не нужно. Таким образом дальнейший прием будет повторяться пока количество реально принятых байтов bytescount не достигнет желаемого. После чего все равно произойдет ручной вызов обработчика конечной точки IN, которая пошлет если не данные, то хотя бы отчет об успешности.

Собственно, устройство точки IN не слишком отличается от OUT. Основная разница, что она сначала пытается передать bytestowrite байтов и только потом структуру msc_csw с использованием ее персонального счетчика msc_csw_count.

Организация памяти и прочие извращения

Раз уж решили реализовать несколько LUN’ов, имеет смысл и внутреннюю организацию им сделать максимально различной (впрочем, максимальной она не получилась, чуть позже объясню почему). Причем желательно обойтись без возни с подключением к контроллеру периферии Допустим, LUN=0 будет отображением части флешки контроллера (а поскольку возиться с записью на нее данных опять же лень, сделаем ее read-only), а LUN=1 — оперативки.

Объем флешки у L151 целых 256 кБ, но ведь нам его еще программировать, а это долго.

Ограничимся объемом 100 кБ: на таком объеме уже можно создать файловую систему FAT и даже место для файлов останется. Оперативки у нас поменьше, всего 32 кБ, от которых мы откусим 29 кБ и заполним первый «сектор» копией из образа флешки. Пусть тоже будет считаться FAT’ом, хотя и корявым. Впрочем, если будете экспериментировать с моим кодом, рекомендую взять образ флешки поменьше, чтобы не ждать минуту пока оно прошьется.

Не уверен, что это нужно, но все же расскажу как можно подготовить этот образ. Первым делом создаем «болванку»

dd if=/dev/zero of=fatexample.img bs=1k count=100

Дальнейшие действия, увы, придется производить от рута:
создаем на «болванке» файловую систему

mkfs.vfat fatexample.img

куда-нибудь ее монтируем

mount fatexample.img /mnt -o user,umask=0

флажки в конце нужны чтобы пользоваться этой файловой системой мог обычный пользователь.

Собственно, от его (своего) имени и кидаем туда файлы. Из хулиганских соображений я предпочел записать туда исходники прошивки. Только надо учитывать, что винда не поймет обычный конец строки ‘n’, ей надо ‘rn’. То есть открываем каждый скопированный файл и меняем ему формат конца строки. Возможно, это как-то делается и из консоли, но я не искал.
Наконец снова заходим в рута дабы отмонтировать образ

umount /mnt

Сразу предупреждаю: я не знаю как подобное делается в Windows. Не исключено, что по-человечески оно там вообще не делается, придется искать и скачивать какие-то сторонние программы. Ну или пользоваться для тестов каким-то из моих образов.

Теперь, когда бинарный образ готов, его надо как-то слинковать с остальным проектом. Для этого преобразуем образ в обычный объектный файл:

arm-none-eabi-ld -r -b binary -o fatexample.o fatexample.img

Проблема в том, что линкер попытается разместить его в оперативной памяти и закономерно потерпит неудачу. Поэтому преобразуем его еще раз:

arm-none-eabi-objcopy —rename-section .data=.rodata,alloc,load,readonly,data,contents fatexample.o fatexample.o

Вот теперь можно линковать. Чтобы не вводить эти команды каждый раз вручную, я прописал их прямо в makefile.

Обязательные команды

Все команды, с которыми мы будем иметь дело, относятся к семейству SCSI (Small Computer System Interface) — интерфейсу обмена данными с носителями информации и много чем еще. Причем не только по USB.

Обязательными для реализации являются следующие (достаточно подробно описаны тут):

#define SCSI_TEST_UNIT_READY 0x00 //проверка готовности LUN
#define SCSI_REQUEST_SENSE   0x03 //запрос подробностей при ошибках
#define SCSI_INQUIRY         0x12 //запрос подробностей по данному LUN'у
#define SCSI_READ_CAPACITY   0x25 //запрос емкости
#define SCSI_READ_10         0x28 //чтение
#define SCSI_WRITE_10        0x2A //запись

Также еще несколько запросов, которые формально не обязательны, но лучше бы их реализовать

#define SCSI_MODE_SENSE_6              0x1A //еще немного подробностей по LUN'у
#define SCSI_MMC_START_STOP_UNIT       0x1B //запрос на отключение
#define SCSI_MMC_PREVENT_ALLOW_REMOVAL 0x1E //еще один запрос на отключение
#define SCSI_MMC_READ_FORMAT_CAPACITY  0x23 //определение емкости (windows-specific)

Первым делом хост запрашивает у носителей данных подробности их внутреннего устройства, посылая для этого запрос SCSI_INQUIRY. В ответ он ожидает очередную волшебную структуру, подробно рассматривать которую я не хочу. Для нашего случая достаточно скопировать готовую и немного поиграться с константами. Скажем, поменять строку вендора. Или, например, нулевой байт изменить с 0x00 на 0x05 чтобы данный LUN считался не просто носителем, а CD/DVD диском. Правда, одного этого недостаточно: необходимо дописать поддержку каких-то специфичных запросы. Поэтому уж настолько извращаться не будем… а жаль

Далее идет запрос емкости (SCSI_READ_CAPACITY), на который надо ответить двумя 32-битными числами (суммарно 8 байт, очевидно): 0-3 байты это номер последнего блока, а 4-7 это размер одного блока. Дело в том, что носители данных не обеспечивают доступ к отдельному байту — только к блоку размером обычно 512 байт. На такие же блоки устройство поделим и мы.

Обратите внимание, что передается не количество блоков, а, как и в случае LUN, номер последнего. То есть uint32_t last_lba = capacity / 512 — 1;

Внимание, грабли! Винда почему-то не полагается на запрос SCSI_READ_CAPACITY, а отправляет SCSI_MMC_READ_FORMAT_CAPACITY с немного другим форматом. И ее не волнует что этот запрос не обязателен для реализации, так что после получения ошибки «запрос не поддерживается» она еще долго отключает-подключает устройство в надежде что то образумится. Если не хотите полчаса ждать пока винда таки смирится что неподдерживаемый запрос не поддерживается, лучше этот запрос реализовать. Как несложно догадаться, линукс ведет себя адекватно и понимает с первого раза.

Еще один запрос, который имеет смысл обработать — SCSI_MODE_SENSE_6. Точнее, опять забить вместо ответа чей-то готовый кусок. 1-й байт в нем отвечает за размер посылки и будет равен 3, второй байт описывает тип носителя (что-то вендор-специфичное, оставим ноль).

Третий байт самый для нас интересный. Его 7-й бит означает защиту от записи, его мы и взведем для 0-го LUN чтобы хост даже не пытался писать в нашу флеш-память. Вот в LUN=1 (оперативка) — другое дело, там этот бит будет нулевым и пусть пишет на здоровье.

Потом хост проверяет готов ли вообще носитель с нужным номером к обмену (SCSI_TEST_UNIT_READY), причем пакет данных ему для ответа не нужен — достаточно кода ошибки: готов — не готов.

Далее по хронологии должен идти рассказ про чтение и запись, но их мы оставим на конец.

Хост может попытаться управлять подключением носителей данных при помощи команд SCSI_MMC_START_STOP_UNIT и SCSI_MMC_PREVENT_ALLOW_REMOVAL, причем если они не поддерживаются, ругается что не может отмонтировать устройство. Впрочем, заглушки, возвращающей «все хорошо» ему достаточно, так что глубже я и не копал. В реальном применении покопать все же придется, поскольку некоторой периферии нужно больше нуля секунд для корректного завершения работы. А пока она медленно отключается, надо попросить хоста чтобы подождал.

Если что-то пошло не так (на этапе подтверждения устройство вернуло ошибку), хост запрашивает по этой ошибке подробности (SCSI_REQUEST_SENSE) — массив из трех байтов, кратко поясняющих что же именно случилось. Чаще всего у нас будет случаться SBC_SENSE_KEY_ILLEGAL_REQUEST (неподдерживаемый запрос), но в принципе таким же способом сообщают о неготовности носителя данных и многом другом. Кстати, раз запрос подробностей ошибки — отдельный запрос, то и хранить результат предыдущего надо в отдельной переменной, msc_sense.

READ / WRITE

Ну и наконец самое интересное — чтение и запись данных. На самом деле это не один запрос, а целое семейство, отличающихся размером. Скажем, бывают READ(6), READ(10), READ(12), даже READ(16), которые занимают в msc_cbw.CB соответственно 6, 10, 12 и 16 байтов. Мы будем пользоваться READ(10). И WRITE(10), естественно. Формат у них одинаковый, отличается только направление передачи: от хоста к устройству или от устройства к хосту. Структура запроса такова:

typedef struct{
  uint8_t opcode; //READ(10) / WRITE(10)
  uint8_t cdb_info1;
  uint32_t block_address;
  uint8_t cdb_info2;
  uint16_t length;
  uint8_t control;
}scsi_cbw_10_t;

Из нее нас интересуют только поля block_address и length — номер первого блока, который предстоит прочитать и количество этих блоков. Напоминаю, что размер блока мы сообщили хосту раньше, по запросу SCSI_READ_CAPACITY и что составляет он у нас 512 байт.

Следовательно, адрес первого байта, с которого начнем чтение, равен block_address * 512, а их суммарное количество length * 512.

Из соображений простоты демонстрационного кода (ну и из лени, конечно) посекторной работы с внешней памятью вы здесь не увидите. В самом деле, что к оперативке, что к флешке контроллера доступ побайтный. Но вот при взаимодействии с более сложной периферией вроде SD-карточек уже придется лавировать между прерываниями USB и этой периферии. Возможно, кстати, работу с USB будет проще осуществлять опросом, чем прерываниями. По той же причине выбор нужного буфера на чтение или запись остался в обработчике IN и OUT, а не в scsi_command.

Заключение

Вот мы и познакомились в общих чертах с принципом передачи данных в запоминающих устройствах и даже заставили макетную плату прикидываться одновременно read-only флешкой, накоторую записаны ее же исходники, и энерго-зависимой флешкой, которая информацию не сохраняет при отключении. Заодно протестировали работу конечной точки типа Bulk и обнаружили что ее использование не особо отличается от других.

Хотелось бы, конечно, еще сэмулировать флоппик и оптический дисковод, но там используются какие-то свои, специфичные команды. В рамках данной статьи это было бы неуместно.

Если кто-то захочет повторить данную конструкцию, исходный код как обычно в репозитоии, туда же я добавил найденную литературу, поскольку это уже какая-то нездоровая традиция создавать на ровном месте проблемы со скачиванием официальной документации.

[1] Universal Serial Bus Mass Storage Class Bulk-Only Transport копия
[2] Universal Serial Bus Mass Storage Class Specification Overview копия
[3] SCSI Multimedia Commands – 2 (MMC-2) копия
[4] SCSI Primary Commands-3 (SPC-3) копия
[5] SCSI Commands Reference Manual (от Seagate) копия

I am trying lessons on USB http://www.linuxforu.com/2011/12/data-transfers-to-from-usb-devices/ and stuck with the problem — while reading, usb_bulk_msg returns error 22 — Invalid argument. Write operation succeeds.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>

#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define BULK_EP_OUT 0x01
#define BULK_EP_IN 0x82
#define MAX_PKT_SIZE 512

static struct usb_device *device;
static struct usb_class_driver class;
static unsigned char bulk_buf[MAX_PKT_SIZE];

static int pen_open(struct inode *i, struct file *f)
{
    return 0;
}
static int pen_close(struct inode *i, struct file *f)
{
    return 0;
}
static ssize_t pen_read(struct file *f, char __user *buf, size_t cnt, loff_t *off)
{
    int retval;
    int read_cnt;

    /* Read the data from the bulk endpoint */
    retval = usb_bulk_msg(device, usb_rcvbulkpipe(device, BULK_EP_IN),
            bulk_buf, MAX_PKT_SIZE, &read_cnt, 5000);
    if (retval)
    {
        printk(KERN_ERR "Bulk message returned %dn", retval);
        return retval;
    }
    if (copy_to_user(buf, bulk_buf, MIN(cnt, read_cnt)))
    {
        return -EFAULT;
    }

    return MIN(cnt, read_cnt);
}
static ssize_t pen_write(struct file *f, const char __user *buf, size_t cnt, loff_t *off)
{
    int retval;
    int wrote_cnt = MIN(cnt, MAX_PKT_SIZE);

    if (copy_from_user(bulk_buf, buf, MIN(cnt, MAX_PKT_SIZE)))
    {
        return -EFAULT;
    }

    /* Write the data into the bulk endpoint */
    retval = usb_bulk_msg(device, usb_sndbulkpipe(device, BULK_EP_OUT),
            bulk_buf, MIN(cnt, MAX_PKT_SIZE), &wrote_cnt, 5000);
    if (retval)
    {
        printk(KERN_ERR "Bulk message returned %dn", retval);
        return retval;
    }

    return wrote_cnt;
}

static struct file_operations fops =
{
    .open = pen_open,
    .release = pen_close,
    .read = pen_read,
    .write = pen_write,
};

static int pen_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    int retval;

    device = interface_to_usbdev(interface);

    class.name = "usb/pen%d";
    class.fops = &fops;
    if ((retval = usb_register_dev(interface, &class)) < 0)
    {
        /* Something prevented us from registering this driver */
        err("Not able to get a minor for this device.");
    }
    else
    {
        printk(KERN_INFO "Minor obtained: %dn", interface->minor);
    }

    return retval;
}

static void pen_disconnect(struct usb_interface *interface)
{
    usb_deregister_dev(interface, &class);
}

/* Table of devices that work with this driver */
static struct usb_device_id pen_table[] =
{
    { USB_DEVICE(0x058F, 0x6387) },
    {} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, pen_table);

static struct usb_driver pen_driver =
{
    .name = "pen_driver",
    .probe = pen_probe,
    .disconnect = pen_disconnect,
    .id_table = pen_table,
};

static int __init pen_init(void)
{
    int result;

    /* Register this driver with the USB subsystem */
    if ((result = usb_register(&pen_driver)))
    {
        err("usb_register failed. Error number %d", result);
    }
    return result;
}

static void __exit pen_exit(void)
{
    /* Deregister this driver with the USB subsystem */
    usb_deregister(&pen_driver);
}

module_init(pen_init);
module_exit(pen_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("USB Pen Device Driver");  

Every time a Bulk Transfer is done for reading data a unknown error (3) occurs:
libusb: error [submit_bulk_transfer] submiturb failed error -1 errno=3

Nothing in the Lib USB documentation covers the unknown error (3) when using the Synchronous Device IO API, which suggests the error is unexpected. Below is the code used to read data from a USB device (uses the CP2102 USB to UART Bridge Controller):

fun runReadLoop(devHandle: CPointer<libusb_device_handle>?) = memScoped {
    val dataLength = 1500
    val data = allocArray<UByteVar>(dataLength)
    val endpoint = 0x81.toUByte()
    println("Running read loop...")
    // Ensure the device is ready for reading.
    libusb_reset_device(devHandle)
    while (true) {
        val readStatus = libusb_bulk_transfer(
                dev_handle = devHandle,
                endpoint = endpoint,
                timeout = 9000,
                length = dataLength,
                data = data,
                actual_length = null
        )
        processRead(data = data, dataLength = dataLength, readStatus = readStatus)
        sleep(1)
    }
}

fun processRead(data: CArrayPointer<UByteVar>, dataLength: Int, readStatus: Int) {
    val success = 0
    if (readStatus == LIBUSB_ERROR_IO) {
        fprintf(stderr, "Cannot read USB device.n")
    } else if (readStatus == success) {
        val str = data.createString(dataLength)
        println("Received Data:n$str")
    }
}

Here is the information for the device (using the lsusb command):

Bus 001 Device 008: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x UART Bridge / myAVR mySmartUSB light
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x10c4 Cygnal Integrated Products, Inc.
  idProduct          0xea60 CP210x UART Bridge / myAVR mySmartUSB light
  bcdDevice            1.00
  iManufacturer           1 Silicon Labs
  iProduct                2 CP2102 USB to UART Bridge Controller
  iSerial                 3 0001
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              2 CP2102 USB to UART Bridge Controller
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
Device Status:     0x0000
  (Bus Powered)

Running the program on Linux Mint 19.2 (64 bit) using the Lib USB library (via the libusb-dev package v2:1.0.21-2)

This post was written by eli on December 7, 2019
Posted Under: USB

Introduction

At times, an attempt to get data from a BULK IN endpoint may result in an overflow error. In other words,

rc = libusb_bulk_transfer(dev_handle, (1 | LIBUSB_ENDPOINT_IN),
                          buf, bufsize, &count, (unsigned int) 1000);

may fail with rc having the value of LIBUSB_ERROR_OVERFLOW. Subsequent attempts to access the same endpoint, even after re-initializing the libusb interface result in rc = LIBUSB_ERROR_IO, which is just “the I/O failed”. Terminating and rerunning the program doesn’t help. Apparently, the only thing that gets the endpoint out of this condition is physically plugging it out and back into the computer (or hub).

Why this happens

The term “transfer” in the “libusb_bulk_transfer” function name refers to a USB transfer cycle. In essence, calling this function ends up with a request to the xHCI hardware controller to receive a given number of bytes (“bufsize” above) from the endpoint, and to write it into a buffer (“buf” above). The host controller start requesting DATA packets from the device, and fills the buffer with the data in those packets. Note that the communication isn’t a continuum of data packets. Rather, it’s a session which fulfills a transfer request. Someone who wrote the USB spec probably thought of the data transmission in terms of a function call of the libusb_bulk_transfer sort: A function is called to request some data, communication takes place, the function returns. This principle holds for all USB versions.

Here’s the crux, and it’s related to the hardware protocol, namely with data packets: The host controller can give the device go-ahead to transmit packets, but it can’t control the number of bytes in each packet. This is completely up to the device. The rule is that if the number of bytes in a packet is less than the maximum allowed for the specific USB version (1024 bytes for USB 3.0), it’s considered a “short packet”. When a short packet arrives at the host controller, it should conclude that the transfer is done (including the data in this last packet).

So the device may very well supply less data than requested for the transfer. This is perfectly normal, and this is why it’s mandatory to check how many bytes have actually arrived when the function returns.

But what if the transfer request was for 10 bytes, and the device sent 20?

An overflow on USB 3.0

On USB 3.0, the xHCI controller requests data from the device indirectly by issuing an ACK Transaction Packet to the device. It may or may not acknowledge packets it has already acknowledged, but this isn’t the point at the moment. Right now, it’s more interesting that all ACK packets also carry the number of DATA packets that the host is ready to receive at the moment that the ACK packet was formed, in a field called NumP. This is how the host controls the flow of data.

When there’s no data flowing, it can be because the device has no data to send, but also if the last ACK for the relevant endpoint was a packet with NumP=0. To resume the flow, the host then issues an ACK packet, with a non-zero NumP, giving the go-ahead to transmit more data.

So a packet capture can look like this for a transfer request of 1500 bytes (the first column is time in microseconds):

       0.000 ACK  seq=0 nump=2
       0.040 DATA seq=0 len=1024
       0.032 DATA seq=1 len=1024
       2.832 ACK  seq=1 nump=1
       2.104 ACK  seq=2 nump=0

Note that the device sent 1024 bytes twice, exceeding the 1500 bytes requested. This causes an overflow error. So far so good. But what about all those LIBUSB_ERROR_IO afterwards?

So this is the time to pay attention to the “seq” numbers. All DATA packets carry this sequence number, which runs cyclically from 0 to 31. The ACK’s sequence number is the one that the host expects next. Always. For example, if the host requests a retransmission, it repeats the sequence number of the DATA packet it failed to receive properly, basically saying, “I expect this packet again” (and then ignores DATA packet that follow until the retransmission).

Now, this is what appears on the bus as a result of a libusb_bulk_transfer() call after the one that returned with an overflow condition:

21286605.320 ACK  seq=0 nump=2

This is not a mistake: The sequence number is zero. Note that the ACK that closed the previous sequence with a nump=0 had seq=2. Hence the ACK that follows it to re-initiate the data flow should have seq=2 as well. But it’s zero. In section 8.11.1, the USB 3.0 spec says that an ACK is considered invalid if it hasn’t an expected sequence number, and that it should be ignored in this case. So the device ignores this ACK and sends no DATA packet in response. The host times out on tDPResponse (400 ns per USB 3.0 spec) and reports a LIBUSB_ERROR_IO. So the forensic explanation is established. Now to the motive.

Handling a babble error

The resetting of the sequence number has been observed with more than one xHCI controller (an Intel chipset as well as Renesas’ uPD720202), so it’s not a bug.

This brings us to the xHCI spec, revision X, section 4.10.2.4: “When a device transmits more data on the USB than the host controller is expecting for a transaction, it is defined to be babbling. In general, this is called a Babble Error. When a device sends more data than the TD transfer size, … the host controller shall set the Babble Detected Error in the Completion Code field of the TRB, generate an Error Event, and halt the endpoint (refer to section 4.10.2.1).”

So the first thing to note is that the endpoint wasn’t halted after the overflow. In fact, there was no significant traffic at all. Quite interestingly, Linux’ host controller didn’t fulfill its duty in this respect.

But still, why was the sequence number reset? Section 8.12.1.2 in the USB 3.0 sheds some light: “The host expects the first DP to have a sequence number set to zero when it starts the first transfer from an endpoint after the endpoint has been initialized (via a Set Configuration, Set Interface, or a ClearFeature (ENDPOINT_HALT) command”.

So had the endpoint been halted, as it’s required per xHCI spec, it would just have returned STALL packets until it was taken out of this condition. At which point the sequence number should be reset to zero per USB spec.

So apparently, whoever designed the xHCI hardware controller assumed that no meaningful communication would take place after the overflow (babble) error, and that the endpoint must be halted anyhow, so reset the sequence number and have it done with. It’s easier doing it this way than detecting the SETUP sequence that clears the ENDPOINT HALT feature.

Given that the xHCI driver doesn’t halt the endpoint, it’s up to the application software to do it.

Halt and unhalt

This is a sample libusb-based C code for halting BULK IN endpoint 1.

      if (rc == LIBUSB_ERROR_OVERFLOW) {
	rc = libusb_control_transfer(dev_handle,
				     0x02, // bmRequestType, endpoint
				     0x03, // bRequest = SET_FEATURE
				     0x00, // wValue = ENDPOINT_HALT
				     (1 | LIBUSB_ENDPOINT_IN), // wIndex = ep
				     NULL, // Data (no data)
				     0, // wLength = 0
				     100); // Timeout, ms

	if (rc) {
	  print_usberr(rc, "Failed to halt endpoint");
	  break;
	}

	rc = libusb_control_transfer(dev_handle,
				     0x02, // bmRequestType, endpoint
				     0x01, // bRequest = CLEAR_FEATURE
				     0x00, // wValue = ENDPOINT_HALT
				     (1 | LIBUSB_ENDPOINT_IN), // wIndex = ep
				     NULL, // Data (no data)
				     0, // wLength = 0
				     100); // Timeout, ms

	if (rc) {
	  print_usberr(rc, "Failed to unhalt endpoint");
	  break;
	}

	continue;
      }

The second control transfer can be exchanged with

	rc = libusb_clear_halt(dev_handle, (1 | LIBUSB_ENDPOINT_IN));

however there is no API function for halting the endpoint, so one might as well do them both with control transfers.

Resetting the entire device

For those who like the big hammer, it’s possible to reset the device completely. This is one of the conditions for resetting the sequence numbers on all endpoints, so there’s no room for confusion.

      if (rc == LIBUSB_ERROR_OVERFLOW) {
	rc = libusb_reset_device(dev_handle);

	if (rc) {
	  print_usberr(rc, "Failed to reset device");
	  break;
	}
	continue;
      }

This causes a Hot Reset. which is an invocation of Recovery with the Hot Reset bit set, and return to U0, which in itself typically takes ~150 μs. However as a result from this call, the is reconfigured — its descriptors are read and configuration commands are sent to it. It keeps its bus number, and the the entire process takes about 100 ms. All this rather extensive amount of actions is hidden in this simple function call.

Also, a line appears in the kernel log, e.g.:

usb 4-1: reset SuperSpeed USB device number 3 using xhci_hcd

So all in all, this is a noisy overkill, and is not recommended. It’s given here mainly because this is probably how some people eventually resolve this kind of problem.

  • Posts

SimpleLibusbDemo usb_bulk_read()

Post


Essentials Only

Full Version

dudmuck

Starting Member

  • Total Posts : 38
  • Reward points : 0
  • Joined: 2009/03/16 19:51:52
  • Location: 0
  • Status: offline



2010/01/26 19:07:36

(permalink)

0

Checking out the source code for windows PC example for «USB Device — LibUSB — Gen
eric Driver Demo», I see usb_bulk_read() being called with endpoint of 0x81.
This is in file Form1.h at Microchip Solutions/USB Device — LibUSB — Generic Driver Demo/Windows Application/Microsoft VC++ 2008 Express/SimpleLibusbDemo

It seems 0x81 is the command byte being sent to read the push-button state, but it seems to me the end point number will always be 1 for this example.  All the other usb_bulk_write() are using endpoint 1.

The use of endpoint 0x81 seems more intentional in the Linux Application.

But when I look in the firmware, I see all USBGenWrite/USBGenRead calls with USBGEN_EP_NUM which is 1.  In the LibUSB firmware, 0x81 value on appears for push button read command.

#1

xiaofan

Super Member

  • Total Posts : 6296
  • Reward points : 0
  • Joined: 2005/04/14 07:05:25
  • Location: Singapore
  • Status: offline

RE: SimpleLibusbDemo usb_bulk_read()


2010/01/26 19:27:14

(permalink)

0

ORIGINAL: dudmuck
The use of endpoint 0x81 seems more intentional in the Linux Application.

Just take note strictly speaking the IN endpoint 1 and OUT endpoint 1 are two different endpoints. For libusb, the endpoint direction should be included. 0x81 means IN Endpoint 1, 0x02 means OUT Endpoint 2, etc. For usb_bulk_read(), you are using IN Endpoint 1, so it is 0x81. For usb_bulk_write(), you are using OUT Endpoint 1, so it is 0x01.

As for the command 0x81 to read the push-button, that is specific to the firmware and has nothing to do with the endpoints.

 

USB_Links and libusb

#2

dudmuck

Starting Member

  • Total Posts : 38
  • Reward points : 0
  • Joined: 2009/03/16 19:51:52
  • Location: 0
  • Status: offline

RE: SimpleLibusbDemo usb_bulk_read()


2010/01/27 10:29:06

(permalink)

0

OK thanks I understand.

Perhaps to reduce confusion, it might be wise to use the libusb definition LIBUSB_ENDPOINT_IN in the PC example:
/* the device’s endpoints */
#define EP_IN (1 | LIBUSB_ENDPOINT_IN)
#define EP_OUT (1 | LIBUSB_ENDPOINT_OUT)

#3

© 2023 APG vNext Commercial Version 4.5

В этой статье (перевод документации MSDN [1]) дается обзор обмену данными пакетами USB bulk [2]. Также приведены пошаговые инструкции, как драйвер клиента может отправлять данные в устройство USB и принимать его данные пакетами bulk.

[Конечные точки bulk]

Конечная точка USB типа bulk (bulk endpoint) способна передавать большие объемы данных. Bulk-передачи надежны, потому что в них встроена аппаратное обнаружение ошибок, включающее ограниченное количество попыток повтора, реализованных аппаратно. Для передач в конечные точки bulk полоса шины USB не резервируется. Когда имеется несколько запросов передачи, которые предназначаются для различных типов конечных точек, контроллер USB сначала планирует передачи для тех данных, которые критичны по времени обработки (см. [2]), такие как изохронные пакеты и interrupt-пакеты. Только в том случае, когда на шине есть свободная полоса во фрейме, контроллер планирует обработку bulk-передач. Когда на шине нет другого важного трафика (обладающего повышенным приоритетом по доставке), bulk-передача способна быстро перемещать данные. Однако когда шина занята передачами других типов, bulk-данные могут бесконечно ожидать, пока на шине не появится во фрейме свободный слот времени для передачи.

Ключевые особенности конечной точки bulk:

• Конечные точки типа bulk опциональны, т. е. их тип bulk выбирается разработчиком устройства USB. Они реализуются на устройстве USB в том случае, когда необходимо передать большие объемы данных. Например, передача файлов на USB-флешку и с неё, данные для принтера или данные от сканера фотографий.

• Конечные точки типа bulk поддерживают устройства USB, работающие со скоростями Full Speed, High Speed и SuperSpeed. Устройства Low Speed не поддерживают конечные точки типа bulk.

• Конечная точка однонаправленная, и данные через неё могут передаваться либо в направлении IN (направление от устройства к хосту USB), либо в направлении OUT (от хоста USB к устройству). Конечная точка bulk IN используется для чтения хостом данных из устройства USB, и конечная точка bulk OUT используется для отправки данных от хоста в устройство USB.

• Конечная точка снабжена битами CRC для проверки на наличие ошибок в передаваемых данных, это обеспечивает целостность данных в процессе передачи. Когда детектируются ошибки CRC, данные автоматически ретранслируются (передаются повторно).

• Конечная точка SuperSpeed bulk может поддерживать потоки (stream). Потоки позволяют хосту посылать данные по индивидуальным каналам потоков.

• Максимальный размер пакета конечной точки зависит от скорости на шине, поддерживаемой устройством. Для скоростей Full Speed, High Speed и SuperSpeed максимальные размеры пакетов составляют 64, 512 и 1024 байт соответственно [2].

[Bulk-транзакции]

Как и все другие передачи USB, передачу bulk также инициирует хост USB (компьютер). Коммуникация между хостом и устройством происходит через конечную точку bulk. Протокол USB не накладывает никаких ограничений на формат полезной нагрузки, которая передается в пакете bulk-транзакции.

Обмен между хостом USB и устройством USB по шине зависит от скорости, на которой было подключено устройство к порту USB. В этой секции описаны некоторые примеры bulk-передач High Speed и SuperSpeed, которые показывают коммуникацию между хостом и устройством.

Структуру транзакций и пакетов можно увидеть с помощью анализатора (сниффера) протоколов шины USB, такого как Beagle, Ellisys, LeCroy. Анализатор покажет, как данные отправляются или принимаются в процессе обмена между устройством и хостом. В этом примере показываются трассировки, захваченные анализатором LeCroy USB.

Пример транзакции Bulk OUT. На рисунке ниже показана трассировка bulk OUT транзакции на высокой скорости (High Speed).

USB bulk OUT High Speed example

На показанной трассировке хост инициирует транзакцию bulk OUT в конечную точку high-speed bulk путем отправки пакета токена, где PID установлен в OUT (OUT token). Пакет содержит адрес устройства и его целевую конечную точку. После пакета OUT хост отправляет пакет данных, который содержит полезную нагрузку bulk-транзакции. Если конечная точка приняла пришедшие данные, то она отправляет пакет ACK (положительное подтверждение приема). В этом примере мы можем увидеть, что хост передал 31 байт в конечную точку 2 устройства 1 (device address:1; endpoint address: 2).

Если конечная точка занята в тот момент, когда поступил новый пакет данных, и она не может принять данные (это происходит в том случае, когда управляющий микроконтроллер устройства еще не успел обработать данные предыдущей bulk-транзакции), устройство может отправить пакет NAK (отрицательное подтверждение приема). В этом случае хост начинает отправлять в устройство пакеты PING. В ответ на эти пакеты устройство посылает пакеты NAK до тех пор, пока не будет снова готово принять данные. Когда устройство станет готовым, оно ответит пакетом ACK. После этого хост может возобновить передачу OUT.

Ниже показана трассировка транзакции SuperSpeed bulk OUT.

USB bulk OUT SuperSpeed example

В этом примере трассировки хост инициирует OUT-транзакцию на конечную точку SuperSpeed bulk путем отправки пакета данных (data). Пакет данных содержит полезную нагрузку (bulk payload), а также адреса устройства и конечной точки. На трассировке видно, что хост отправил 31 байт в конечную точку 2 устройства 4 (device address:4; endpoint address: 2).

Устройство принимает и подтверждает пакет данных, отправив хосту обратно пакет ACK. Если конечная точка занята в тот момент, как поступил новый пакет данных, и она не может принять эти данные, устройство может вместо ACK отправить пакет NRDY. В отличие от обмена High Speed, хост после приема пакета NRDY не будет опрашивать готовность устройства пакетами PING. Вместо этого хост ждет от устройства пакет ERDY. Когда устройство готово, оно посылает пакет ERDY, и теперь хост может снова посылать данные в конечную точку.

Пример транзакции Bulk IN. На рисунке ниже показана трассировка bulk IN транзакции на высокой скорости (High Speed).

USB bulk IN High Speed example

На показанной трассировке хост инициирует транзакцию отправкой пакета токена, у которого PID установлен в IN (IN token). Затем устройство отправит bulk-пакет данных с полезной нагрузкой. Если в конечной точке нет данных для отправки, или она еще не готова отправить данные, то устройство может отправить пакет рукопожатия NAK. Хост будет повторять попытки IN-передачи, пока не получит пакет ACK от устройства. Пакет ACK подразумевает, что устройство успешно передало данные.

Ниже показана трассировка транзакции SuperSpeed bulk IN.

USB bulk IN SuperSpeed example

Для инициации транзакции bulk IN от конечной точки SuperSpeed хост начинает bulk-транзакцию отправкой пакета ACK. Спецификация USB версии 3.0 оптимизирует эту начальную порцию передачи путем слияния пакетов ACK и IN в один ACK-пакет. Итак, вместо токена IN в режиме SuperSpeed хост отправляет токен ACK для инициации bulk-передачи. Устройство ответит пакетом данных. Затем хост подтвердит пакет данных отправкой пакета ACK. Если конечная точка занята и не может отправить данные, то устройство может отправить статус NRDY. В этом случае хост ждет, пока не получит пакет ERDY от устройства.

[Действия драйвера клиента USB для bulk-передачи]

Приложение или драйвер на хосте всегда инициирует bulk-передачу для отправки или приема данных. Драйвер клиента отправляет соответствующий запрос стеку драйверов USB. Драйвер стека USB программирует запрос в контроллер USB хоста, и затем отправляет пакеты протокола (как это было описано в предыдущей секции) по линку порта USB в устройство.

Давайте посмотрим, как драйвер клиента отправляет запрос для bulk-передачи как результат запроса от приложения или от другого драйвера. Альтернативно драйвер сам может инициировать эту передачу. Независимо от того, как все происходит, у драйвера должен быть буфер передачи и запрос, чтобы инициировать bulk-передачу.

Для драйвера Kernel-Mode Driver Framework (KMDF) запрос описан в объекте запроса фреймворка (см. Framework Request Object Reference [3]). Драйвер клиента вызывает методы объекта запроса путем указания дескриптора WDFREQUEST для отправки запроса стеку драйверов USB. Если драйвер клиента отправляет bulk-транзакцию в ответ на запрос приложения или другого драйвера, то фреймворк создает объект запроса и поставляет запрос драйверу клиента с помощью объекта очереди фреймворка (framework queue object). Тогда драйвер клиента может использовать этот запрос для целей отправки bulk-транзакции. Если драйвер клиента инициировал запрос, то драйвер может принять решение выделить свой собственный объект запроса.

Если приложение или другой драйвер отправил или запросил данные, драйверу передается буфер транзакции, предоставленный фреймворком. Альтернативно драйвер клиента может выделить буфер транзакции и создать объект запроса, если драйвер сам инициирует передачу.

Вот основные задачи драйвера клиента, которые он должен выполнить:

1. Получить буфер передачи.

2. Получить, отформатировать и послать framework request object в стек драйверов USB.

3. Реализовать стандартную подпрограмму завершения, чтобы получить оповещение, когда стек драйверов USB выполнит запрос.

Здесь эти задачи описываются на примере, в котором драйвер инициирует bulk-передачу в ответ на запрос от приложения для отправки или приема данных.

Для чтения данных из устройства драйвер клиента может использовать предоставленный фреймворком объект continuous reader. Для дополнительной информации см. статью [4].

[Пример запроса bulk-передачи]

В качестве примера рассмотрим сценарий, когда программа хочет прочитать данные из устройства USB или записать данные в него. Для отправки таких запросов приложение вызывает Windows API. В этом примере приложение открывает дескриптор для устройства путем использования идентификатора интерфейса устройства (device interface GUID), опубликованного Вашим драйвером в режиме ядра (kernel mode). Затем приложение вызывает ReadFile или WriteFile для инициирования запроса чтения или записи. В этом вызове приложение также указывает буфер, содержащий данные для читаемых или записываемых данных, и длину этого буфера.

Менеджер ввода/вывода (I/O Manager) принимает запрос, создает пакет запроса ввода/вывода (I/O Request Packet, IRP), и перенаправляет его драйверу клиента.

Фреймворк перехватывает этот запрос, создает объект запроса (framework request object), и добавляет его к объекту очереди (framework queue object). Затем фреймворк оповещает драйвер клиента, что новый запрос ожидает обработки. Оповещения реализуются путем запуска подпрограмм обратного вызова драйвера очереди (driver’s queue callback) для EvtIoRead или EvtIoWrite (обработка событий чтения или записи ввода/вывода).

Когда платформа поставляет запрос драйверу клиента, он получает следующие параметры:

• Дескриптор WDFQUEUE для объекта очереди (framework queue object) который содержит запрос.

• Дескриптор WDFREQUEST для объекта запроса (framework request object), в котором содержится информация об этом запроса.

• Длина передачи, т. е. количество байт, которые нужно прочитать или записать.

В реализации драйвера клиента обработчиков EvtIoRead или EvtIoWrite драйвер инспектирует параметры запроса и может опционально выполнять проверки на корректность.

Если Вы используете потоки на конечной точке SuperSpeed bulk, то будете отправлять запрос в URB, потому что KMDF не поддерживает потоки внутри себя. Для информации по поводу предоставления запроса для передачи потокам конечной точки bulk см. [7].

Если Вы не используете потоки, то можете использовать методы, определенные KMDF, для отправки запроса, следуя описанной ниже пошаговой процедуре.

Предварительные требования. Перед тем как начать, убедитесь, что у Вас есть следующая информация:

• У драйвера клиента должен быть созданный объект целевого устройства USB фреймворка, и получен дескриптор WDFUSBDEVICE путем вызова метода WdfUsbTargetDeviceCreateWithParameters.

Если Вы используете шаблоны USB, которые предоставляет Microsoft Visual Studio Professional 2012, то эти задачи выполняет код шаблона. Код шаблона получает дескриптор для объекта целевого устройства и сохраняет его в контексте устройства. Для дополнительной информации см. «Device source code» в статье [5].

• Дескриптор WDFREQUEST для объекта запроса фреймворка, который содержит информацию по этому запросу.

• Количество читаемых или записываемых байт.

• Дескриптор WDFUSBPIPE для объекта канала фреймворка (framework pipe object), связанного с целевой конечной точкой. Вы для этого должны получить дескрипторы каналов во время конфигурации устройства путем перечисления каналов USB (см. [6]).

Если конечная точка поддерживает потоки (stream), то Вы должны иметь дескриптор канала (pipe handle) для потока (см. [7]).

Шаг 1: получение буфера передачи. Буфер передачи, или transfer buffer MDL (MDL расшифровывается как memory descriptor list), содержит данные для передачи или приема. Эта подразумевает, что Вы передаете или принимаете данные в буфере передачи. Буфер передачи описан в объекте памяти WDF (WDF расшифровывается как Windows Driver Framework, см. [8]). Чтобы получить объект памяти, связанный с буфером передачи, вызовите один из этих методов:

• Для запроса передачи bulk IN вызовите метод WdfRequestRetrieveOutputMemory.

• Для запроса передачи bulk OUT вызовите метод WdfRequestRetrieveInputMemory.

Драйверу клиента не нужно освобождать эту память. Эта память связанна с родительским объектом запроса, и она будет освобождена, когда будет освобожден родитель.

Шаг 2: форматирование и отправка объекта запроса драйверам стека USB. Вы можете отправить запрос асинхронно (без блокировки хода вычислений на ожидании результата) или синхронно (подразумевает бесконечную блокировку, пока не будет выполнен запрос).

Асинхронные методы:

WdfUsbTargetPipeFormatRequestForRead
WdfUsbTargetPipeFormatRequestForWrite

Эти два метода форматируют запрос. Если Вы отправляете запрос асинхронно, то установите указатель на реализованную драйвером подпрограмму завершения, путем вызова метода WdfRequestSetCompletionRoutine (описано на следующем шаге). Для отправки запроса вызовите WdfRequestSend.

Для синхронной отправки запроса вызовите методы:

WdfUsbTargetPipeReadSynchronously
• WdfUsbTargetPipeWriteSynchronously

Шаг 3: реализуйте для запроса подпрограмму завершения. Если запрос отправляется асинхронно, то Вы должны реализовать обработчик события завершения (completion routine), чтобы получить оповещение о том, что драйвера стека USB завершили обработку запроса. По завершении фреймворк вызовет этот обработчик завершения с передачей следующих параметров:

• Дескриптор WDFREQUEST для объекта запроса.

• Дескриптор WDFIOTARGET для целевого объекта ввода/вывода (I/O target object) запроса.

• Указатель на структуру WDF_REQUEST_COMPLETION_PARAMS, которая содержит информацию о завершении. Информация, специфичная для USB, содержится в поле CompletionParams->Parameters.Usb этой структуры.

• Дескриптор WDFCONTEXT, указывающий на контекст, который указал драйвер в своем вызове WdfRequestSetCompletionRoutine.

В обработчике завершения (completion routine), выполните следующие действия:

• Проверьте статус запроса в значении CompletionParams->IoStatus.Status.

• Проверьте USBD статус, установленный стеком драйверов USB.

• В случае ошибок на канале (pipe errors) выполните операции по восстановлению из состояния ошибки (см. [9]).

• Проверьте количество переданных (принятых или отправленных) байт.

Передача bulk завершена, когда запрошенное количество байт передано в устройство или из него. Если Вы отправили буфер запроса вызовом метода KMDF, то проверьте значение, принятое в поле CompletionParams->Parameters.Usb.Completion->Parameters.PipeWrite.Length или CompletionParams->Parameters.Usb.Completion->Parameters.PipeRead.Length.

В простой передаче, где стек драйверов USB передал все запрошенные данные в одном пакете, Вы можете сравнить длину Length с количеством запрошенных байт. Если драйверы стека USB обрабатывают запрос в нескольких пакетах данных, Вы должны отслеживать количество переданных данных и оставшееся количество байт.

• Если было передано все запрошенное количество байт, то запрос завершен. Если произошла ошибка (error condition), запрос завершится с возвратом кода ошибки. Завершите запрос вызовом WdfRequestComplete. Если Вы хотите установить информацию, такую как количество переданных байт, вызовите WdfRequestCompleteWithInformation.

• Удостоверьтесь, когда завершаете запрос, что количество байт должны быть равно или меньше количество запрошенных байт. Фреймворк проверяет эти значения. Если количество, установленное в завершенном запросе, больше количества в оригинальном запросе, то может произойти bugcheck (критическая ошибка Windows).

Листинг примера кода ниже показывает, как драйвер клиента может отправить запрос bulk-передачи. Драйвер также устанавливает подпрограмму завершения, которая показана во втором блоке кода.

/*
Эта подпрограмма посылает запрос bulk write в стек драйверов USB.
Запрос отправляется асинхронно, и драйвер оповещается подпрограммой
завершения.
 
Аргументы:
   Queue - дескриптор объекта очереди (framework queue object).
   Request - дескриптор объекта запроса (framework request object).
   Length - количество байт для передачи.
 
Подпрограмма не возвращает никакого значения.
*/
VOID Fx3EvtIoWrite(IN WDFQUEUE Queue,
                   IN WDFREQUEST Request,
                   IN size_t Length)
{
   NTSTATUS  status;
   WDFUSBPIPE  pipe;
   WDFMEMORY  reqMemory;
   PDEVICE_CONTEXT  pDeviceContext;
 
   pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));
   pipe = pDeviceContext->BulkWritePipe;
   status = WdfRequestRetrieveInputMemory(Request,
                                          &reqMemory);
   if (!NT_SUCCESS(status))
   {
      goto Exit;
   }
 
   status = WdfUsbTargetPipeFormatRequestForWrite(pipe,
                                                  Request,
                                                  reqMemory,
                                                  NULL);
   if (!NT_SUCCESS(status))
   {
      goto Exit;
   }
 
   WdfRequestSetCompletionRoutine(Request,
                                  BulkWriteComplete,
                                  pipe);
   if (WdfRequestSend(Request,
                      WdfUsbTargetPipeGetIoTarget(pipe),
                      WDF_NO_SEND_OPTIONS) == FALSE)
   {
      status = WdfRequestGetStatus(Request);
      goto Exit;
   }
 
Exit:
   if (!NT_SUCCESS(status))
   {
      WdfRequestCompleteWithInformation(Request,
                                        status,
                                        0);
   }
   return;
}

Ниже показан пример кода подпрограммы завершения для bulk-передачи. Драйвер клиента завершает запрос в подпрограмме завершения и устанавливает следующую информацию запроса: статус и количество переданных байт.

/*
Это подпрограмма завершения (completion routine), которую вызовет фреймворк,
когда стек драйверов USB завершит ранее отправленный запрос bulk write.
Драйвер клиента завершает запрос, если в устройство было передано полное
количество запрошенных байт.
 
В случае отказа это ставит рабочий элемент очереди на начало процедуры
восстановления ошибки путем сброса целевого канала (target pipe).
 
Аргументы:
   Queue - дескриптор объекта очереди (framework queue object).
   Request - дескриптор объекта запроса (framework request object).
   Length - количество байт для передачи.
   Pipe - дескриптор канала, который является целью этого запроса.
 
Подпрограмма не возвращает никакого значения.
*/
VOID BulkWriteComplete(_In_ WDFREQUEST Request,
                       _In_ WDFIOTARGET Target,
                       PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
                       _In_ WDFCONTEXT Context)
{
   PDEVICE_CONTEXT deviceContext;
   size_t bytesTransferred=0;
   NTSTATUS status;
 
   UNREFERENCED_PARAMETER (Target);
   UNREFERENCED_PARAMETER (Context);
 
   KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
             "In completion routine for Bulk transfer.n"));
 
   // Получение контекста устройства. Это структура контекста, которую драйвер
   // клиента предоставил в момент отправки запроса.
   deviceContext = (PDEVICE_CONTEXT)Context;
 
   // Получение статуса запроса.
   status = CompletionParams->IoStatus.Status;
   if (!NT_SUCCESS (status))
   {
      // Получение кода статуса USBD для дополнительной информации
      // о состоянии ошибки.
      status = CompletionParams->Parameters.Usb.Completion->UsbdStatus;
      KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
                "Bulk transfer failed. 0x%xn",
                status));
      // Постановка рабочего элемента в очередь, чтобы начать операцию
      // сброса на канале (не показано). 
      goto Exit; 
   } 
 
   // Получение реального количества переданных байт.
   bytesTransferred =
      CompletionParams->Parameters.Usb.Completion->Parameters.PipeWrite.Length; 
   KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
             "Bulk transfer completed. Transferred %d bytes. n",
             bytesTransferred));
 
Exit:
   // Завершение запроса и обновление запроса информацией кода статуса
   // и количеством переданных байт.
   WdfRequestCompleteWithInformation(Request, status, bytesTransferred);
   return;
}

[Ссылки]

1. How to send USB bulk transfer requests site:msdn.microsoft.com.
2. Фреймы и типы передач USB.
3. Framework Request Object Reference site:msdn.microsoft.com.
4. How to use the continuous reader for reading data from a USB pipe site:msdn.microsoft.com.
5. Understanding the USB client driver code structure (KMDF) site:msdn.microsoft.com.
6. How to enumerate USB pipes site:msdn.microsoft.com.
7. How to open and close static streams in a USB bulk endpoint site:msdn.microsoft.com.
8. Framework Memory Object Reference site:msdn.microsoft.com.
9. How to recover from USB pipe errors site:msdn.microsoft.com.

The following documentation is based on libusb-0.1 API documentation (http://libusb.sourceforge.net/doc/) by Johannes Erdfelt, but added with libusb-win32 specific information.

Preface
The purpose of this document is to explain the libusb-win32 API and how to use it to make a USB aware application. Any suggestions, corrections and comments regarding this document should be sent to the libusb-win32 developers mailing list.

I. Introduction

This documentation will provide an overview of how the v0.1 libusb API works and relates to USB. It is assumed that the reader has a good understanding of the USB 2.0 specification. The USB 2.0 specification can be found the USB Implementers Forum website (http://www.usb.org). libusb-0.1 works under Linux, FreeBSD, NetBSD, OpenBSD; Darwin/MacOS X and Solaris. libusb-win32 is API compatible with libusb-0.1 but also includes some new features.

II. API

This is the external API for applications to use. The API is relatively lean and designed to closely correspond with the USB 2.0 specification.

Devices and interfaces

The libusb API ties an open device to a specific interface. This means that if you want to claim multiple interfaces on a device, you should open the device multiple times to receive one usb_dev_handle for each interface you want to communicate with. Don’t forget to call usb_claim_interface().

Timeouts in libusb are always specified in milliseconds.

libusb uses both abstracted and non abstracted structures to maintain portability.

All functions in the original libusb v0.1 are synchronous, meaning the functions block and wait for the operation to finish or timeout before returning execution to the calling application. libusb-win32 adds some asynchronous APIs. libusb-1.0 has more asynchronous API support.

There are two types of return values used in libusb v0.1. The first is a handle returned by usb_open(). The second is an int. In all cases where an int is returned, >= 0 is a success and < 0 is an error condition.

Timeout
The unit for timeout value is ms.

Under Linux libusb-0.1 (which only supports synchronous API), timeout value of 0 means infinite. libusb-win32 version 1.2.4.7 and later will follow this for synchronous API. Before that, it behaves differently from Linux libusb-0.1.

Since Windows (same for Linux and Mac OS X) is not an RTOS, it is not a good idea to use small timeout value like 10ms or 100ms.

Caveats of synchronous transfer when timeout happens
How synchronous API works:
1) Submits read request to driver
2) Waits the specified timeout using WaitForSingleObject()
a. if a wait timeout occurred, send abort pipe request and return -116
b. if wait succeeded, get transfer results via GetOverlappedResults() and return error or transfer length.

So, if a transfer completes just after a timeout is detected in 2a, the entire transfer is lost.

There are currently a couple ways to avoid this:
1) Use the async transfer functions and usb_reap_async_nocancel()
2) Use the sync transfer functions from within their own thread and
always use INFINITE for the timeout.

Reported Error Codes

/* Connection timed out */
#define ETRANSFER_TIMEDOUT 116

Standard Error Codes from WDK crt errno.h and the explanation from MinGW are listed here. Take note that not all error codes below are used.

#define EPERM       1   /* Operation not permitted */ 
#define ENOENT      2   /* No entry, ENOFILE, no such file or directory */
#define ESRCH       3   /* No such process */
#define EINTR       4   /* Interrupted function call */
#define EIO     5   /* Input/output error */
#define ENXIO       6   /* No such device or address */
#define E2BIG       7   /* Arg list too long */
#define ENOEXEC     8   /* Exec format error */
#define EBADF       9   /* Bad file descriptor */
#define ECHILD      10  /* No child processes */
#define EAGAIN      11  /* Resource temporarily unavailable */
#define ENOMEM      12  /* Not enough space */
#define EACCES      13  /* Permission denied */
#define EFAULT      14  /* Bad address */
#define EBUSY       16  /* strerror reports "Resource device" */
#define EEXIST      17  /* File exists */
#define EXDEV       18  /* Improper link (cross-device link?) */
#define ENODEV      19  /* No such device */
#define ENOTDIR     20  /* Not a directory */
#define EISDIR      21  /* Is a directory */
#define EINVAL      22  /* Invalid argument */
#define ENFILE      23  /* Too many open files in system */
#define EMFILE      24  /* Too many open files */
#define ENOTTY      25  /* Inappropriate I/O control operation */
#define EFBIG       27  /* File too large */
#define ENOSPC      28  /* No space left on device */
#define ESPIPE      29  /* Invalid seek (seek on a pipe?) */
#define EROFS       30  /* Read-only file system */
#define EMLINK      31  /* Too many links */
#define EPIPE       32  /* Broken pipe */
#define EDOM        33  /* Domain error (math functions) */
#define ERANGE      34  /* Result too large (possibly too small) */
#define EDEADLK     36      /* Resource deadlock avoided */
#define ENAMETOOLONG    38  /* Filename too long */
#define ENOLCK      39  /* No locks available */
#define ENOSYS      40  /* Function not implemented */
#define ENOTEMPTY   41  /* Directory not empty */

III. Functions

1. Core

These functions comprise the core of libusb. They are used by all applications that utilize libusb.

usb_init()
Name 
usb_init -- Initialize libusb
Description
void usb_init(void);

Just like the name implies, usb_init sets up some internal structures. usb_init must be called before any other libusb functions.

usb_find_busses()
Name
usb_find_busses -- Finds all USB busses on system
Description
int usb_find_busses(void);

usb_find_busses will find all of the busses on the system. Returns the number of changes since previous call to this function (total of new busses and busses removed).

usb_find_devices()
Name
usb_find_devices -- Find all devices on all USB devices
Description
int usb_find_devices(void);

usb_find_devices() will find all of the devices on each bus. This should be called after usb_find_busses(). Returns the number of changes since the previous call to this function (total of new device and devices removed).

usb_get_busses()
Name
usb_get_busses -- Return the list of USB busses found
Description
struct usb_bus *usb_get_busses(void);

usb_get_busses() simply returns the value of the global variable usb_busses. This was implemented for those languages that support C calling convention and can use shared libraries, but don't support C global variables (like Delphi).

usb_set_debug()
Name
usb_set_debug -- set debug message verbose level
Description
void usb_set_debug(int level);

usb_set_debug() sets the debug message verbose level. You can set it to 4 to print the debug message which is quite verbose.
 0  LOG_OFF,
 1  LOG_ERROR,
 2  LOG_WARNING,
 3  LOG_INFO,
 4  LOG_DEBUG

2. Device operations

This group of functions deal with the device. It allows you to open and close the device as well standard USB operations like setting the configuration, alternate settings, clearing halts and resetting the device. It also provides OS level operations such as claiming and releasing
interfaces.

usb_open()
Name
usb_open -- Opens a USB device
Description
usb_dev_handle *usb_open(struct *usb_device dev);

usb_open() is to be used to open up a device for use. usb_open must be called before attempting to perform any operations to the device. Returns a handle used in future communication with the device.

usb_close()
Name
usb_close -- Closes a USB device
Description
int usb_close(usb_dev_handle *dev);

usb_close closes a device opened with usb_open. No further operations may be performed on the handle after usb_close is called. Returns 0 on success or < 0 on error.

usb_set_configuration()
Name
usb_set_configuration -- Sets the active configuration of a device
Description
int usb_set_configuration(usb_dev_handle *dev, int configuration);

usb_set_configuration sets the active configuration of a device. The configuration parameter is the value as specified in the descriptor field bConfigurationValue. Returns 0 on success or < 0 on error.

    '''Must be called'''!: usb_set_configuration must be called with a valid configuration (not 0) before you can claim the interface. This might not be necessary in the future. This behavior is different from Linux libusb-0.1.

usb_set_altinterface()
Name
usb_set_altinterface -- Sets the active alternate setting of the current interface
Description
int usb_set_altinterface(usb_dev_handle *dev, int alternate);

usb_set_altinterface() sets the active alternate setting of the current interface. The alternate parameter is the value as specified in the descriptor field bAlternateSetting. Returns 0 on success or < 0 on error.

usb_resetep()
Name
usb_resetep -- Resets state for an endpoint
Description
int usb_resetep(usb_dev_handle *dev, unsigned int ep);

usb_resetep resets all state (like toggles) for the specified endpoint. The ep parameter is the value specified in the descriptor field bEndpointAddress. Returns 0 on success or < 0 on error.
    '''Deprecated''': usb_resetep is deprecated. You probably want to use usb_clear_halt.

usb_clear_halt()
Name
usb_clear_halt -- Clears any halt status on an endpoint
Description
int usb_clear_halt(usb_dev_handle *dev, unsigned int ep);

usb_clear_halt() clears any halt status on the specified endpoint. The ep parameter is the value specified in the descriptor field bEndpointAddress. Returns 0 on success or < 0 on error.

usb_reset()
Name
usb_reset -- Resets a device
Description
int usb_reset(usb_dev_handle *dev);
usb_reset() resets the specified device by sending a RESET down the port it is connected to. Returns 0 on success or < 0 on error.

    '''Causes re-enumeration''': After calling usb_reset, the device will need to re-enumerate and thus requires you to find the new device and open a new handle. The handle used to call usb_reset() will no longer work.

usb_claim_interface()
Name
usb_claim_interface -- Claim an interface of a device
Description
int usb_claim_interface(usb_dev_handle *dev, int interface);

usb_claim_interface() claims the interface with the Operating System. The interface parameter is the value as specified in the descriptor field bInterfaceNumber. Returns 0 on success or < 0 on error.
    '''Must be called'''!: usb_claim_interface must be called before you perform any operations related to this interface (like usb_set_altinterface, usb_bulk_write, etc).

Return Codes for usb_claim_interface():
code    description
-EBUSY  Interface is not available to be claimed
-ENOMEM Insufficient memory

usb_release_interface()
Name
usb_release_interface -- Releases a previously claimed interface
Description
int usb_release_interface(usb_dev_handle *dev, int interface);

usb_release_interface() releases an interface previously claimed with usb_claim_interface. The interface parameter is the value as specified in the descriptor field bInterfaceNumber. Returns 0 on success or < 0 on error.

3. Control Transfers

This group of functions allow applications to send messages to the default control pipe.

usb_control_msg()
Name
usb_control_msg -- Send a control message to a device
Description
int usb_control_msg(usb_dev_handle *dev, int requesttype, int request, int value, int index, char *bytes, int size, int timeout);

usb_control_msg() performs a control request to the default control pipe on a device. The parameters mirror the types of the same name in the USB specification. Returns number of bytes written/read or < 0 on error.

usb_get_string()
Name
usb_get_string -- Retrieves a string descriptor from a device
Description
int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf, size_t buflen);

usb_get_string() retrieves the string descriptor specified by index and langid from a device. The string will be returned in Unicode as specified by the USB specification. Returns the number of bytes returned in buf or < 0 on error.

usb_get_string_simple()
Name
usb_get_string_simple -- Retrieves a string descriptor from a device using the first language
Description
int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen);

usb_get_string_simple() is a wrapper around usb_get_string that retrieves the string description specified by index in the first language for the descriptor and converts it into C style ASCII. Returns number of bytes returned in buf or < 0 on error.

usb_get_descriptor()
Name
usb_get_descriptor -- Retrieves a descriptor from a device's default control pipe
Description
int usb_get_descriptor(usb_dev_handle *dev, unsigned char type, unsigned char index, void *buf, int size);

usb_get_descriptor() retrieves a descriptor from the device identified by the type and index of the descriptor from the default control pipe. Returns number of bytes read for the descriptor or < 0 on error.
See usb_get_descriptor_by_endpoint() for a function that allows the control endpoint to be specified.

usb_get_descriptor_by_endpoint()
Name
usb_get_descriptor_by_endpoint -- Retrieves a descriptor from a device
Description
int usb_get_descriptor_by_endpoint(usb_dev_handle *dev, int ep, unsigned char type, unsigned char index, void *buf, int size);

usb_get_descriptor_by_endpoint() retrieves a descriptor from the device identified by the type and index of the descriptor from the control pipe identified by ep. Returns number of bytes read for the descriptor or < 0 on error.

4. Bulk Transfers

This group of functions allow applications to send and receive data via bulk pipes.

usb_bulk_write()
Name
usb_bulk_write -- Write data to a bulk endpoint
Description
int usb_bulk_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

usb_bulk_write() performs a bulk write request to the endpoint specified by ep. Returns number of bytes written on success or < 0 on error.

usb_bulk_read()
Name
usb_bulk_read -- Read data from a bulk endpoint
Description
int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

usb_bulk_read() performs a bulk read request to the endpoint specified by ep. Returns number of bytes read on success or < 0 on error.

5. Interrupt Transfers

This group of functions allow applications to send and receive data via interrupt pipes.

usb_interrupt_write()
Name
usb_interrupt_write -- Write data to an interrupt endpoint
Description
int usb_interrupt_write(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

usb_interrupt_write() performs an interrupt write request to the endpoint specified by ep. Returns number of bytes written on success or < 0 on error.

usb_interrupt_read()
Name
usb_interrupt_read -- Read data from a interrupt endpoint
Description
int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size, int timeout);

usb_interrupt_read performs a interrupt read request to the endpoint specified by ep. Returns number of bytes read on success or < 0 on error.

6. Asynchronous API and Isochronous Transfer

»’Windows Specific»’

libusb-win32 supports Isochronous Transfer through its Asynchronous API. The libusb-win32 Asynchronous API also supports the other transfer types like Control Transfer, Interrupt Transfer and Bulk Transfer.

int usb_isochronous_setup_async(usb_dev_handle *dev, void **context, unsigned char ep, int pktsize);

   Allocates an asynchonous request for endpoint 'ep' and returns that request in 'context'.
   Returns 0 on success, < 0 on failure.

int usb_bulk_setup_async(usb_dev_handle *dev, void **context, unsigned char ep);

   Allocates an asynchonous request for endpoint 'ep' and returns that request in 'context'.
   Returns 0 on success, < 0 on failure.

int usb_interrupt_setup_async(usb_dev_handle *dev, void **context, unsigned char ep);

   Allocates an asynchonous request for endpoint 'ep' and returns that request in 'context'.
   Returns 0 on success, < 0 on failure.

int usb_submit_async(void *context, char *bytes, int size);

   Submits a previously allocated request to the device. Data pointed to by 'bytes' of size 'size' will be written or read to or from the device depending on the endpoint's direction bit. 
   Returns 0 on success, < 0 on failure.

int usb_reap_async(void *context, int timeout);

   Waits for the request to finish. Returns the number of bytes written/read or < 0 on failure.
   The request will be canceled if it doesn't complete within 'timeout' (in ms).

int usb_reap_async_nocancel(void *context, int timeout);

   Same as usb_reap_async() but doesn't cancel the request if it times out. Therefore you can try to reap it later.

int usb_cancel_async(void *context);

   Cancels a request.

int usb_free_async(void **context);

   Frees a request.

* Usage:

 char data[1024];
 void *request;
 int read;
 if(usb_bulk_setup_async(dev, &request, 0x81) < 0) {
   // error handling
 }
 if(usb_submit_async(request, data, sizeof(data)) < 0) {
   // error handling
 }
 read =  usb_reap_async(request, 1000);
 if(read >= 0)
 printf("read %d bytesn", read);
 else
   // error handling
 usb_free_async(&request);

7. Non Portable

These functions are non portable. They may expose some part of the USB API on one OS or perhaps more, but not all. They are all marked with the string _np at the end of the function name.

A C preprocessor macro will be defined if the function is implemented. The form is LIBUSB_HAS_ prepended to the function name, without the leading «usb_», in all caps. For example, if usb_get_driver_np is implemented, LIBUSB_HAS_GET_DRIVER_NP will be defined.

*Linux Specific*

usb_get_driver_np()
Name
usb_get_driver_np -- Get driver name bound to interface
Description
int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name, int namelen);

This function will obtain the name of the driver bound to the interface specified by the parameter interface and place it into the buffer named name limited to namelen characters. Returns 0 on success or < 0 on error. Implemented on Linux only.

usb_detach_kernel_driver_np()
Name
usb_detach_kernel_driver_np -- Detach kernel driver from interface
Description
int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface);

This function will detach a kernel driver from the interface specified by parameter interface. Applications using libusb can then try claiming the interface. Returns 0 on success or < 0 on error. Implemented on Linux only.

*Windows Specific*

    /* Windows specific functions */

const struct usb_version *usb_get_version(void); 
    Return the library version.

The following functions are related to driver installation (device driver mode of filter driver mode). Normally you should not use them. You can bundle the GUI Inf-Wizard with your application. Or you can choose to write your own driver installation applications.

libwdi (http://libwdi.sf.net/) or WDK DIFX (http://msdn.microsoft.com/en-us/library/ff544838%28v=VS.85%29.aspx) can be used for that purpose. You can combine libwdi/DIFx with installers like Inno Setup (http://www.jrsoftware.org/isinfo.php) or NSIS (http://nsis.sourceforge.net/).

    #define LIBUSB_HAS_INSTALL_SERVICE_NP 1
    int usb_install_service_np(void);
    void CALLBACK usb_install_service_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show);

    #define LIBUSB_HAS_UNINSTALL_SERVICE_NP 1
    int usb_uninstall_service_np(void);
    void CALLBACK usb_uninstall_service_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show);

    #define LIBUSB_HAS_INSTALL_DRIVER_NP 1
    int usb_install_driver_np(const char *inf_file);
    void CALLBACK usb_install_driver_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show);

    #define LIBUSB_HAS_TOUCH_INF_FILE_NP 1
    int usb_touch_inf_file_np(const char *inf_file);
    void CALLBACK usb_touch_inf_file_np_rundll(HWND wnd, HINSTANCE instance, LPSTR cmd_line, int cmd_show);

    #define LIBUSB_HAS_INSTALL_NEEDS_RESTART_NP 1
    int usb_install_needs_restart_np(void);

More details to Be Added.

IV. Examples

There are some non-intuitive parts of libusb v0.1 that aren’t difficult, but are probably easier to understand with some examples.

Basic Examples

Before any communication can occur with a device, it needs to be found. This is accomplished by finding all of the busses and then finding all of the devices on all of the busses:

{{{
        struct usb_bus *busses;

        usb_init();
        usb_find_busses();
        usb_find_devices();

        busses = usb_get_busses();
}}}

After this, the application should manually loop through all of the busess and all of the devices and matching the device by whatever criteria is needed:

    struct usb_bus *bus;
    int c, i, a;

    /* ... */

    for (bus = busses; bus; bus = bus->next) {
        struct usb_device *dev;

        for (dev = bus->devices; dev; dev = dev->next) {
            /* Check if this device is a printer */
            if (dev->descriptor.bDeviceClass == 7) {
                /* Open the device, claim the interface and do your processing */
                ...
            }

            /* Loop through all of the configurations */
            for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
                /* Loop through all of the interfaces */
                for (i = 0; i < dev->config[c].bNumInterfaces; i++) {
                    /* Loop through all of the alternate settings */
                    for (a = 0; a < dev->config[c].interface[i].num_altsetting; a++) {
                        /* Check if this interface is a printer */
                        if (dev->config[c].interface[i].altsetting[a].bInterfaceClass == 7) {
                        /* Open the device, set the alternate setting, claim the interface and do your processing */
                           ...
                        }
                    }
                }
            }
        }
    }

Понравилась статья? Поделить с друзьями:
  • Usb audio device ошибка драйвера
  • Usb a disk read error occurred press ctrl alt del to restart
  • Us333 ошибка ржд
  • Us0 ошибка хлебопечка панасоник
  • Urp error subsystem renderer internalerror 0xc8 line number 1857