Как показано на рисунке ниже, USB-контроллер может иметь два разных состояния. Когда USB-контроллер выступает в роли хоста, он называется хост-контроллером USB и использует драйвер хост-контроллера USB. Когда USB-контроллер выступает в качестве устройства, он называется контроллером USB-устройства и использует драйвер UDC (контроллер USB-устройства). В этом разделе анализируется структура драйверов только в том случае, если контроллер USB используется в качестве устройства.
Когда USB-контроллер используется в качестве устройства, структуру драйверов можно разделить на 5 уровней. Верхний уровень — это драйвер функции гаджета, который представляет собой драйвер конкретного устройства, например драйвер устройства хранения данных большой емкости (U-диск, мобильный жесткий диск и т. д.), драйвер устройства связи (последовательный порт USB, виртуальная сеть USB). карта и т. д.), драйвер UAC (микрофон USB, аудиоустройства USB, например звуковые карты USB). Далее идет уровень API функции гаджета, который представляет собой уровень абстракции, который обеспечивает унифицированный API вверх и вниз, скрывая различия и улучшая совместимость драйверов. Композитный уровень — это дополнительный средний уровень, который может эффективно поддерживать многофункциональные устройства с помощью одной или нескольких конфигураций, упрощая разработку драйверов составных USB-устройств. В настоящее время наиболее популярным является использование конфигов USB-гаджетов, реализованных на базе Composite и configfs, позволяющих гибко настраивать USB-устройства в пользовательском пространстве. Драйвер UDC напрямую обращается к оборудованию и управляет связью между USB-устройством и USB-хостом. Контроллер USB-устройства подключается к хост-контроллеру USB через USB-кабель и отвечает за отправку и получение данных USB.
Все драйверы функции USB-гаджета ядра Linux находятся в каталоге driver/usb/gadget/function/, включая класс коммуникационного устройства. Class) драйвер (f_acm.c, f_ecm, f_serial.c и т. д.), драйвер класса аудиоустройства USB (f_uac1.c, f_uac2.c, u_audio.c), драйвер запоминающего устройства (f_mass_storage.c), видеоустройство USB класс Драйвер (f_uvc.c) и т.д.
Вход драйвера функции гаджета описывается структурой данных usb_function_driver, и драйвер должен реализовать функции alloc_inst и alloc_func. alloc_inst создает структуру данных usb_function_instance и инициализирует ее. alloc_func создает usb_function и инициализирует ее. Целью является установка внутри функции обратного вызова. Обычно структура данных usb_function не используется напрямую, а встроена в структуру данных драйвера. Композитный драйвер будет вызывать функции alloc_inst и alloc_func через API функции гаджета. usb_function описывает драйвер функции гаджета. Целью драйвера функции гаджета является реализация этих функций обратного вызова.
[include/linux/usb/composite.h]
struct usb_function_driver {
const char *name;
struct module *mod;
struct list_head list;
// Создайте usb_function_instance и инициализируйте его.
struct usb_function_instance *(*alloc_inst)(void);
// Создайте usb_function и инициализируйте ее.
struct usb_function *(*alloc_func)(struct usb_function_instance *inst);
};
struct usb_function { // описывает гаджет Функциональный драйвер
const char *name; // gadget Функциональный драйверимя struct usb_gadget_strings **strings; // Таблица строк, идентификаторов языка, предоставленных запросами на распределение привязки и управление.
struct usb_descriptor_header **fs_descriptors; // full дескриптор скорости
struct usb_descriptor_header **hs_descriptors; // high дескриптор скорости
struct usb_descriptor_header **ss_descriptors; // super дескриптор скорости
struct usb_configuration *config; // Конфигурация добавлена функцией usb_add_function
// Функция обратного вызова привязки драйвера выделяет ресурсы, необходимые драйверу, такие как конфигурация, конечные точки, буферы ввода-вывода и т. д.
int (*bind)(struct usb_configuration *, struct usb_function *);
// Освободить ресурсы, выделенные привязкой
void (*unbind)(struct usb_configuration *, struct usb_function *);
void (*free_func)(struct usb_function *f); // выпустить usb_function
// Установите дополнительные конфигурации. Иногда драйвер может иметь несколько конфигураций, и вам нужно использовать set_alt для переключения.
int (*set_alt)(struct usb_function *, unsigned interface, unsigned alt);
// Получите дополнительную конфигурацию текущих настроек. Если нескольких конфигураций нет, по умолчанию будет использоваться конфигурация 0 и будет возвращено 0.
int (*get_alt)(struct usb_function *, unsigned interface);
// disable gadget функциональный драйвер, используемый при перезагрузке хоста, перенастройке гаджетов и отключении хоста.
void (*disable)(struct usb_function *);
// Управляющие запросы для конкретных интерфейсов
int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);
// Проверьте, могут ли быть обработаны запросы определенных классов устройств.
bool (*req_match)(struct usb_function *, const struct usb_ctrlrequest *);
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
/* USB 3.0 additions */
// Запросить GetStatus для возврата текущего гаджета Функциональный Статус водителя
int (*get_status)(struct usb_function *);
// Когда получен SetFeature(FUNCTION_SUSPEND), выполните обратный вызов этой функции.
int (*func_suspend)(struct usb_function *, u8 suspend_opt);
/* private: internals */
struct list_head list;
DECLARE_BITMAP(endpoints, 32); // растровое изображение конечной точки
const struct usb_function_instance *fi;
unsigned int bind_deactivated:1;
};
usb_function_driver обычно определяется и инициализируется с помощью макроса DECLARE_USB_FUNCTION_INIT. После расширения макроса он определяет экземпляр структуры usb_function_driver, который в основном устанавливает члены alloc_inst и alloc_func. Префикс используется для создания usb_function_instance, который представляет экземпляр функции гаджета. Последний используется для создания и инициализации usb_function. Методы в usb_function реализуют определенные драйверы функции гаджета. Функции usb_function_register и usb_function_unregister завершают регистрацию и отмену регистрации структуры usb_function_driver.
[include/linux/usb/composite.h]
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static int __init _name ## mod_init(void) \
{ \
return usb_function_register(&_name ## usb_func); \ // Зарегистрируйте драйвер устройства UAC
} \
static void __exit _name ## mod_exit(void) \
{ \
usb_function_unregister(&_name ## usb_func); \ // Отмените регистрацию драйвера устройства UAC
} \
module_init(_name ## mod_init); \ // Инициализация модуля
module_exit(_name ## mod_exit) // Удаление модуля
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
// Гаджет, определяющий UAC2.0 Функциональный драйвер с именем uac2_usb_func
static struct usb_function_driver _name ## usb_func = { \
.name = __stringify(_name), \ // Имя драйвера — uac2.
.mod = THIS_MODULE, \
.alloc_inst = _inst_alloc, \
.alloc_func = _func_alloc, \
}; \
MODULE_ALIAS("usbfunc:"__stringify(_name));
API функции гаджета — это уровень абстракции. Верхний драйвер функции гаджета использует API функции гаджета для регистрации и отмены регистрации, а нижний составной драйвер использует API функции гаджета и привязку и сопоставление драйвера функции гаджета. Драйвер функции гаджета должен реализовать структуру данных usb_function_driver и зарегистрироваться на уровне API функции гаджета.
Ниже приведены основные API-интерфейсы API функции гаджета. usb_function_register помещает зарегистрированный usb_function_driver в связанный список func_list. Функция usb_function_instance обойдет связанный список func_list, сравнит имя параметра с именем usb_function_driver, если имена совпадают, совпадение будет успешным, затем вызовет функцию обратного вызова alloc_inst в успешно совпавшем usb_function_driver, чтобы получить usb_function_instance, а затем установит указатель usb_function_driver на usb_function_instance Наконец, возвращает указатель на usb_function_instance. Функция usb_get_function получает и инициализирует usb_function посредством функции обратного вызова alloc_func. Информацию о других API см. в исходном коде.
[drivers/usb/gadget/functions.c]
// В гаджет Function Гаджет регистрации уровня API Функциональный драйвер
int usb_function_register(struct usb_function_driver *newf)
// Выйти из гаджета Функциональный драйвер
void usb_function_unregister(struct usb_function_driver *fd)
// Из гаджета Function Уровень API получает usb_function_instance
struct usb_function_instance *usb_get_function_instance(const char *name)
// Обратный вызов free_func_inst уничтожает usb_function_instance
void usb_put_function_instance(struct usb_function_instance *fi)
// Из гаджета Function Уровень API получает usb_function_instance
struct usb_function *usb_get_function(struct usb_function_instance *fi)
// Обратный вызов free_func уничтожает usb_function
void usb_put_function(struct usb_function *f)
Основная структура данных USB Composite — usb_composite_driver. Композитный драйвер должен реализовывать дескриптор устройства и привязывать функции обратного вызова.
Составные (составные) устройства описываются с помощью структуры данных usb_composite_dev. Эта структура данных автоматически создается ядром перед вызовом функции привязки драйвера при регистрации составного драйвера и не требует создания драйвера.
гаджет указывает на usb_gadget в структуре dwc3. req выделяется заранее при регистрации составного драйвера и используется для ответа на запросы управления, отправленные хостом. config указывает на текущую используемую конфигурацию USB. desc — дескриптор текущего устройства, который устанавливается при регистрации составного драйвера. driver указывает на соответствующий usb_composite_driver. Структура usb_composite_driver содержит структуру данных usb_gadget_driver, которая используется для представления драйвера USB-устройства.
[include/linux/usb/composite.h]
struct usb_composite_driver {
const char *name; // Имя водителя
const struct usb_device_descriptor *dev; // Дескриптор устройства, должен быть определен
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed; // Максимальная скорость, поддерживаемая устройством
unsigned needs_serial:1;
// Используется для выделения ресурсов, общих для всего устройства, используйте usb_add_config для добавления конфигурации, необходимо реализовать
int (*bind)(struct usb_composite_dev *cdev);
int (*unbind)(struct usb_composite_dev *); // Уничтожить ресурсы
void (*disconnect)(struct usb_composite_dev *); // Дополнительное отключение драйвера method
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
// Слой составного драйвера предоставляет реализацию по умолчанию, а именно complex_driver_template.
struct usb_gadget_driver gadget_driver;
};
struct usb_composite_dev { // Композитное оборудование
// Только для чтения, абстракция контроллера USB-устройства, указывающая на usb_gadget в структуре dwc3.
struct usb_gadget *gadget;
struct usb_request *req; // Буфер, используемый для ответа на запросы управления, выделяется заранее.
struct usb_request *os_desc_req; // Буфер, используемый в ответ на дескрипторы ОС, выделяется заранее.
struct usb_configuration *config; // В настоящее время используется конфигурация
// qwSignature part of the OS string
u8 qw_sign[OS_STRING_QW_SIGN_LEN];
u8 b_vendor_code; // bMS_VendorCode part of the OS string
struct usb_configuration *os_desc_config; // Конфигурация, используемая дескрипторами ОС
unsigned int use_os_string:1;
unsigned int suspended:1;
struct usb_device_descriptor desc; // дескриптор устройства
struct list_head configs;
struct list_head gstrings;
struct usb_composite_driver *driver; // Укажите на составной драйвер
......
};
Большинство устаревших драйверов USB-гаджетов, которые напрямую используют составной уровень ядра Linux, находятся в каталоге driver/usb/gadget/legacy/, например, файл драйвера аудиоустройства USB audio.c, файл драйвера виртуального Ethernet-устройства USB ether. .c и драйвер устройства HID. Устаревший драйвер может напрямую использовать макрос Module_usb_composite_driver, предоставляемый ядром, чтобы легко определить составной драйвер. Параметром является структура usb_composite_driver. Используйте usb_composite_probe для регистрации составного драйвера. Используйте функцию usb_composite_unregister, чтобы отменить регистрацию драйвера Composite.
[include/linux/usb/composite.h]
#define module_usb_composite_driver(__usb_composite_driver) \
module_driver(__usb_composite_driver, usb_composite_probe, \
usb_composite_unregister)
[include/linux/device.h]
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \ // функция инициализации
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \ // функция выхода из системы
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
Функции usb_composite_probe и usb_composite_unregister определяются следующим образом. usb_composite_probe инициализирует составной драйвер устройства, usb_composite_unregister удаляет составной драйвер устройства.
[include/linux/usb/composite.h]
/**
* usb_composite_probe() - register a composite driver
* @driver: the driver to register
*
* Context: single threaded during gadget setup
*
* This function is used to register drivers using the composite driver
* framework. The return value is zero, or a negative errno value.
* Those values normally come from the driver's @bind method, which does
* all the work of setting up the driver to match the hardware.
*
* On successful return, the gadget is ready to respond to requests from
* the host, unless one of its components invokes usb_gadget_disconnect()
* while it was binding. That would usually be done in order to wait for
* some userspace participation.
*/
int usb_composite_probe(struct usb_composite_driver *driver)
/**
* usb_composite_unregister() - unregister a composite driver
* @driver: the driver to unregister
*
* This function is used to unregister drivers using the composite
* driver framework.
*/
void usb_composite_unregister(struct usb_composite_driver *driver)
Ядро реализует usb_gadget_driver, который представляет собой переменную complex_driver_template, на уровне составного драйвера. Все составные устройства используют эту структуру данных и не требуют реализации драйвера.
Когда составной драйвер регистрируется с помощью usb_composite_probe, ядро скопирует данные из Composite_driver_template в элемент гаджет_драйвер usb_composite_driver.
[drivers/usb/gadget/composite.c]
static const struct usb_gadget_driver composite_driver_template = { // Драйвер USB-устройства, реализованный ядром
.bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
.reset = composite_disconnect,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
Configfs — это файловая система на основе оперативной памяти, которая может напрямую управлять объектами ядра в пользовательском пространстве. Она в основном подходит для модулей с множеством конфигураций объектов ядра, таких как составные USB-устройства. В Linux версии 3.11 представлены настройки USB-гаджетов. На уровне пользователя любая функция и конфигурация устройства USB-гаджета может быть определена через открытый API, что значительно упрощает настройку и использование составных USB-устройств. Принципы и использование будут подробно представлены далее в этом разделе. USB Gadget Configfs реализован в файле driver/usb/gadget/configfs.c.
Модуль драйвера UDC определяется следующим образом. Он инициализируется во время инициализации ядра или загрузки модуля, создает udc_class и устанавливает функцию обратного вызова uevent в значение usb_udc_uevent.
[drivers/usb/gadget/udc/core.c]
static struct class *udc_class;
static int __init usb_udc_init(void)
{
udc_class = class_create(THIS_MODULE, "udc");
......
udc_class->dev_uevent = usb_udc_uevent;
return 0;
}
subsys_initcall(usb_udc_init);
static void __exit usb_udc_exit(void)
{
class_destroy(udc_class);
}
module_exit(usb_udc_exit);
Используйте usb_add_gadget_udc для регистрации драйвера UDC. Сначала выделите структуру данных usb_udc, инициализируйте соответствующие элементы и, наконец, поместите usb_udc в связанный список udc_list. После успешной регистрации статус UDC будет USB_STATE_NOTATTACHED. Используйте usb_del_gadget_udc для удаления драйвера UDC. Сначала выполните обратный вызов для отключения, затем выполните обратный вызов udc_stop для остановки контроллера USB-устройства и, наконец, удалите usb_udc из связанного списка udc_list.
[drivers/usb/gadget/udc/core.c]
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
* driver's device.
* @gadget: the gadget to be added to the list
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
/**
* usb_del_gadget_udc - deletes @udc from udc_list
* @gadget: the gadget to be removed.
*
* This, will call usb_gadget_unregister_driver() if
* the @udc is still busy.
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
Композитный драйвер вызывает usb_gadget_probe_driver для сопоставления с драйвером UDC. Он сначала проходит по связанному списку udc_list. Если член драйвера usb_udc пуст, это означает, что совпадение выполнено успешно. Затем составной драйвер привязывается к драйверу UDC, устанавливая адрес. из usb_composite_driver.gadget_driver составного драйвера с usb_udc Привязка завершается в члене .driver, и, наконец, udc_start вызывается обратно для запуска контроллера USB-устройства. Вызовите usb_gadget_unregister_driver, чтобы отменить привязку составного драйвера и драйвера UDC.
[drivers/usb/gadget/udc/core.c]
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
Уровень UDC также предоставляет некоторые интерфейсы функциональному драйверу USB-устройства для включения и выключения контроллеров USB-устройств, включения и отключения конечных точек, постановки в очередь/удаления из очереди запросов ввода-вывода, выделения и освобождения usb_requests, сопоставления конечных точек и т. д. Эти функции будут внутренне вызывать драйвер UDC конкретного контроллера USB-устройства. На платформе RK3399 будет вызываться драйвер UDC, реализованный dwc3. Что касается конкретного содержания, оно будет подробно объяснено при анализе драйвера UDC dwc3 в следующих главах.
[drivers/usb/gadget/udc/core.c]
int usb_ep_enable(struct usb_ep *ep) // включить конечную точку
int usb_ep_disable(struct usb_ep *ep) // запретить конечную точку
int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) // queues usb_request
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) // dequeue usb_request
struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) // выделить usb_request
void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req) // Отпустить usb_request
// Соответствует конечной точке для использования на основе дескриптора
int usb_gadget_ep_match_desc(struct usb_gadget *gadget, struct usb_ep *ep,
struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *ep_comp)
Драйвер UDC описывается с использованием структуры данных usb_udc, и все зарегистрированные структуры данных usb_udc будут связаны со связанным списком udc_list. Функции драйвера UDC в основном реализуются гаджетом-членом, то есть структурой данных usb_gadget. struct usb_gadget_driver реализуется составным слоем и используется для подключения функционального драйвера USB и драйвера UDC. usb_gadget_ops — это функция аппаратной работы контроллера USB-устройства, включая такие функции, как запуск контроллера USB-устройства, остановка контроллера USB-устройства и подача питания vbus. ep0 представляет конечную точку 0, которая будет выделена заранее при регистрации драйвера и будет использоваться для ответа на запросы управления. Помимо конечной точки 0, драйвер устройства USB также будет использовать другие конечные точки, и эти структуры данных конечной точки связаны со связанным списком ep_list. скорость представляет текущую скорость контроллера USB-устройства. max_speed указывает максимальную скорость контроллера USB-устройства.
[drivers/usb/gadget/udc/core.c]
static LIST_HEAD(udc_list);
struct usb_udc { // Описать контроллер USB-устройства
// Укажите на составной драйвервusb_gadget_driver
struct usb_gadget_driver *driver;
// Структура, реализующая драйвер UDC, включая функцию работы аппаратного обеспечения контроллера USB-устройства.
struct usb_gadget *gadget;
struct device dev;
// Структура usb_udc может формировать связанный список.
struct list_head list;
bool vbus; // Это значение всегда верно для UDC, которым не важен статус vbus.
};
[include/linux/usb/gadget.h]
struct usb_gadget {
// Рабочая очередь для sysfs_notify
struct work_struct work;
struct usb_udc *udc; // Наведите указатель на usb_udc
// Функция аппаратного управления контроллером USB-устройства, не включает операцию ввода-вывода
const struct usb_gadget_ops *ops;
struct usb_ep *ep0; // Конечная точка 0, используемая для ответа на запросы управления чтением и записью.
struct list_head ep_list; // Связанный список всех конечных точек, необходимых драйверу USB-устройства.
enum usb_device_speed speed; // Текущая скорость соединения с USB-хостом
enum usb_device_speed max_speed; // Максимальная скорость, поддерживаемая драйвером udc
enum usb_device_state state; // текущий статус
const char *name; // udcИмя водителя,Используйте и подтвердите тип оборудования контроллера
struct device dev;
unsigned out_epnum; // Последний использованный номер выходной конечной точки
unsigned in_epnum; // Последний использованный входной номер конечной точки
unsigned mA; // Недавно установленное значение м А
struct usb_otg_caps *otg_caps; // Возможности OTG
unsigned sg_supported:1; // Поддерживать ли совокупный DMA
unsigned is_otg:1; // Поддерживать ли OTG, поддержка OTG должна предоставить дескриптор OTG
unsigned is_a_peripheral:1; // Обычно неверно, если OTG не поддерживается.
// Размер буфера запроса выходной конечной точки выравнивается по MaxPacketSize.
unsigned quirk_ep_out_aligned_size:1;
unsigned is_selfpowered:1; // Это автономное питание?
unsigned connected:1; // Соединение успешное?
unsigned uvc_enabled:1; // Включена ли функция uvc?
......
};
struct usb_gadget_driver {
char *function; // Строка, описывающая usb_gadget_driver
enum usb_device_speed max_speed; // Максимальная скорость, с которой может справиться этот водитель
// Функция обратного вызова, с помощью которой можно привязать гаджет верхнего уровня функциональный драйвер
int (*bind)(struct usb_gadget *gadget, struct usb_gadget_driver *driver);
void (*unbind)(struct usb_gadget *);
// Вызов запроса управления конечной точкой 0, используемый для управления дескриптором и конфигурацией, обычно вызываемый в прерывании, не может перейти в режим сна.
int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *);
// Когда хост отключен, он вызывается после остановки всех передач. Он может быть вызван в режиме прерывания и не может перейти в режим ожидания.
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
// Вызывается при сбросе шины USB, должен быть реализован, вызывается в прерывании
void (*reset)(struct usb_gadget *);
struct device_driver driver;
};
struct usb_gadget_ops { // Функция аппаратной работы контроллера USB-устройства, не затрагивающая конечные точки и io
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
// Потяните вниз, чтобы USB-хост почувствовал, что USB-устройство подключено к USB-шине, и USB-хост перечислит USB-устройство.
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *, unsigned code, unsigned long param);
void (*get_config_params)(struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *); // начать УДК
int (*udc_stop)(struct usb_gadget *); // остановить УДК
// Сопоставить конечную точку USB
struct usb_ep *(*match_ep)(struct usb_gadget *,
struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *);
};