До того, как в ядро были добавлены конфигурации USB-гаджетов, ядро использовало жесткое кодирование для реализации составных устройств. Было невозможно динамически изменять и связывать различные функциональные драйверы в пользовательском пространстве. Если вы хотите изменить его, вам необходимо изменить код ядра. и перекодировать его, что очень неудобно. В настоящее время эта часть кода находится в каталоге driver/usb/gadget/legacy/. При компиляции в модуль ядра имя начинается с буквы g, например, аудиоустройство g_audio.ko, устройство последовательного порта g_serial.ko, устройство CDC и запоминающее устройство g_multi.ko. По сравнению с предыдущими версиями, конфигурации USB-устройств имеют другую форму реализации составного устройства. Функция устройства в конечном итоге должна быть реализована через функциональный драйвер. Ниже в качестве примера используется аудиокомпозитное оборудование для анализа рабочего процесса драйвера g_audio.
Как видно из предыдущего анализа, составной драйвер устройства вращается вокруг двух структур данных usb_composite_driver и usb_composite_dev, и устаревший составной драйвер устройства не является исключением.
Реализация составного аудиоустройства находится в файле driver/usb/gadget/legacy/audio.c, а его структура данных usb_composite_driver определяется следующим образом. Наиболее важными являются две функции обратного вызова audio_bind и audio_unbind, которые вызываются, когда драйвер g_audio привязывается и отсоединяется. Драйвер g_audio регистрируется в ядре с помощью макроса Module_usb_composite_driver, функция инициализации — usb_composite_probe, функция удаления — usb_composite_unregister.
[drivers/usb/gadget/legacy/audio.c]
static struct usb_composite_driver audio_driver = {
.name = "g_audio", // Имя водителя
.dev = &device_desc, // Дескриптор USB-устройства, составное USB-устройство имеет только один дескриптор устройства.
.strings = audio_strings, // нить
.max_speed = USB_SPEED_HIGH, // Максимальная скорость USB-устройства, USB2.0
.bind = audio_bind, // audio функция обратного вызова привязки составного драйвера
.unbind = audio_unbind, // audio функция обратного вызова для отмены привязки составного драйвера
};
module_usb_composite_driver(audio_driver); // Зарегистрировать audio_driver
[include/linux/usb/composite.h]
#define module_usb_composite_driver(__usb_composite_driver) \
module_driver(__usb_composite_driver, usb_composite_probe, \
usb_composite_unregister)
Макрос модуль_драйвер определяется следующим образом. В конечном итоге он инициализируется с помощью модуля_init и удаляется с помощью макроса модуля_exit.
[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);
Драйвер g_audio определяет параметры звука как параметры модуля, как показано ниже. При загрузке модуля вы можете указать параметры для изменения значений этих параметров по умолчанию.
[drivers/usb/gadget/legacy/audio.c]
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC2_DEF_PCHMASK;
module_param(p_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
static int p_srate = UAC2_DEF_PSRATE;
module_param(p_srate, uint, S_IRUGO);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
module_param(p_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC2_DEF_CCHMASK;
module_param(c_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
static int c_srate = UAC2_DEF_CSRATE;
module_param(c_srate, uint, S_IRUGO);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
module_param(c_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
После успешной загрузки модуля драйвера эти параметры будут экспортированы в пространство пользователя в каталоге /sys/module/g_audio/parameters.
Драйвер g_audio вызывает функцию usb_composite_probe для инициализации. Процесс выполнения показан на рисунке ниже. Он аналогичен процессу инициализации составного драйвера устройства, определенному в configfs USB-гаджета, за исключением того, что параметр usb_gadget_driver отличается. Usb_gadget_driver, определенный в конфигурации USB-гаджета, — это configfs_driver_template, а usb_gadget_driver, определенный в устаревшем режиме, — Composite_driver_template. Composite_driver_template является мостом между функциональным драйвером и драйвером UDC и будет вызван драйвером UDC в соответствующее время.
[drivers/usb/gadget/composite.c]
static const struct usb_gadget_driver composite_driver_template = {
.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,
},
};
Рабочий процесс usb_composite_probe выглядит следующим образом:
Как видно из вышесказанного, составные устройства, определенные в устаревшем режиме, очень негибкие, и пользователи не могут динамически настраивать составные устройства и привязанные функциональные драйверы в пользовательском пространстве. Если вы хотите использовать аудиоустройство, вы можете создать составное устройство только с помощью драйвера g_audio. Если вы используете виртуальную сетевую карту USB, вы можете создать составное устройство только с помощью драйвера g_ether. Если вам нужно USB-устройство с несколькими устройствами. функции, вам необходимо восстановить составное устройство и определение кодировки usb_composite_driver. Конфигурациям USB-гаджетов не требуется предварительно определять составные устройства в ядре. Пользователи настраивают их в пользовательском пространстве, а ядро автоматически генерирует необходимые составные устройства и привязывает их к соответствующим функциональным драйверам.