Блок-схема драйвера USB Device Controller (UDC) показана на рисунке ниже и состоит из трёх частей. Первая часть — это основной уровень драйвера UDC, который реализован в файле driver/usb/gadget/udc/core.c. Этот уровень представляет собой уровень совместимости, который изолирует драйвер функции USB от конкретного драйвера USB-гаджета и абстрагирует его. Интерфейс и структура данных обеспечивают унифицированный и стабильный интерфейс для функционального драйвера USB и в то же время завершают согласование функционального драйвера USB и драйвера USB-гаджета. Вторая часть — это уровень драйвера гаджета, который отвечает за управление работой оборудования и связан с конкретным оборудованием контроллера USB-устройства. Драйвер гаджета dwc3 реализован в файле driver/usb/dwc3/gadget.c. Третья часть — это аппаратное обеспечение контроллера USB-устройства.
Драйвер USB-гаджета описывает аппаратный метод работы контроллера USB-устройства, и разные USB-контроллеры имеют разные реализации. Некоторые USB-контроллеры можно использовать только в качестве контроллеров устройств, например ompa, pxa2 и другие контроллеры USB-устройств, а их драйверы находятся в папке driver/usb/gadget/udc. Некоторые USB-контроллеры можно использовать в качестве хост-контроллеров или контроллеров устройств. Они имеют функции OTG и могут переключаться между двумя режимами, например USB-контроллер dwc3, драйвер которого находится в файле driver/usb/dwc3. Контроллер USB3.0 RK3399 использует USB-контроллер dwc3 и имеет функцию OTG.
Контроллер USB использует драйвер UDC после переключения в режим устройства, поэтому мы начнем с процесса инициализации контроллера USB-устройства и объясним ключевые структуры данных.
В дереве устройств установите атрибут dr_mode="otg". При инициализации контроллера dwc3 он перейдет в режим USB_DR_MODE_OTG. В то же время вызовите функции dwc3_host_init и dwc3_gadget_init для инициализации ресурсов, необходимых для хоста. режим и режим устройства Контроллер может впоследствии динамически переключаться между режимом хоста и режимом устройства. Процесс инициализации контроллера dwc3 USB3.0 показан на рисунке ниже, уделяя особое внимание процессу инициализации режима устройства. Основная работа заключается в следующем:
(1) Установите контроллер в режим USB_DR_MODE_OTG.
(2) Инициализируйте ресурсы, необходимые в режиме хоста. Конкретный процесс будет проанализирован при анализе драйвера хоста.
(3) Инициализируйте ресурсы, необходимые для режима устройства.
(a) Получите номер прерывания и выделите память, необходимую для передачи конечной точки 0. Конечная точка 0 используется во время перечисления устройств и должна отвечать на запросы от хоста, поэтому память необходимо выделить заранее.
(b) Установите для набора рабочих функций контроллера устройства dwc3 значение dwc3_gadget_ops, которое включает только аппаратное управление и не требует операций ввода-вывода.
(c) Инициализируйте аппаратную конечную точку. Сначала инициализируйте выходную конечную точку, затем входную конечную точку. Максимальная длина пакета для конечной точки 0 составляет 512 байт, а максимальная длина пакета для других конечных точек — 1024 байта. Операционная функция конечной точки 0 — dwc3_gadget_ep0_ops, а рабочая функция других конечных точек — dwc3_gadget_ep0_ops. Операционная функция конечной точки в основном описывает операции ввода-вывода. Неконечная точка 0 будет связана со связанным списком гаджета.ep_list. Конечная точка 0 поддерживает передачу управления, а другие конечные точки поддерживают изохронную, пакетную передачу и передачу с прерываниями.
(d) Добавьте драйвер udc. Сначала выделите структуру данных usb_udc, затем поместите ее в связанный список udc_list и, наконец, установите статус драйвера udc на USB_STATE_NOTATTACHED.
Структура данных после завершения инициализации показана на рисунке ниже. Самое важное — это контент, связанный с конечной точкой. Конечная точка 0 используется для передачи данных контроллера, например, для перечисления устройств и ответа на запросы настройки, отправленные хостом. Ресурсы необходимо выделить заранее. Конечная точка 0 существенно отличается от других конечных точек, поэтому ее рабочие функции уникальны. Другие конечные точки в основном используются для передачи данных, а функции работы являются общими.
В дереве устройств настройте USB-контроллер dwc3 в периферийный режим. При запуске системы USB-контроллер будет переведен в режим устройства, и будут инициализированы ресурсы, связанные с гаджетом. Если он настроен в режиме otg, только ресурсы, связанные с гаджетом. будет инициализирован. Контроллер dwc3 не будет переведен в режим устройства. В это время контроллер dwc3 находится в режиме otg и его необходимо переключить в режим устройства (только в режиме otg его можно переключить на хост или устройство).
// Переключиться в режим otg
echo 0 > /sys/devices/platform/usb0/dwc3_mode
echo otg > /sys/devices/platform/usb0/dwc3_mode
// Переключиться в режим хоста
echo 1 > /sys/devices/platform/usb0/dwc3_mode
echo host > /sys/devices/platform/usb0/dwc3_mode
// Переключиться в режим устройства
echo 2 > /sys/devices/platform/usb0/dwc3_mode
echo peripheral > /sys/devices/platform/usb0/dwc3_mode
Вышеупомянутое переключение режима завершается планированием рабочей очереди otg_work, и рабочая очередь вызывает функцию dwc3_rockchip_otg_extcon_evt_work для переключения.
[drivers\usb\dwc3\dwc3-rockchip.c]
struct dwc3_rockchip {
......
struct work_struct otg_work;
......
};
static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work)
{
if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_PERIPHERAL :
extcon_get_cable_state_(edev, EXTCON_USB)) {
......
spin_lock_irqsave(&dwc->lock, flags);
// режим устройства
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
spin_unlock_irqrestore(&dwc->lock, flags);
} else if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_HOST :
extcon_get_cable_state_(edev, EXTCON_USB_HOST)) {
......
spin_lock_irqsave(&dwc->lock, flags);
// режим хоста
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
spin_unlock_irqrestore(&dwc->lock, flags);
......
} else {
......
}
......
}
struct usb_ep — это общая структура данных, используемая ядром Linux для описания конечных точек контроллера USB-устройства. ops — это операционная функция, соответствующая конечной точке, в основном используемая для описания операций ввода-вывода. ep_list — это узел связанного списка конечной точки, который обычно связан со связанным списком ep_list usb_gadget. maxpacket описывает максимальную длину пакета конечной точки, которая настраивается дескриптором конечной точки (программным обеспечением). Например, в USB3.0 максимальная длина пакета для массовой передачи равна 512, а максимальная длина пакета для isoc — 1024. maxpacket_limit описывает максимальную длину пакета, которую может обработать оборудование конечной точки. Например, в USB3.0 конечная точка 0 может обрабатывать максимум 512 байт, а другие конечные точки могут обрабатывать максимум 1024 байта. USB3.0 поддерживает пакетную передачу нескольких пакетов данных в течение 125 микросекунд. Максимальное значение устанавливается параметром maxburst. Диапазон 0–15 означает передачу 1 пакета, а 15 означает, что может быть передано 16 пакетов. может быть только 0. . Каждая конечная точка имеет свой адрес, описываемый с помощью addr. desc указывает на дескриптор конечной точки. Если вы используете USB3.0, вам также необходимо установить дескриптор comp_desc.
struct usb_ep обычно не используется напрямую, а встраивается в большую структуру данных. В контроллере dwc3 он встроен в структуру struct dwc3_ep. pending_list и start_list используются для хранения структуры данных запроса ввода-вывода struct usb_request. Первый хранит ожидающие запросы ввода-вывода, которые еще не могут быть обработаны, а второй хранит запросы ввода-вывода, которые уже начали обрабатываться. trb_pool — это массив, состоящий из trb, который автоматически обрабатывается аппаратным обеспечением. Он хранит адрес, длину и флаг буфера передачи. Неконечная точка 0 выделяет 256 trb, а trb_pool_dma сохраняет физический адрес trb_pool. trb_enqueue и trb_dequeue — это используемые и неиспользуемые индексы массива trb_pool. selected_requests указывает количество выделенных запросов ввода-вывода.
[include/linux/usb/gadget.h]
struct usb_ep { // USBрежим устройства Конечная точка общаяструктура данных
const char *name; // имя
const struct usb_ep_ops *ops; // Операционная функция, соответствующая этой конечной точке
struct list_head ep_list; // Узел связанного списка конечной точки
struct usb_ep_caps caps; // Типы транспорта, поддерживаемые конечной точкой
bool claimed;
bool enabled; // Включена ли конечная точка
unsigned maxpacket:16; // Максимальная длина пакета, настраиваемая дескриптором конечной точки
unsigned maxpacket_limit:16; // Максимальная длина пакета, которую может обработать оборудование конечной точки.
unsigned max_streams:16; // Максимальное количество потоков, диапазон 0–16.
unsigned mult:2; // multiplier, 'mult' value for SS Isoc EPs
// Максимальный пакет, поддерживаемый конечной точкой, диапазон 0–15, USB3.0 поддерживает эту опцию.
unsigned maxburst:5;
u8 address; // Адрес конечной точки, используемый для различения разных конечных точек.
const struct usb_endpoint_descriptor *desc; // дескриптор конечной точки
const struct usb_ss_ep_comp_descriptor *comp_desc; // Дескриптор сопряжения USB3.0
};
[drivers/usb/dwc3/core.h]
struct dwc3_ep { // dwc3 USB-контроллеррежим устройстваконечная точкаструктура данных
struct usb_ep endpoint; // Общая структура конечной точки устройства данных
struct list_head pending_list; // ожидающий ввода-вывода requests
struct list_head started_list; // начал ИО requests
spinlock_t lock; // спин-блокировка
void __iomem *regs; // Базовый адрес регистра этой конечной точки
struct dwc3_trb *trb_pool; // Массив trb этой конечной точки, используемый для передачи данных DMA.
dma_addr_t trb_pool_dma; // физический адрес этой конечной точки в массиве trb
const struct usb_ss_ep_comp_descriptor *comp_desc; // Дескриптор сопряжения USB3.0
struct dwc3 *dwc; // направленный dwc3 ctrl
u32 saved_state;
unsigned flags; // Флаг этой конечной точки определяется макросом, начинающимся с DWC3_EP.
#define DWC3_EP_ENABLED (1 << 0)
#define DWC3_EP_STALL (1 << 1)
#define DWC3_EP_WEDGE (1 << 2)
#define DWC3_EP_BUSY (1 << 4)
#define DWC3_EP_PENDING_REQUEST (1 << 5)
#define DWC3_EP_MISSED_ISOC (1 << 6)
#define DWC3_EP0_DIR_IN (1 << 31)
u8 trb_enqueue; // индекс очереди массива trb
u8 trb_dequeue; // Индекс удаления из очереди массива trb
u8 number; // endpoint number (1 - 15)
// set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
u8 type;
u8 resource_index;
u32 allocated_requests; // Уже выделено IO requests
u32 queued_requests; // IO поставлен в очередь на передачу количество запросов
// the interval on which the ISOC transfer is started
u32 interval;
// a human readable name e.g. ep1out-bulk
char name[20];
unsigned direction:1; // true for TX, false for RX
unsigned stream_capable:1;
};
struct usb_ep_ops описывает функцию работы конечной точки, в основном связанную с операциями ввода-вывода. Эти функции тесно связаны с аппаратным обеспечением. Контроллер USB-устройства должен реализовывать эти функции, а реализации функций конечной точки 0 и неконечной точки 0 также несовместимы. Enable включает конечную точку, Disable отключает конечную точку, alloc_request выделяет структуру данных запроса ввода-вывода usb_request, free_request освобождает запрос ввода-вывода, очередь добавляет запрос ввода-вывода в очередь, dequeue удаляет запрос ввода-вывода из очереди и fifo_status получает статус fifo, fifo_flush обновляет fifo.
[include/linux/usb/gadget.h]
struct usb_ep_ops {
int (*enable) (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc);
int (*disable) (struct usb_ep *ep);
struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags);
void (*free_request) (struct usb_ep *ep, struct usb_request *req);
int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);
int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
int (*set_halt) (struct usb_ep *ep, int value);
int (*set_wedge) (struct usb_ep *ep);
int (*fifo_status) (struct usb_ep *ep);
void (*fifo_flush) (struct usb_ep *ep);
};
Запросы ввода-вывода USB описываются с помощью структуры usb_request. Драйвер functon инкапсулирует данные в форму usb_request, а затем отправляет их драйверу udc. Драйвер udc затем преобразует их в trb и, наконец, передает trb. Конечная точка USB-контроллера, и конечная точка будет работать автоматически. Эта структура представляет собой общую структуру данных. Базовый драйвер обычно не использует ее напрямую, а встраивает в другую структуру. buf хранит данные, которые необходимо передать, length хранит длину данных, dma хранит физический адрес buf, sg — это адрес агрегированной записи таблицы передачи DMA, num_sgs указывает количество скаттерлистов, Complete — обратный вызов функция после завершения передачи usb_request и не может перейти в спящий режим с помощью контроллера dwc3 Вызывается нижняя часть (поток прерывания), context — параметр полной функции обратного вызова, status указывает на результат передачи, 0 — на то, что передача завершена, отрицательное число — на то, что передача не удалась, а ошибка -ESHUTDOWN Код указывает, что причина сбоя передачи заключается в отключении устройства. При открытии соединения или закрытии конечной точки драйвером фактическое значение указывает количество переданных байт.
Структура dwc3_request — это структура данных, используемая драйвером устройства контроллера dwc3 для описания запросов ввода-вывода, а структура данных usb_request общего запроса ввода-вывода встроена внутрь.
[include/linux/usb/gadget.h]
struct usb_request { // Используется для описания Запроса. ввода-вывода
void *buf; // Буфер для отправки или получения данных
unsigned length; // Длина данных буфера
dma_addr_t dma; // Физический адрес buf
struct scatterlist *sg; // a scatterlist for SG-capable controllers
unsigned num_sgs; // number of SG entries
unsigned num_mapped_sgs; // number of SG entries mapped to DMA
// The stream id, when USB3.0 bulk streams are being used
unsigned stream_id:16;
/* If true, hints that no completion irq is needed.
Helpful sometimes with deep request queues that are handled
directly by DMA controllers. */
unsigned no_interrupt:1;
/* If true, when writing data, makes the last packet be "short"
by adding a zero length packet as needed; */
unsigned zero:1;
/* When reading data, makes short packets be
treated as errors (queue stops advancing till cleanup). */
unsigned short_not_ok:1;
/* Function called when request completes, so this request and
its buffer may be re-used. The function will always be called with
interrupts disabled, and it must not sleep.
Reads terminate with a short packet, or when the buffer fills,
whichever comes first. When writes terminate, some data bytes
will usually still be in flight (often in a hardware fifo).
Errors (for reads or writes) stop the queue from advancing
until the completion function returns, so that any transfers
invalidated by the error may first be dequeued. */
void (*complete)(struct usb_ep *ep, struct usb_request *req);
void *context; // Полные параметры функции обратного вызова
struct list_head list; // For use by the gadget driver.
/* Reports completion code, zero or a negative errno.
Normally, faults block the transfer queue from advancing until
the completion callback returns.
Code "-ESHUTDOWN" indicates completion caused by device disconnect,
or when the driver disabled the endpoint. */
int status;
/* Reports bytes transferred to/from the buffer. For reads (OUT
transfers) this may be less than the requested length. If the
short_not_ok flag is set, short reads are treated as errors
even when status otherwise indicates successful completion.
Note that for writes (IN transfers) some data bytes may still
reside in a device-side FIFO when the request is reported as
complete. */
unsigned actual;
};
[drivers/usb/dwc3/core.h]
struct dwc3_request { // Описать передачу ввода-вывода контроллера dwc3.
struct usb_request request; // Универсальный Запрос ввода-вывода
struct list_head list; // Список очереди запросов
struct dwc3_ep *dep; // Конечная точка, которой принадлежит этот запрос
u8 first_trb_index; // index to first trb used by this request
u8 epnum; // Номер конечной точки, соответствующий запросу
struct dwc3_trb *trb; // Адрес trb, которому он принадлежит
dma_addr_t trb_dma; // DMA-адрес trb, которому он принадлежит
unsigned direction:1; // IN or OUT direction flag
unsigned mapped:1; // true when request has been dma-mapped
unsigned started:1; // true when request has been queued to HW
};
TRB (блок запроса на передачу) Блок запроса на передачу — это аппаратный формат, который автоматически обрабатывается оборудованием конечной точки. bpl и bph — это младшие 32 бита и верхние 32 бита адреса DMA 64-битного буфера соответственно, размер — это длина буфера, на которую приходится 23 бита, а остальные — управляющие биты. Драйвер устройства контроллера dwc3 свяжет dwc3_request и dwc3_trb, установит каждый бит в TRB, а затем запишет адрес DMA TRB в контроллер. Наконец, включите передачу, и контроллер автоматически передаст TRB в конечную точку. данные в буфере, указанном TRB.
[drivers/usb/dwc3/core.h]
struct dwc3_trb {
u32 bpl; // Буфер нижних 32 адресов DW0-3
u32 bph; // Старший адрес буфера 32 DW4-7
u32 size; // Длина буфера[23:0] DW8-B
u32 ctrl; // управляющий бит DWC-F
} __packed;
Подробное битовое поле TRB показано на рисунке ниже, его общий размер составляет 16 байт. Настройки программного обеспечения синей зоны, настройки программного обеспечения зеленой зоны, обновления оборудования. Подробности смотрите в таблице ниже.
область | область | иллюстрировать | Как получить доступ к оборудованию |
---|---|---|---|
BPTRL | Buffer Pointer Low | Младшие 32 бита 64-битного буферного адреса DMA | R_W |
BPTRH | Buffer Pointer High | Старшие 32 бита 64-битного буферного адреса DMA | R_W |
BUFSIZ | Buffer Size | Размер буфера, диапазон 0-(16 МБ - 1 байт), аппаратное обеспечение уменьшит эту область после завершения передачи. | R_W |
PCM1 | Packet Count M1 | Входная конечная точка изохронной передачи USB2.0 поддерживает передачу нескольких пакетов данных в одном микрокадре. Эта область предназначена для установки количества пакетов данных, передаваемых в одном микрокадре. Это значение необходимо установить при подготовке первого trb. USB2.0 может передавать 3 пакета данных в течение 125 микросекунд. | R_W |
TRBSTS | TRB Status | Статус передачи, установленный аппаратно 0-Успех 1-MissedIsoc2-SetupPending4-TransferInProgress4-ZLP_PENDING | R_W |
HWO | Hardware Owner of Descriptor | Когда программное обеспечение готовит TRB, ему присваивается значение 1, что указывает на то, что TRB принадлежит контроллеру. Программное обеспечение не может изменить этот TRB до тех пор, пока бит не будет очищен аппаратным обеспечением. | R_W |
LST | Last TRB | Идентифицирует последний TRB. Обычно один TRB не может передавать все данные, например очень длинный дескриптор конфигурации. В этом случае TRB необходимо организовать в связанный список, а позиция CHN должна быть установлена в 1. Последний TRB. необходимо установить CHN на 0, LST установить на 1 | R |
CHN | Chain Buffers | Обычно один TRB не может передавать все данные. Для CHN необходимо установить значение 1, а TRB организованы в связанный список. Контроллер будет рассматривать эти TRB как транзакцию для передачи. Для последнего TRB необходимо установить значение 0 и LST. до 0. 1 | R |
TRBCTL | TRB Control | Укажите тип TRB1-Control-Data-2+/Bulk/Interrupt2-Control-Setup3-Control-Status-24-Control-Status-35-Control-Data6-Isochronous-First7-Isochronous8-Link TRB, Normal-ZLP ( Массовый вход) | R |
ISP/IMI | Interrupt on Short Packet / Interrupt on Missed ISOC | ISP-Когда выходная конечная точка получает короткий пакет и CSP=1 и LST=0, контроллер генерирует событие XferInProgress. Для конечной точки ISOC, если этот бит равен 1 и время передачи ISOC истекает, контроллер генерирует событие. Событие XferInProgress. (Передача ISOC ориентирована на производительность в реальном времени и чувствительна к требованиям времени. Когда сторона устройства ep_queues пакет данных, она должна нести номер микрокадра ожидаемых данных для отправки. Если срок действия номера микрокадра истек, то , текущий номер микрокадра больше заданного номера микрокадра, произойдет событие Missed Isoc и пакет будет отброшен) | R |
IOC | Interrupt on Complete | Когда IOC=1, контроллер сгенерирует XferInProgress? | R |