1. Как понять структуру гаджета
Протокол USB представляет собой структуру «ведущий-подчиненный»:
Левый — хост, правый — ведомый; USB имеет хост-контроллер UHC и ведомый контроллер UDC. На стороне хоста имеется драйвер устройства USB, а на ведомой стороне — функциональный драйвер USB.
Это означает, что макетная плата может использоваться в качестве USB-хоста и подключаться к мыши, клавиатуре и другим подчиненным машинам. Макетная плата также может использоваться в качестве USB-накопителя и подключаться к ПК, и в этом случае макетная плата является макетной платой; рабская машина. Поэтому нам необходимо освоить два набора драйверных фреймворков. Здесь USB под Linux сложнее, чем I2C и I2C и т.п. вообще не будут считать главный Soc подчиненным, а вот USB нужно учитывать.
В этой статье основное внимание уделяется ситуации, когда главный Soc действует как подчиненный USB-устройство, а Linux предоставляет для него платформу гаджетов.
При написании драйвера USB-устройства необходимо выполнить следующие основные действия:
При моделировании USB-устройства на основе платформы драйверов гаджетов возможности передачи данных конечной точки обеспечиваются базовым драйвером контроллера USB-устройства. Все, что нам нужно сделать, это:
Гаджет означает «маленькое устройство». В USB-системе Linux это означает «USB-устройство». Драйвер гаджета используется для имитации USB-устройства. Настоящее USB-устройство состоит из двух основных элементов:
В процессе обучения очень полезно помнить следующие несколько моментов:
Ядром передачи USB является конечная точка, которую можно использовать для отправки и получения данных. В верхней части конечной точки вы можете имитировать последовательный порт USB, сенсорный экран USB и камеру USB. С этой точки зрения структуру гаджета можно разделить на два уровня:
На разных платформах используются разные модели USB-контроллера. Метод подтверждения модели состоит в том, чтобы декомпилировать из dtb, найти узел, содержащий символ otg, и выполнить поиск совместимого узла dts в коде Linux, чтобы найти соответствующий драйвер подчиненного USB-контроллера.
Код базовой конечной точки нужно начать анализировать с драйвера UDC:
Linux-4.9.88\drivers\usb\chipidea\ci_hdrc_imx.c
ci_hdrc_imx_probe
ci_hdrc_add_device
pdev = platform_device_alloc("ci_hdrc", id);
// Linux-4.9.88\drivers\usb\chipidea\core.c
static struct platform_driver ci_hdrc_driver = {
.probe = ci_hdrc_probe,
.remove = ci_hdrc_remove,
.driver = {
.name = "ci_hdrc",
.pm = &ci_pm_ops,
},
};
ci_hdrc_probe
ret = ci_hdrc_gadget_init(ci);
udc_start
Linux-5.4\drivers\usb\dwc2\platform.c
dwc2_driver_probe
retval = dwc2_gadget_init(hsotg);
Как работает программное обеспечение при моделировании различных USB-устройств? В качестве примера рассмотрим доступ к устройству и получение дескриптора:
Поэтому с точки зрения получения дескрипторов программное обеспечение верхнего уровня делится как минимум на 2 уровня:
Уровень программного обеспечения можно доработать, как показано ниже:
Здесь задействованы 2 структуры:
struct usb_composite_dev {
struct usb_gadget *gadget;
struct usb_request *req;
struct usb_request *os_desc_req;
struct usb_configuration *config;
/* OS String is a custom (yet popular) extension to the USB standard. */
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
u8 b_vendor_code;
struct usb_configuration *os_desc_config;
unsigned int use_os_string:1;
/* private: */
/* internals */
unsigned int suspended:1;
struct usb_device_descriptor desc;
struct list_head configs;
struct list_head gstrings;
struct usb_composite_driver *driver;
u8 next_string_id;
char *def_manufacturer;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
/* the composite driver won't complete the control transfer's
* data/status stages till delayed_status is zero.
*/
int delayed_status;
/* protects deactivations and delayed_status counts*/
spinlock_t lock;
/* public: */
unsigned int setup_pending:1;
unsigned int os_desc_pending:1;
};
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device dev;
struct list_head list;
bool vbus;
};
Предположим, вы хотите [эмулировать] USB-устройство:
Возьмем, к примеру, ноль.c:
Эти файлы задействованы снизу вверх:
При чтении читайте исходный код,Входфункциядаusb_composite_probe(&zero_driver)
:
Основные функции в процессе вызова функции следующие. Обратите внимание на функцию «xxx_bind», которая означает инициализацию:
Углубленное объяснение процесса построения дескриптора можно привести к следующему рисунку:
После установки драйвера гаджета (например, modprobe g_zero) он просто конструирует различные дескрипторы. Дескриптор считывается во время перечисления устройств.
При использовании кабеля OTG для подключения компьютера и макетной платы программное обеспечение компьютера выполняет следующие операции:
В описанном выше процессе устройство получает данные, отправленные хостом в конечную точку 0, а затем отвечает. Разные устройства-гаджеты выполняют одни и те же операции при возврате дескрипторов хосту, но данные, на которые они отвечают, различны. Отправной точкой анализа исходного кода всегда является функция прерывания:
IMX6ULL в чипе USB Модель контроллера chipidea,существоватьLinux-4.9.88\drivers\usb\chipidea\core.c
прерывание зарегистрировано вфункция:
ci_hdrc_probe
ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
ci->platdata->name, ci);
После возникновения прерывания поток обработки данных для конечной точки 0 выглядит следующим образом:
// Linux-4.9.88\drivers\usb\chipidea\core.c
ci_irq
/* Handle device/host interrupt */
if (ci->role != CI_ROLE_END)
ret = ci_role(ci)->irq(ci); // udc_irq
// Linux-4.9.88\drivers\usb\chipidea\udc.c
udc_irq
if (USBi_UI & intr)
// Linux-4.9.88\drivers\usb\chipidea\udc.c
isr_tr_complete_handler(ci);
/* Only handle setup packet below */
if (i == 0 &&
hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
// Linux-4.9.88\drivers\usb\chipidea\udc.c
isr_setup_packet_handler(ci);
функцияisr_setup_packet_handler
Сразудаиметь дело с endpoint 0 Получен ключ для управления передачей.
STM32MP157 в чипе USB Модель контроллера dwc2,существоватьLinux-5.4\drivers\usb\dwc2\gadget.c
прерывание зарегистрировано вфункция:
dwc2_gadget_init
ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq,
IRQF_SHARED, dev_name(hsotg->dev), hsotg);
После перерыва,функцияdwc2_hsotg_irq
называется,этоиметь дело с endpoint Есть два способа прервать:
dwc2_hsotg_epint
справитьсяdwc2_hsotg_handle_rx
справитьсякdwc2_hsotg_epint
Возьмем в качестве примераанализировать,для endpoint 0 Порядок обработки данных следующий:
// Linux-5.4\drivers\usb\dwc2\gadget.c
dwc2_hsotg_irq
// Обработка прерываний конечной точки
for (ep = 0; ep < hsotg->num_of_eps && daint_out; ep++, daint_out >>= 1) {
if (daint_out & 1)
dwc2_hsotg_epint(hsotg, ep, 0);
}
for (ep = 0; ep < hsotg->num_of_eps && daint_in; ep++, daint_in >>= 1) {
if (daint_in & 1)
dwc2_hsotg_epint(hsotg, ep, 1);
}
функцияdwc2_hsotg_epint
середина,для endpoint 0 Обработка заключается в следующем:
// Linux-5.4\drivers\usb\dwc2\gadget.c
dwc2_hsotg_epint
if (idx == 0 && !hs_ep->req)
dwc2_hsotg_enqueue_setup(hsotg);
функцияdwc2_hsotg_enqueue_setup
называетсячас,Gadget Устройство получено. SETUP Пакет токенов, но еще не получил его DATA0 пакет токенов。dwc2_hsotg_enqueue_setup
изэффектда,настраивать、начать запрос, суть заключается в настройке request из complete функция (когда SETTUP Эта функция вызывается после завершения транзакции):
при передаче управления из "наладочной транзакции"Заканчивать,функцияdwc2_hsotg_complete_setup
называется。
Или MX6ULL изфункцияisr_setup_packet_handler
,возвращатьсяда STM32M157 изфункцияdwc2_hsotg_complete_setup
,этоих Вседасуществовать Gadget Он вызывается после того, как устройство получает «транзакцию SETUP». После получения «транзакции SETUP» вы можете узнать, что хочет сделать эта передача управления (req.bRequest что есть) и тогда с этим можно разобраться.
Как с этим справиться? Можно разделить на 3 слоя:
UDC водить программа машины: аналогична программе «Установить адрес» из управления передачей, в Первый этажиз UDC Это можно решить в драйвере.
USB_REQ_SET_ADDRESS
USB_REQ_SET_FEATURE // Есть некоторые запросы, о которых, возможно, потребуется сообщить для исправления. gadget driver
USB_REQ_CLEAR_FEATURE // Есть некоторые запросы, о которых, возможно, потребуется сообщить для исправления. gadget driver
USB_REQ_GET_STATUS // Есть некоторые запросы, о которых, возможно, потребуется сообщить для исправления. gadget driver
IMX6ULL: Linux-4.9.88\drivers\usb\chipidea\udc.c, функция isr_setup_packet_handler
STM32MP157: Linux-5.4\drivers\usb\dwc2\gadget.c, функция dwc2_hsotg_complete_setup
драйвер гаджета: включает дескриптор издействовать
USB_REQ_GET_DESCRIPTOR
USB_REQ_SET_CONFIGURATION
USB_REQ_GET_CONFIGURATION
USB_REQ_SET_INTERFACE
USB_REQ_GET_INTERFACE
USB_REQ_GET_STATUS // Первый этаж UDC водить машина не может справиться со словами, gadget driver справиться
USB_REQ_CLEAR_FEATURE // Первый этаж UDC водить машина не может справиться со словами, gadget driver справиться
USB_REQ_SET_FEATURE // Первый этаж UDC водить машина не может справиться со словами, gadget driver справиться
Файл: driver\usb\gadget\composite.c
функция:composite_setup
usb_configuration или usb_function из лечения: Это альтернатива из. Большинство передач управления оборудованиемиспользовать соответствуют стандартам USB запрос, но передача управления также может использоваться для реализации связанных запросов. Для этих нестандартных запросов требуется верхний уровень. машинусправиться。
В протоколе USB передачу всегда инициирует хост. В качестве драйвера гаджета это всегда выглядит так:
USB Объект передачи – это конечную точку, процесс использования выглядит следующим образом:
Функцияводить структура машиныиз usb_request,можно получить Host Отправлятьизданные,Также возможнокда К Host Отправить данные. Когда передача будет завершена, usb_request изфункция обратного вызов называется.
В функции обратного вызова usb_request можно отправить еще раз.
Как вызвать функцию обратного вызова? Источник UDC изсерединаперерывфункция。
Отношения вызова следующие:
// Linux-4.9.88\drivers\usb\chipidea\core.c
ci_irq
/* Handle device/host interrupt */
if (ci->role != CI_ROLE_END)
ret = ci_role(ci)->irq(ci); // udc_irq
udc_irq
if (USBi_UI & intr)
isr_tr_complete_handler(ci);
err = isr_tr_complete_low(hwep);
usb_gadget_giveback_request(&hweptemp->ep, &hwreq->req);
req->complete(ep, req);
Отношения вызова следующие:
// Linux-5.4\drivers\usb\dwc2\gadget.c
dwc2_hsotg_irq
// Обработка прерываний конечной точки
for (ep = 0; ep < hsotg->num_of_eps && daint_out;
ep++, daint_out >>= 1) {
if (daint_out & 1)
dwc2_hsotg_epint(hsotg, ep, 0);
dwc2_hsotg_handle_outdone(hsotg, idx);
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req);
req->complete(ep, req);
}
for (ep = 0; ep < hsotg->num_of_eps && daint_in;
ep++, daint_in >>= 1) {
if (daint_in & 1)
dwc2_hsotg_epint(hsotg, ep, 1);
dwc2_hsotg_complete_in(hsotg, hs_ep);
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req);
req->complete(ep, req);
}
loopback Это петля, Хост Отправить данные на Гаджет, то читай Gadget Толькокпридетсяприезжать Как естьизданные。
Host При выборе определенного интерфейса будет выбран интерфейс по умолчанию. 0 настройки (альтсеттинг);
Когда хост Отрегулировать USB_REQ_SET_INTERFACE к При необходимости укажите настройку по запросу.
Итак, начнем с f_loopback.c изфункцияloopback_set_alt
начинатьанализировать。
Вызывающие отношения:
loopback_set_alt
enable_loopback
result = enable_endpoint(cdev, loop, loop->in_ep);
result = enable_endpoint(cdev, loop, loop->out_ep);
result = alloc_requests(cdev, loop);
Как показано на рисунке выше, сначала отправьте сообщение out_req, он ждет Host Отправлятьданные。
Предположим, точка останова loop->out_ep из out_req получать Понятноданные,этоизфункция обратного вызоваloopback_complete
называется,Такие как Вниз:
фронт из f_loopback Также реализована передача в обе стороны: Хост приезжать Gadget、Gadget приезжать Хост, но между ними существуют отношения зависимости: Хост Данные необходимо сначала отправить, а затем прочитать.
f_sourcesink.c Также реализована передача в обе стороны: Хост приезжать Gadget、Gadget приезжать Хозяин, они независимы.
Host При выборе определенного интерфейса будет выбран интерфейс по умолчанию. 0 настройки (альтсеттинг);
Когда хост Отрегулировать USB_REQ_SET_INTERFACE к При необходимости укажите настройку по запросу.
То, что мы делаем, мы начинаем с f_sourcesink.c изфункцияsourcesink_set_alt
начинатьанализировать。
sourcesink_set_alt
enable_source_sink(cdev, ss, alt);
как"source",функцияsource_sink_start_ep
Построюданные、Отправьте usb_request:
когда Host читать Выбиратьприезжатьданныеназад,usb_request изфункция обратного вызывается вызов, и он просто снова отправляется USB попросить, дать Host Продолжайте подавать как в прошлый раз изданные:
еще из f_sourcesink.c изфункцияsourcesink_set_alt
начинатьанализировать。
sourcesink_set_alt
enable_source_sink(cdev, ss, alt);
как"sink",функцияsource_sink_start_ep
Сделаю это намеренно Пучокданныенастраиватьдля 0x55 (Это для отладки, вы можете увидеть приезжать, когда читатьприезжатые 0x55 перезаписано), отправить usb_request:
когда Host Отправлятьданные,usb_request изфункция обратного вызывается вызов, он проверяет получение выездных и отправляет снова usb_request: