Содержание


Обслуживание периферии в коде модулей ядра: Часть 55. Взаимодействие с USB-устройствами в коде модулей ядра

Comments

В этой статье мы рассмотрим, как сменные USB-устройства отображаются в код модуля ядра, и какие операции с данными устройствами можно осуществлять в программном коде модуля.

Связывание USB-устройств

В архиве usb.tgz (см. ссылку в разделе "Материалы для скачивания") находится модуль lab1_usb.ko, содержащий примеры подключения, отключения и регистрации USB-устройства. Общие принципы алгоритмов, представленные в этих примерах, были позаимствованы из упоминавшейся ранее книги Джерри Купперстайна (Jerry Cooperstein) "Writing Linux Device Drivers", но при этом в получившийся код были внесены достаточно существенные изменения. Читатели же, в свою очередь, при запуске этих примеров обязательно должны заменить VID:PID-идентификаторы USB-устройств, приведённые в коде примера, на аналогичные идентификаторы устройств, присутствующих в системе. Но прежде, чем рассматривать пример, отметим, что регистрация USB-устройства во многом напоминает регистрацию PCI-устройства. Основой для связывания является определяемая разработчиком большая структура данных struct usb_driver, исходный код которой можно найти в файле <linux/usb.h>.

struct usb_driver {
  const char *name;
  int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);
  void (*disconnect) (struct usb_interface *intf);
  int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
  ...
}

Однако, в отличии от подхода, используемого для инициализации PCI-устройств, функции обратного вызова probe() и disconnect() вызываются не при загрузке и выгрузке модуля, а при физическом подключении и отключении USB-устройства, как показано в листинге 1.

Листинг 1. Подключение и отключение USB-устройства.
#include <linux/module.h>
#include <linux/usb.h>

// собственная структура данных, неизвестная ядру
struct my_usb_info {
   int connect_count;
};

#define USB_INFO KERN_INFO "MY: "

static int my_usb_probe( struct usb_interface *intf, const struct usb_device_id *id ) {
   struct my_usb_info *usb_info;
   struct usb_device *dev = interface_to_usbdev( intf );
   static int my_counter = 0;
   printk( USB_INFO "connect\n" );
   printk( USB_INFO "devnum=%d, speed=%d\n", dev->devnum, (int)dev->speed );
   printk( USB_INFO "idVendor=0x%hX, idProduct=0x%hX, bcdDevice=0x%hX\n",
           dev->descriptor.idVendor,
           dev->descriptor.idProduct, dev->descriptor.bcdDevice );
   printk( USB_INFO "class=0x%hX, subclass=0x%hX\n",
           dev->descriptor.bDeviceClass, dev->descriptor.bDeviceSubClass );
   printk( USB_INFO "protocol=0x%hX, packetsize=%hu\n",
           dev->descriptor.bDeviceProtocol,
           dev->descriptor.bMaxPacketSize0 );
   printk( USB_INFO "manufacturer=0x%hX, product=0x%hX, serial=%hu\n",
           dev->descriptor.iManufacturer, dev->descriptor.iProduct,
           dev->descriptor.iSerialNumber);
   usb_info = kmalloc( sizeof( struct my_usb_info ), GFP_KERNEL );
   usb_info->connect_count = my_counter++;
   usb_set_intfdata( intf, usb_info );
   printk( USB_INFO "connect_count=%d\n\n", usb_info->connect_count );
   return 0;
}

static void my_usb_disconnect( struct usb_interface *intf ) {
   struct my_usb_info *usb_info;
   usb_info = usb_get_intfdata(intf);
   printk( USB_INFO "disconnect\n" );
   kfree( usb_info );
}

static struct usb_device_id my_usb_table[] = {
   { USB_DEVICE( 0x046d, 0x080f ) }, // Logitech, Inc. - Webcam C120
   { }                               // обязательный пустой символ-терминатор
};

MODULE_DEVICE_TABLE( usb, my_usb_table );

static struct usb_driver my_usb_driver = {
   .name = "usb-my",
   .probe = my_usb_probe,
   .disconnect = my_usb_disconnect,
   .id_table = my_usb_table,
};

static int __init my_init_module( void ) {
   int err;
   printk( USB_INFO "Hello USB\n" );
   err = usb_register( &my_usb_driver );
   return err;
}

static void my_cleanup_module( void ) {
   printk( USB_INFO "Goodbye USB\n" );
   usb_deregister( &my_usb_driver );
}

module_init( my_init_module );
module_exit( my_cleanup_module );

В листинге 1 в качестве USB-устройства мы использовали Web-камеру Webcam C120 производства Logitech Inc. с VID:PID-идентификатором равным 046d:080f. Основная сложность при разработке и отладке подобного модуля для любого собственного устройства состоит в том, что из системы необходимо удалить модуль, который ранее поддерживал данное устройство и обрабатывал его подключение. Нужный модуль можно найти по сообщениям, выводимым при подключении данного USB-устройства. Существуют несколько способов обнаружить подобные сообщения. В данном случае для поиска модуля, обслуживающего Web-камеру, мы воспользовались системным журналом dmesg.

$ dmesg
...
usb 1-4: new high speed USB device using ehci_hcd and address 19
usb 1-4: New USB device found, idVendor=046d, idProduct=080f
usb 1-4: New USB device strings: Mfr=0, Product=0, SerialNumber=2
usb 1-4: SerialNumber: 1DC23270
usb 1-4: configuration #1 chosen from 1 choice
uvcvideo: Found UVC 1.00 device <unnamed> (046d:080f)
input: UVC Camera (046d:080f) as /devices/pci0000:00/0000:00:1d.7/usb1/...
$ lsmod | grep uvcvideo
uvcvideo               47532  0
videodev               28423  1 uvcvideo
v4l1_compat            11370  2 uvcvideo,videodev
$ sudo rmmod uvcvideo$ lsmod | grep uvcvideo

Теперь необходимо удалить обнаруженный модуль uvcvideo и проверить работу созданного модуля lab1_usb.ko при последовательных подключениях устройства.

Устанавливаем новый модуль для поддержки Web-камеры:

$ sudo insmod lab1_usb.ko.$ lsmod | grep lab
lab1_usb                1546  0
$ dmesg | tail -n 10
MY: Hello USB
...

Отключаем Web-камеру, разомкнув USB-кабель:

$ dmesg | tail -n 3
...
usb 1-4: USB disconnect, address 19
MY: disconnect

И снова подключаем кабель Web-камеры:

$ dmesg | tail -n 20
...
usb 1-4: new high speed USB device using ehci_hcd and address 20
usb 1-4: New USB device found, idVendor=046d, idProduct=080f
usb 1-4: New USB device strings: Mfr=0, Product=0, SerialNumber=2
usb 1-4: SerialNumber: 1DC23270
usb 1-4: configuration #1 chosen from 1 choice
...
MY: connect
MY: devnum=20, speed=3
MY: idVendor=0x46D, idProduct=0x80F, bcdDevice=0x9
MY: class=0xEF, subclass=0x2
MY: protocol=0x1, packetsize=64
MY: manufacturer=0x0, product=0x0, serial=2
MY: connect_count=1
...
$ sudo rmmod lab1_usb$ dmesg | tail -n 2
MY: Goodbye USB
usbcore: deregistering interface driver usb-my

Обмен данными с USB-устройствами

Код, приведённый в листинге 1, выполняет идентификацию USB-устройства и увязывание его в код модуля. Однако, он никак не затрагивает непосредственный обмен данными с устройством. Модель описания USB-устройства, в общем случае, включает в себя одну или несколько конфигураций для каждого устройства, но активной в любой момент времени может быть только одна из них. Конфигурации определяют один или несколько интерфейсов, каждый из которых может содержать различные параметры/настройки. Такие интерфейсы могут соответствовать стандарту USB, а могут быть специфичными лишь для определенного производителя/устройства. Интерфейсы имеют одну или более конечных точек (endpoint — EP), каждая из которых поддерживает один тип и направление передачи данных. Полная конфигурация может иметь до шестнадцати конечных точек в каждом направлении. Передача данных по протоколу USB осуществляется пакетами. Для каждой концевой точки хранится запись о максимальном размере пакета. Хост всегда является мастером в обмене независимо от направления, он устанавливает флаг направления обмена:

  • out— если хост отсылает данные устройству,
  • in— если хост отправляет запрос на приём данных из устройства.

Устройство никогда не инициирует передачу данных к хосту.

Любая операция по обмену данными может производиться только с конечной точкой устройства, которая может выступать как источник или приёмник данных. Устройство может иметь до 32 EP: 16 на приём и 16 на передачу. Обращение к выбранной EP происходит по её адресу (номеру). Стандарты USB поддерживает 4 типа передачи данных:

  • bulk— для пакетной передачи больших объёмов некритичной по времени информации, размер пакетов 8, 16, 32, 64 для USB 1.1 и 512 для USB 2.0, используется алгоритм подтверждения и повторной передачи (в случае возникновения ошибок), поэтому этот тип является достоверным, поддерживаются оба направления —in и out;
  • control— применяется для передачи конфигурационной и управляющей информации, используются алгоритмы подтверждения и повторной передачи, направления —in (status) и out (setup, control);
  • interrupt— для получения малых порций критичной по времени информации от устройства, размер пакета от 1 до 64 байт для USB 1.1 и до 1024 байт для USB 2.0, этот тип предполагает, что устройство будет опрашиваться со стороны хоста с заданным интервалом, направление только in;
  • isochronous— для передачи real-time потоков на фиксированных скоростях передачи без управления потоком (без подтверждения), область применения: аудио и видео-потоки, размер пакета до 1023 байт для USB 1.1 и до 1024 байт для USB 2.0, предусмотрен контроль ошибок на приёмной стороне по CRC16, направления —in и out.

Из представленного описания становится понятно, какие возможности открыты для разработчиков USB-устройств. Все алгоритмы, необходимые для работы USB-устройств, реализованы в ядре Linux, а программисту драйверов предлагается удобный и простой интерфейс в виде набора функций, макросов, структур, таких, например, как usb_register_dev(), interface_to_usbdev(), usb_control_msg() и т.д. С помощью этого API можно легко реализовать обмен данными в рамках любой конкретной задачи.

Непосредственная реализация обмена данными с USB-устройством, после его связывания с модулем ядра, зависит от специфики самого устройства и используемых протоколов. Задачи подобного рода не относятся к общей функциональности модуля ядра, поэтому различные подходы к реализации обмена данными между модулем ядра и USB-устройством также не стоит рассматривать в общем виде.

Заключение

На этом мы завершаем краткое введение в программную поддержку USB-устройств. Написание законченного примера обмена данными с USB-устройством изначально не входило в наши планы, в силу огромного разнообразия как самих устройств, так и различных подходов к реализации обмена данными. Но существует хорошее описание процесса создания драйвера для простого USB-устройства "от начала и до конца" (Павел Курочкин, «Разработка драйверов для USB-устройств под Linux»), которое можно найти по этому адресу. Эта публикация может послужить отправной точкой для написания собственных драйверов для USB-устройств.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=931592
ArticleTitle=Обслуживание периферии в коде модулей ядра: Часть 55. Взаимодействие с USB-устройствами в коде модулей ядра
publish-date=05282013