configfs — это файловая система на базе оперативной памяти, функции которой отличаются от sysfs. Sysfs — это представление объектов ядра, основанное на файловой системе. Хотя некоторые атрибуты позволяют пользователям читать и писать, объекты создаются, регистрируются и уничтожаются в ядре, и ядро контролирует их жизненный цикл. А configfs — это менеджер объектов ядра (или config_items), основанный на файловой системе. config_items явно создается в пространстве пользователя через mkdir и уничтожается с помощью rmdir. Соответствующие атрибуты появятся после mkdir. Эти атрибуты можно изменить в пространстве пользователя. написания, в отличие от sysfs, жизненный цикл этих объектов полностью контролируется пользовательским пространством, а ядру нужно только реагировать на операции в пользовательском пространстве. И configfs, и sysfs могут сосуществовать, но не могут заменять друг друга.
Ранний USB поддерживал только одно гаджет-устройство, и сценарий использования был относительно простым. Позже была добавлена составная структура для поддержки гаджет-устройств с несколькими функциями. Привязка нескольких функций выполняется в ядре. необходимо изменить, он негибкий и неудобный. В Linux версии 3.11 представлены конфигурации USB-гаджетов, основанные на файлах configfs. USB-гаджет configfs повторно реализует уровень составного устройства. Пользователи могут настраивать и комбинировать функции ядра в пользовательском пространстве для гибкого создания составных USB-устройств, что значительно повышает эффективность работы.
Следующий скрипт настраивает uac2.0 через configfs USB-гаджета. Перед использованием ядру необходимо включить конфигурации, связанные с UAC2.0. Параметры uac2.0 находятся в файле /sys/kernel/config/usb_gadget/g1/configs/c.1/uac2.0. Можно настроить канал, формат, частоту дискретизации и т. д. Следующий скрипт использует файл. конфигурация по умолчанию.
#!/bin/bash
function start_uac2()
{
# usb_gadget зависит от модуля libcomposite
modprobe libcomposite
# Смонтируйте файловую систему конфигурации
mount -t configfs none /sys/kernel/config
# Создайте каталог g1 и создайте экземпляр нового шаблона гаджета.
echo "mkdir /sys/kernel/config/usb_gadget/g1"
mkdir -m 0770 /sys/kernel/config/usb_gadget/g1
# Установите VendorID, ProductID и номер версии спецификации USB продукта.
echo "Setting Vendor Product ID's and bcdDevice"
echo 0x2207 > /sys/kernel/config/usb_gadget/g1/idVendor
echo 0x0019 > /sys/kernel/config/usb_gadget/g1/idProduct
# Номер версии устройства
echo 0x0200 > /sys/kernel/config/usb_gadget/g1/bcdDevice
# USB 1.1: 0x0110
# USB 2.0: 0x0200, USB 2.1: 0x0210, USB 2.5: 0x0250
# USB 3.0: 0x0300, USB 3.1: 0x0310, USB 3.2: 0x0320
# echo 0x0210 > /sys/kernel/config/usb_gadget/g1/bcdUSB
# Создать экземпляр строк идентификатора разработчика, продукта и серийного номера на английском языке, записанных в ядро.
echo "Setting English strings"
mkdir -m 0770 /sys/kernel/config/usb_gadget/g1/strings/0x409
echo "0123456789ABCDEF" > /sys/kernel/config/usb_gadget/g1/strings/0x409/serialnumber
echo "rockchip" > /sys/kernel/config/usb_gadget/g1/strings/0x409/manufacturer
echo "USB Audio Device" > /sys/kernel/config/usb_gadget/g1/strings/0x409/product
# Creating Config
echo "Creating Config"
mkdir -m 0770 /sys/kernel/config/usb_gadget/g1/configs/c.1
mkdir -m 0770 /sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409
echo "uac2" > /sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409/configuration
echo 500 > /sys/kernel/config/usb_gadget/g1/configs/c.1/MaxPower
# bind functions
# uac2 need to put before uvc, otherwise uvc + uac2 enumerate failed in win10
echo "Creating UAC2 gadget functionality : uac2.0"
mkdir /sys/kernel/config/usb_gadget/g1/functions/uac2.0
ln -s /sys/kernel/config/usb_gadget/g1/functions/uac2.0 /sys/kernel/config/usb_gadget/g1/configs/c.1
# Binding USB Device Controller
echo "Binding USB Device Controller"
echo fe800000.dwc3 > /sys/kernel/config/usb_gadget/g1/UDC
}
function stop_uac2()
{
# Disabling the gadget
echo "Disabling the USB gadget"
echo "" > /sys/kernel/config/usb_gadget/g1/UDC
# Remove functions from configurations
rm /sys/kernel/config/usb_gadget/g1/configs/c.1/uac2.0
# Remove strings directories in configurations
rmdir /sys/kernel/config/usb_gadget/g1/configs/c.1/strings/0x409
# remove the configurations
rmdir /sys/kernel/config/usb_gadget/g1/configs/c.1
# Remove functions (function modules are not unloaded, though)
rmdir /sys/kernel/config/usb_gadget/g1/functions/uac2.0
# Remove strings directories in the gadget
rmdir /sys/kernel/config/usb_gadget/g1/strings/0x409
# finally remove the gadget
rmdir /sys/kernel/config/usb_gadget/g1
}
usage()
{
echo "Usage: ./usb-gadget-uac2.sh start|stop"
echo " options:"
echo " start start uac2.0 gadget"
echo " stop stop uac2.0 gadget"
}
case $1 in
start)
start_uac2
;;
stop)
stop_uac2
;;
*)
usage
exit 1
;;
esac
exit 0
После выполнения приведенного выше сценария состав файлов в каталоге /sys/kernel/config/usb_gadget/g1/ выглядит следующим образом. Файлы, начинающиеся с b, используются для настройки дескрипторов устройств. Файлы в каталоге функций используются для настройки параметров функционального драйвера.
├── bcdDevice
├── bcdUSB
├── bDeviceClass
├── bDeviceProtocol
├── bDeviceSubClass
├── bMaxPacketSize0
├── configs
│ └── c.1
│ ├── bmAttributes
│ ├── MaxPower
│ ├── strings
│ │ └── 0x409
│ │ └── configuration
│ └── uac2.0 -> ../../../../usb_gadget/g1/functions/uac2.0
├── functions
│ └── uac2.0
│ ├── c_chmask # Маска канала записи, по умолчанию 0x3.
│ ├── c_srate # Частота дискретизации записи, по умолчанию 64000.
│ ├── c_ssize # Количество бит, занимаемое одним кадром записи данных, по умолчанию 16 бит.
│ ├── p_chmask # Маска канала воспроизведения, по умолчанию 0x3
│ ├── p_srate # Частота дискретизации воспроизведения, по умолчанию 48000.
│ ├── p_ssize # Биты, занимаемые воспроизведением одного кадра данных, по умолчанию — 16 бит.
│ └── req_number # Выделить количество запросов USB, по умолчанию 2
├── idProduct
├── idVendor
├── os_desc
│ ├── b_vendor_code
│ ├── qw_sign
│ └── use
├── strings
│ └── 0x409
│ ├── manufacturer
│ ├── product
│ └── serialnumber
└── UDC # Используется для установки имени привязанного USB-контроллера.
Функция инициализации модуля конфигурации USB-гаджета — гаджет_cfs_init. После вызова этой функции подсистема будет зарегистрирована в configfs и описана с помощью структуры configfs_subsystem. Подсистемы можно разделить на группы, которые описываются с помощью config_group. В группе есть члены, которые описываются с помощью config_item. USB-гаджет configfs является членом подсистемы configfs. Имя этого элемента — «usb_gadget». Тип элемента описывается config_item_type. Тип элемента содержит функцию инициализации gadgets_ops. Поэтому подсистема configfs USB-гаджета окончательно инициализируется вызовом ignores_make. При загрузке модуля libcomposite.ko в каталоге /sys/kernel/config/ будет создан каталог usb_gadget.
[drivers/usb/gadget/configfs.c]
static struct configfs_group_operations gadgets_ops = {
.make_group = &gadgets_make,
.drop_item = &gadgets_drop,
};
static struct config_item_type gadgets_type = {
.ct_group_ops = &gadgets_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem gadget_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "usb_gadget",
.ci_type = &gadgets_type,
},
},
.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
};
static int __init gadget_cfs_init(void)
{
int ret;
config_group_init(&gadget_subsys.su_group);
ret = configfs_register_subsystem(&gadget_subsys);
#ifdef CONFIG_USB_CONFIGFS_UEVENT
android_class = class_create(THIS_MODULE, "android_usb");
if (IS_ERR(android_class))
return PTR_ERR(android_class);
#endif
return ret;
}
module_init(gadget_cfs_init);
Основная работа функции ignore_make заключается в установке структуры данных составного устройства usb_composite_dev и структуры данных составного драйвера устройства usb_composite_driver. Рабочий процесс выглядит следующим образом:
[drivers/usb/gadget/configfs.c]
static struct configfs_group_operations gadgets_ops = {
.make_group = &gadgets_make,
.drop_item = &gadgets_drop,
};
static struct config_item_type gadgets_type = {
.ct_group_ops = &gadgets_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem gadget_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "usb_gadget",
.ci_type = &gadgets_type,
},
},
.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
};
static int __init gadget_cfs_init(void)
{
int ret;
config_group_init(&gadget_subsys.su_group);
ret = configfs_register_subsystem(&gadget_subsys);
#ifdef CONFIG_USB_CONFIGFS_UEVENT
android_class = class_create(THIS_MODULE, "android_usb");
if (IS_ERR(android_class))
return PTR_ERR(android_class);
#endif
return ret;
}
module_init(gadget_cfs_init);
Основная работа функции ignore_make заключается в установке структуры данных составного устройства usb_composite_dev и структуры данных составного драйвера устройства usb_composite_driver. Рабочий процесс выглядит следующим образом:
[drivers/usb/gadget/configfs.c]
static struct config_group *gadgets_make(
struct config_group *group, const char *name)
{
......
/* выделить структуру Структура гаджета_info */
gi = kzalloc(sizeof(*gi), GFP_KERNEL);
......
/* настраиватьgroup,gadgets_makeв конце концов вернутьсяиздаgi->group */
gi->group.default_groups = gi->default_groups;
gi->group.default_groups[0] = &gi->functions_group;
gi->group.default_groups[1] = &gi->configs_group;
gi->group.default_groups[2] = &gi->strings_group;
gi->group.default_groups[3] = &gi->os_desc_group;
/* Установите groups_group для настройки параметров функционального драйвера. */
config_group_init_type_name(&gi->functions_group, "functions",
&functions_type);
/* Установите configs_group для настройки параметров USB-устройства. */
config_group_init_type_name(&gi->configs_group, "configs",
&config_desc_type);
/* Установите strings_group, настраиваемые параметры строки. */
config_group_init_type_name(&gi->strings_group, "strings",
&gadget_strings_strings_type);
/* Установите os_desc_group для настройки дескриптора операционной системы. */
config_group_init_type_name(&gi->os_desc_group, "os_desc",
&os_desc_type);
/* Инициализировать составной драйвер устройства-usb_composite_driver */
gi->composite.bind = configfs_do_nothing; // Реализован как пустой
gi->composite.unbind = configfs_do_nothing; // Реализован как пустой
gi->composite.suspend = NULL;
gi->composite.resume = NULL;
gi->composite.max_speed = USB_SPEED_SUPER; // Поддержка USB3.0
/* Инициализировать составное устройство-usb_composite_dev */
composite_init_dev(&gi->cdev);
/* Установить составной дескриптор устройства */
gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;
gi->cdev.desc.bcdDevice = cpu_to_le16(get_default_bcdDevice());
/* Установите usb_gadget_driver в configfs */
gi->composite.gadget_driver = configfs_driver_template;
gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
gi->composite.name = gi->composite.gadget_driver.function;
/* Установить config_group */
config_group_init_type_name(&gi->group, name,
&gadget_root_type);
/* КconfigfsСистема возвращает&gi->group */
return &gi->group;
}
/* usb gadget usb_gadget_driver определяется configfs */
static const struct usb_gadget_driver configfs_driver_template = {
.bind = configfs_composite_bind,
.unbind = configfs_composite_unbind,
#ifdef CONFIG_USB_CONFIGFS_UEVENT
.setup = android_setup,
.reset = android_disconnect,
.disconnect = android_disconnect,
#else
.setup = composite_setup,
.reset = composite_disconnect,
.disconnect = composite_disconnect,
#endif
.suspend = composite_suspend,
.resume = composite_resume,
.max_speed = USB_SPEED_SUPER,
.driver = {
.owner = THIS_MODULE,
.name = "configfs-gadget",
},
};
Ниже приведены несколько операций config_item_type и configfs_group_operations, определенных конфигурациями USB-гаджетов. Когда новый экземпляр гаджета (g1) создается в каталоге /sys/kernel/config/usb_gadget/, сначала вызывается ignore_root_type для создания bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0, idVendor, idProduct, bcdDevice, bcdUSB, файла свойств UDC, пользователей. можно настроить его в пользовательском пространстве ; Затем вызовите function_type для создания каталога функций в каталоге g1. После привязки функционального драйвера файл свойств функционального драйвера будет экспортирован в этот каталог для изменения пользователем, затем вызовите config_desc_type для создания каталога конфигураций в каталоге g1; затем вызовите гаджет_strings_strings_type, создайте каталог строк в каталоге g1, который содержит такую информацию, как английский идентификатор, представленный строками, разработчиком, продуктом и серийным номером. Наконец, вызовите os_desc_type, чтобы создать каталог os_desc в каталоге g1, который содержит информацию об операционной системе и обычно не требует установки.
[drivers/usb/gadget/configfs.c]
static struct configfs_attribute *gadget_root_attrs[] = {
&gadget_dev_desc_attr_bDeviceClass,
&gadget_dev_desc_attr_bDeviceSubClass,
&gadget_dev_desc_attr_bDeviceProtocol,
&gadget_dev_desc_attr_bMaxPacketSize0,
&gadget_dev_desc_attr_idVendor,
&gadget_dev_desc_attr_idProduct,
&gadget_dev_desc_attr_bcdDevice,
&gadget_dev_desc_attr_bcdUSB,
&gadget_dev_desc_attr_UDC,
NULL,
};
static struct config_item_type gadget_root_type = {
.ct_item_ops = &gadget_root_item_ops,
.ct_attrs = gadget_root_attrs,
.ct_owner = THIS_MODULE,
};
static struct configfs_group_operations functions_ops = {
.make_group = &function_make,
.drop_item = &function_drop,
};
static struct config_item_type functions_type = {
.ct_group_ops = &functions_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_group_operations config_desc_ops = {
.make_group = &config_desc_make,
.drop_item = &config_desc_drop,
};
static struct config_item_type config_desc_type = {
.ct_group_ops = &config_desc_ops,
.ct_owner = THIS_MODULE,
};
/* Макрос, определяющий gadget_strings_strings_type */
USB_CONFIG_STRINGS_LANG(gadget_strings, gadget_info);
[include/linux/usb/gadget_configfs.h]
#define USB_CONFIG_STRINGS_LANG(struct_in, struct_member) \
...... \
static struct configfs_group_operations struct_in##_strings_ops = { \
.make_group = &struct_in##_strings_make, \
.drop_item = &struct_in##_strings_drop, \
}; \
\
static struct config_item_type struct_in##_strings_type = { \
.ct_group_ops = &struct_in##_strings_ops, \
.ct_owner = THIS_MODULE, \
}
static struct configfs_item_operations os_desc_ops = {
.release = os_desc_attr_release,
.allow_link = os_desc_link,
.drop_link = os_desc_unlink,
};
static struct config_item_type os_desc_type = {
.ct_item_ops = &os_desc_ops,
.ct_attrs = os_desc_attrs,
.ct_owner = THIS_MODULE,
};
Следующее объединяет сценарий настройки пользовательского пространства uac2.0 и код configfs USB-гаджета в ядре для анализа того, что происходит в ядре при настройке пользовательского пространства. Сосредоточьтесь на процессах выполнения, связанных с драйверами.
Когда команда mkdir -m 0770 /sys/kernel/config/usb_gadget/g1/configs/c.1 выполняется в пространстве пользователя, будет вызвана функция config_desc_make из configfs. Основной рабочий процесс:
Как упоминалось ранее, функциональный драйвер имеет две важные структуры данных: usb_function_instance и usb_function. Ниже приведен процесс получения usb_function_instance. Как показано на рисунке ниже, когда в пользовательском пространстве выполняется mkdir /sys/kernel/config/usb_gadget/g1/functions/uac2.0, ядро вызывает функцию function_make из конфигурации USB-гаджета. Основная задача функции function_make — это. для получения данных usb_function_instance функционального драйвера. Структура и процесс выполнения следующие:
Ниже приведен процесс получения usb_function. Как показано на рисунке ниже, когда в пользовательском пространстве выполняется ln -s /sys/kernel/config/usb_gadget/g1/functions/uac2.0 /sys/kernel/config/usb_gadget/g1/configs/c.1, ядро вызовет USB. Функция config_usb_cfg_link гаджета configfs. Основная задача функции config_usb_cfg_link — получить управляемую функцией структуру данных usb_function. Процесс выполнения выглядит следующим образом:
Как показано на рисунке ниже, когда пользовательское пространство выполняет echo fe800000.dwc3 > /sys/kernel/config/usb_gadget/g1/UDC, ядро вызовет USB gadget Функция гаджета_dev_desc_UDC_store из configfs. Основная задача функции gadget_dev_desc_UDC_store — привязать usb_gadget_driver к базовому USB-контроллеру. usb_gadget_driver эквивалентен мосту. Два конца моста — это функциональный драйвер и драйвер UDC. Процесс выполнения следующий:
В этом разделе в качестве примера используется uac2, чтобы представить использование пользовательского пространства конфигурации USB-гаджета и рабочий процесс в ядре. configfs USB-устройства предоставляет удобный метод настройки. Пользователи могут гибко организовывать функциональные драйверы USB для формирования составных устройств USB-устройств с различными функциями. После завершения настройки configfs USB-устройства не участвует в рабочем процессе составного устройства USB-устройства. Структура данных usb_gadget_driver эквивалентна мосту. Вместе с функциональным драйвером и драйвером UDC configfs USB-гаджета обеспечивает реализацию usb_gadget_driver как configfs_driver_template. Для устаревшего драйвера его реализация отличается. В этом разделе не рассматривается рабочий процесс драйвера uac2, а также процесс отправки и получения данных. Они будут подробно описаны в главе о драйвере uac2.