Получение информации об устройстве и поиск подходящего драйвера
1.1. Получение информации об устройстве
1.1.1. dmesg (util-linux)
1.1.2. lsusb (usbutils)
1.1.3. Дескрипторы
1.1.4. usb-devices (usbutils)
1.1.5. sane-find-scanner (sane-utils)1.2. Бэкенд
1.2.1. Конфигурационные файлы
1.2.2. Правила udev
1.2.3. Библиотеки
Перед вами исчерпывающее руководство по идентификации, установке и настройке сканирующих устройств в операционной системе (ОС) Linux. Описанные приемы и рекомендации применимы ко всем популярным дистрибутивам лишь с незначительными отличиями в виде различных пакетных менеджеров и используемых директорий с конфигурационными файлами. За время работы с клиентскими обращениями нашей команде удалось накопить огромный опыт по методам диагностики различных бэкендов всех имеющихся на рынке вендоров. Попытаемся охватить все этапы настройки устройства, начиная от его идентификации в ОС как сканирующего устройства (далее по тексту просто «устройство»), отладки и заканчивая приемами установки неподдерживаемых драйвером устройств без вмешательства в его код (но это неточно). Всё описанное затрагивает устройства, подключаемые только по локальному интерфейсу USB (но не SCSI). Предоставленный в тексте листинг команд выполнен в ОС Astra Linux Special Edition (ALSE) 1.7.4 и 1.7.6 в режиме защищенности «Смоленск» без сторонних репозиториев. При наличии особенностей, специфичных только для ALSE, это будет отмечено в конце раздела. Упоминаемые в тексте названия программ в скобках содержат название пакета, в состав которого они входят.
Пропущен первый (низший) уровень работы USB-стека — драйвер хост-контроллера, и внимание уделено рассмотрению событий обработки подключения, отключения, инициализации устройств, выбора драйвера и управления ресурсами, происходящих на втором (среднем) уровне, а также работе драйверов, поддерживающих определенные классы устройств на третьем (высшем) уровне. Использованные в тексте команды известны администраторам любого Linux-дистрибутива. Впрочем, это не мешает останавливаться на таких очевидных вещах, как описание параметров их выполнения. Впереди вас ожидают портянки консольного вывода, но без этого никак.
Для удобства восприятия руководство разбито на три части:
Получение информации об устройстве и поиск подходящего драйвера. Об этом и пойдет речь ниже.
Сбор информации об устройстве при помощи системных утилит, входящих в состав ОС, а также программ от разработчиков SANE. Расскажем, как физическое устройство (например, МФУ) представляется в ОС сразу и принтером, и сканером, и много ещё чем. Обязательно затронем тему дескрипторов и детально рассмотрим значение каждого из них. База, так сказать. Заглянем в типовой пакет проприетарного драйвера и на примерах покажем, как он должен выглядеть в идеальном современном мире. Глазами разработчиков SANE, конечно. Мы не будем с ними спорить.
Установка и конфигурирование устройства.
Во второй части начнем работать со сканирующим устройством. Разберем, каким образом файлу символьного устройства назначаются права и загружается драйвер (а иногда и модуль), как выполнять диагностику и отладку в случае возникновения проблем, и сведем все возможные проблемы в один список в порядке очередности их решения. Попробуем установить устройство без драйвера.
Практики использования SANE.
Вы уверены, что используете функционал SANE на полную? Рассмотрим некоторые из best practics (да, такие существуют).
Материал адресован системным администраторам, но также будет интересен и опытным пользователям, кто хочет разобраться в проблемах своего сканирующего или печатающего оборудования.
Получение информации об устройстве и поиск подходящего драйвера
Прежде всего, надо убедиться, что перед нами устройство, поддерживающее сканирование. Наличие USB-интерфейса и стекла для размещения копий ещё не говорит о том, что оно будет выполнять сканирование в Linux. В то же время в ОС Windows такое устройство сканировать будет. Почему так, разберемся дальше.
Подключаем устройство в первый попавшийся USB-порт.
Получение информации об устройстве
dmesg (util-linux)
Команда для просмотра событий ядра с символьного устройства /dev/kmsg. Начиная с ядра версии 4.8, выполнение требует повышенных привилегий.
sudo dmesg
[192897.481014] usb 4-1: new high-speed USB device number 2 using ehci-pci
[192898.044288] usb 4-1: New USB device found, idVendor=232b, idProduct=2732, bcdDevice= 1.00
[192898.044308] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[192898.044322] usb 4-1: Product: BM5100ADW series
[192898.044334] usb 4-1: Manufacturer: Pantum
[192898.044345] usb 4-1: SerialNumber: CK1A046238
[192898.528713] usblp 4-1:1.0: usblp0: USB Bidirectional printer dev 2 if 0 alt 0 proto 2 vid 0x232B pid 0x2732
[192898.528861] usbcore: registered new interface driver usblp
Выполнение dmesg без дополнительных параметров приведет к выводу огромного количества событий с момента загрузки ОС в хронологическом порядке. Поиск информации о нужном устройстве может вызвать затруднения. Запуск dmesg с параметрами -wT отобразит cобытия ядра (а операции, связанные с физическим подключением оборудования, относятся именно к ним) в режиме реального времени, а также с указанием временных меток в формате текущей даты и времени. Это может быть удобно при фиксации событий подключения множества устройств или акцентировании внимания именно на последних событиях.
Каждый администратор не раз видел эти строки. Остановимся на них и рассмотрим значения.
[192897.481014] usb 4-1: new high-speed USB device number 2 using ehci-pci
Временная метка (timestamp) — по умолчанию время в секундах с момента запуска ОС. Параметр -T сменит вывод на текущую дату и время в формате date.
Расположение устройства (devicepath) — номер шины и порта, к которому подключено устройство. Номера статичные, при переподключении устройства в тот же физический порт значения не поменяются.
Скорость обмена информацией (data transfer rate) с устройством — максимальная скорость передачи информации между хостом и устройством. Определяется хост-контроллером в зависимости от спецификации USB-устройства.
Номер устройства (device address from bus) — уникальный номер устройства на текущей шине, устанавливаемый по порядку 127 ? 1 : devnum + 1. Численное значение device number следующему подключенному USB-устройству будет увеличено на единицу. Предыдущие значения не назначаются новым устройствам. Переподключенное USB-устройство будет считаться новым и, следовательно, получит следующий порядковый номер.
Используемый интерфейсом хост-контроллера драйвер (host controller driver) — драйвер хост-контроллера. ohci&uhci соответствуют USB 1.0-1.1; ehci USB 2.0; xhci USB 3.0. По наименованию драйвера ещё нельзя с уверенностью заявить о версии самого USB-порта. Остается возможность использовать ehci-pci как на USB 2.0, так и на портах USB 3.0.
xhci hand-off
Производители современных материнских плат заложили возможность выбора режима работы портов USB 3.0 (xHCI) в BIOS. Возможны следующие конфигурации:
XHCI = disable -> все USB-порты в режиме USB 2.0.
XHCI = enable -> все USB-порты в режиме USB 3.0.
XHCI = auto -> все USB-порты будут работать в режиме USB 2.0 до начала загрузки модуля в ОС для USB 3.0, после которой USB станет USB 3.0. После перезагрузки ОС порт USB 3.0 переключится на 2.0. Такой режим может быть полезным при установке ОС, не имеющих в своем дистрибутиве драйверов на USB 3.0 (полезно для Windows OС).
XHCI = Smart Auto -> поведение аналогичное auto, но после перезагрузки не переключает на USB 3.0.
Также для некоторых производителей могут быть доступны дополнительные настройки, например Pre-Boot Mode (или XHCI pre-boot mode). Подробную информацию можно получить в технической документации материнской платы.
[192898.044288] usb 4-1: New USB device found, idVendor=232b, idProduct=2732, bcdDevice= 1.00
Событие об обнаружении нового USB-устройства, содержащее следующую информацию:
уникальный идентификатор производителя оборудования idVendor (значение 232b зарезервировано для Pantum);
идентификатор модели idProduct (значение 2732 устанавливается производителем);
аппаратная ревизия устройства bcdDevice (1.00).
Device release number
Заполнение производителем дескриптора bcdDevice —довольно редкое явление. Для получения актуальных данных об аппаратной ревизии рекомендуется использовать панель управления устройства или информацию о состоянии устройства со страницы диагностики. Между тем некоторую сервисную информацию об устройстве можно получить в расширенной диагностической информации бэкенда от Kyocera SANE_DEBUG_KYOCERA_WC3. Более подробно о выводе отладочной информации будет рассказано во второй части "Установка и конфигурирование устройства".
<...>
[kyocera_wc3] GetServiceInfoRes o_ServiceInfo.information:
[kyocera_wc3] GetServiceInfoRes o_ServiceInfo.modelName: FS-6525MFP
[kyocera_wc3] GetServiceInfoRes o_ServiceInfo.releaseDate: 2015.11.02
[kyocera_wc3] GetServiceInfoRes o_ServiceInfo.version: 2.0.131.2
<...>
[192898.044308] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[192898.044322] usb 4-1: Product: BM5100ADW series
[192898.044334] usb 4-1: Manufacturer: Pantum
[192898.044345] usb 4-1: SerialNumber: CK1A046238
Событие о считывании строковых дескрипторов:
Mfr с индексом 1
Product с индексом 2
SerialNumber с индексом 3
Далее происходит вывод значений этих дескрипторов в указанном индексами порядке.
Немного духоты
Почему три строковых значения прочитаны в порядке Mfr=1, Product=2, SerialNumber=3, а dmesg отображает их в другом порядке? Логика подсказывает, что вывод ниже должен выглядеть следующим образом:
[192898.044308] usb 4-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[192898.044334] usb 4-1: Manufacturer: Pantum #Mfr=1
[192898.044322] usb 4-1: Product: BM5100ADW series #Product=2
[192898.044345] usb 4-1: SerialNumber: CK1A046238
Похоже, что это опечатка [2], которая была у всех на глазах на протяжении 20 лет. В linux/drivers/usb, кроме hub.c[1], нигде более не используется этот порядок.
dev_info(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
udev->descriptor.iSerialNumber);
show_string(udev, "Product", udev->product);
show_string(udev, "Manufacturer", udev->manufacturer);
show_string(udev, "SerialNumber", udev->serial);
[192898.528713] usblp 4-1:1.0: usblp0: USB Bidirectional printer dev 2 if 0 alt 0 proto 2 vid 0x232B pid 0x2732
[192898.528861] usbcore: registered new interface driver usblp
Выбранная конфигурация устройства (configuration) — источник события от устройства на 4-й шине, 1 порту, 1 конфигурации, 0 интерфейсе. Более подробное рассмотрение значений дескрипторов — в разделе «Дескрипторы».
События об устройстве от модуля usblp (usblp module device info) — загружен модуль usblp для устройства с device number 2, 0 интерфейса, 0 альтернативной настройке, протоколом интерфейса 2 со значениями idVendor 0x232b, idProduct 0x2732. Более подробное рассмотрение порядка загрузки модулей активным интерфейсам — во второй части «Установка и конфигурирование устройства. udevadm (systemd)».
[192898.528861] usbcore: registered new interface driver usblp
Уведомление от usbcore о загрузке модуля usblp. Попытка выгрузить модуль вручную будет сопровождаться подобным событием.
[120347.118966] usbcore: deregistering interface driver usblp
За обработку событий отвечает код driver.c [3].
Наиболее важную информацию в выводе dmesg представляют значения:
idVendor=232b
idProduct=2732
На уникальных для каждого устройства значениях idVendor и idProduct основана вся логика его идентификации драйверами (равно как и модулями, а также бэкендами). Эти значения «вшиты» в ПЗУ устройства, благодаря чему гарантируется предсказуемость работы драйвера с определенным устройством.
драйвер хост-контроллера ehci-pci
Использование xhci драйвера накладывает свои ограничения для устройств, выпущенных до появления современных USB-протоколов семейства 3.Х. Несмотря на обратную совместимость с предыдущими USB 1.0-2.0, использование вкупе с устаревшими библиотеками libusb-0.1-4 может приводить к потере работоспособности отдельного интерфейса устройства или аппаратному сбросу хост-контроллера, к которому подключено устройство. Например, попытка выполнить сканирование в таких условиях будет приводить к сбросу (USB_RESET) устройства.
kernel: usb 1-1: reset high-speed USB device number 2 using xhci_hcd
по одной из следующих причин hub.c [1]
case -ENOENT: /* synchronous unlink */
case -ECONNRESET: /* async unlink */
case -ESHUTDOWN: /* hardware going away */
device number
Неестественно большая величина device number впервые подключенного устройства (> 10) в выводе dmesg может указывать на проблемы с его инициализацией в системе, если только не используется конфигурация со значительным количеством подключенных USB-устройств.
Отсутствие ошибок на данном этапе является свидетельством, что устройство инициализировано штатно. Между тем это ещё не означает, что оно будет корректно выполнять сканирование или печать. Во время выполнения операций сканирования или печати в dmesg будут попадать все аппаратные события от хост-контроллера, к которому подключено устройство. Анализ этих событий — первый шаг при диагностике неполадок.
lsusb (usbutils)
Выполнение этой команды уже не требует повышенных привилегий. В исключительных случаях команда может не отобразить информацию об устройстве, если отсутствуют права на чтения файла символьного устройства (660). Вывод содержит информацию о всех подключенных на текущий момент USB-устройствах.
lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 004: ID 232b:2732
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Обратите внимание: lsusb безошибочно считал информацию об idVendor и idProduct, но сведения о производителе и модели устройства отсутствуют. dmesg получил эту информацию из дескрипторов iProduct и iSerial, а lsusb смотрит в другое место — базу данных /lib/udev/hwdb.bin, список /var/lib/usbutils/usb.ids и правила в /lib/udev/hwdb.d/20-usb-vendor-model.hwdb.
Важно!
Данное поведение актуально для ядра 5.4.0-110-generic в Astra Linux Special Edition 1.7.4.
Убедиться в этом можно при помощи молотка и зубила strace (strace). Но предпочтительным способом будет выбор udevadm (systemd) в режиме отображения диагностической информации (подробнее — в разделе "udevadm (systemd)").
sudo strace lsusb
<...>
openat(AT_FDCWD, "/etc/systemd/hwdb/hwdb.bin", O_RDONLY|O_CLOEXEC) = -1 ENOENT (Нет такого файла или каталога)
openat(AT_FDCWD, "/etc/udev/hwdb.bin", O_RDONLY|O_CLOEXEC) = -1 ENOENT (Нет такого файла или каталога)
openat(AT_FDCWD, "/usr/lib/systemd/hwdb/hwdb.bin", O_RDONLY|O_CLOEXEC) = -1 ENOENT (Нет такого файла или каталога)
openat(AT_FDCWD, "/lib/systemd/hwdb/hwdb.bin", O_RDONLY|O_CLOEXEC) = -1 ENOENT (Нет такого файла или каталога)
openat(AT_FDCWD, "/lib/udev/hwdb.bin", O_RDONLY|O_CLOEXEC) = 3
<...>
Блокировка трассировки ptrace
В Astra Linux Special Edition в режиме защищенности «Смоленск» по умолчанию активен механизм запрета трассировки ptrace. Для возможности использовать strace необходимо отключить запрет командой.
sudo astra-ptrace-lock disable
Подробная информация — на странице справочного центра "Настройка механизмов защиты и блокировок" [5].
За актуальность базы данных hwdb.bin отвечает update-usbids (usb.ids). В популярных Linux-дистрибутивах для обновления списка idVendor и idProduct достаточно выполнить команду update-usbids. Программа получит свежий список из www.linux-usb.org/usb.ids и запустит обновление базы /lib/udev/hwdb.bin. Там же, где этой команды нет (например ALSE), остается дожидаться свежей версии пакета usb.ids в составе main-репозитория. В случае необходимости внести изменения о idVendor и idProduct в /lib/udev/hwdb.d/20-usb-vendor-model.hwdb выполнить обновление базы можно самостоятельно. Продемонстрируем на примере с Pantum BM5100ADW.
Внести изменения в файл /lib/udev/hwdb.d/20-usb-vendor-model.hwdb, добавив строки с:
idVendor для наименования производителя;
idVendor и idProduct для наименования модели устройства.
/lib/udev/hwdb.d/20-usb-vendor-model.hwdb
usb:v232B*
ID_VENDOR_FROM_DATABASE=Pantum Inc.
usb:v232Bp2732*
ID_MODEL_FROM_DATABASE=BM5100ADW
Сохранить изменения и выполнить обновление базы.
sudo systemd-hwdb --usr update
Особенности редактирования
Буквенные значения vXXXX и pYYYY следует указывать строго в верхнем регистре.
Добавление только одной строки с полным наименованием устройства
usb:v232Bp2732*
ID_MODEL_FROM_DATABASE=Pantum Inc. BM5100ADW
приведёт к появлению лишнего символа-пробела в выводе lsusb.
Bus 001 Device 004: ID 232b:2732 Pantum Inc. BM5100ADW
Запуск обновления без параметра --usr создаст базу данных в директории /etc/udev/hwdb.bin. Целевая /lib/udev/hwdb.bin останется нетронутой. Но, как известно из вывода strace, lsusb считает данные и из этой базы, и вывод всё же будет содержать обновленные данные.
Убедиться в правильности выполненных действий можно в повторном выводе lsusb
lsusb
Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 004: ID 232b:2732 Pantum Inc. BM5100ADW
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
В более свежих версиях ядер старше 5.4.0-110 поведение lsusb изменено. Данные считываются из sysfs:
/sys/bus/usb/devices/<bus>/<port>/manufacturer;
/sys/bus/usb/devices/<bus>/<port>/product.
Отныне точность данных о idVendor и idProduct всецело зависит от корректности заполнения дескриптора устройства производителем.
Дополнительные возможности запуска lsusb с параметром -d позволяют вывести информацию о конкретном устройстве по его ID
lsusb -d 232b:2732
Bus 001 Device 004: ID 232b:2732 Pantum Inc. BM5100ADW
или расширенную информацию о конкретном устройстве по пути к файлу символьного устройства при помощи параметра -D.
lsusb -D /dev/bus/usb/001/004
Device: ID 232b:2732 Pantum Inc. BM5100ADW
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x232b Pantum
idProduct 0x2732 BM5100ADW
<...>
Вывод lsusb с параметром -t будет полезен для получения информации об иерархии подключения устройства к корневому концентратору.
lsusb -t
/: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 5000M
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=vhci_hcd/8p, 480M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=uhci_hcd/2p, 12M
|__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 2: Dev 3, If 0, Class=Hub, Driver=hub/7p, 12M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/6p, 480M
|__ Port 1: Dev 4, If 0, Class=Printer, Driver=usblp, 480M
|__ Port 1: Dev 4, If 1, Class=Vendor Specific Class, Driver=, 480M
|__ Port 1: Dev 4, If 2, Class=Vendor Specific Class, Driver=, 480M
В данном примере будет полезной информация о подключении устройства в 1 порт концентратора на первой шине с помощью драйвера ehci-pci. С большой долей уверенности можно заявить, что устройство подключено в USB 2.0.
Не факт
Между тем существует возможность использовать физические порты USB 3.0 в режиме xhci = off. В таком случае в системе для них будет загружен ehci-драйвер.
О выборе режима работы портов в ОС при помощи настроек BIOS упоминалось в разделе"dmesg (util-linux)".
Дескрипторы
Дескриптор — это поток данных об устройстве, передаваемый на хост в строгом соответствии со стандартом Universal Serial Bus Specification [6]. Инициатором передачи всегда является хост.
Приведем для примера вывод lsusb с параметром -v с максимальной детализацией с трех разных сканирующих устройств. В данном случае все три - многофункциональные устройства (МФУ).
Pantum BM5100ADW
Bus 001 Device 003: ID 232b:2732
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x232b
idProduct 0x2732
bcdDevice 1.00
iManufacturer 1 Pantum
iProduct 2 BM5100ADW series
iSerial 3 CK1A042222
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x009a
bNumInterfaces 3
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 2mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 2 Bidirectional
iInterface 5 BM5100ADW series
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 4
iInterface 6 IPP Printer
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 7 Scanner Data
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 EP 4 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x000c 1x 12 bytes
bInterval 10
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 4
iInterface 8 IPP Printer
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x85 EP 5 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 1
bInterfaceProtocol 1
iInterface 9 EWS
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x86 EP 6 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x05 EP 5 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 4
iInterface 10 IPP Printer
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x06 EP 6 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x87 EP 7 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
bNumConfigurations 1
can't get debug descriptor: Resource temporarily unavailable
Device Status: 0x0001
Self Powered
Kyocera FS-1020MFP
Bus 001 Device 005: ID 0482:0495 Kyocera Corp.
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x0482 Kyocera Corp.
idProduct 0x0495
bcdDevice 3.27
iManufacturer 1 Kyocera
iProduct 2 FS-1020MFP
iSerial 3 LDA3910333
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0037
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 2mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 2 Bidirectional
iInterface 4 MFP Printer
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 5 MFP Scanner
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 EP 4 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
can't get debug descriptor: Resource temporarily unavailable
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
bNumConfigurations 1
Device Status: 0x0001
Self Powered
Xerox WorkCentre 3345
Bus 001 Device 004: ID 0924:42e3 Xerox
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x0924 Xerox
idProduct 0x42e3
bcdDevice 1.00
iManufacturer 1 Xerox
iProduct 2 WorkCentre 3345
iSerial 3 5311117019
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0037
bNumInterfaces 2
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xc0
Self Powered
MaxPower 2mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 EP 4 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 2 Bidirectional
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes can't get debug descriptor: Resource temporarily unavailable
2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 10
Device Status: 0x0100
(Bus Powered)
Ошибка "can't get debug descriptor"
Сообщения Device Qualifier (for other device speed) об ошибках:
Resource temporarily unavailable
Device Qualifier (for other device speed):
bLength 10
bDescriptorType 6
can't get debug descriptor: Resource temporarily unavailable
can't get device qualifier: Resource temporarily unavailable
can't get debug descriptor: Resource temporarily unavailable
Носят информационный характер и не влияют на функционал устройства.
Секция с Device Qualifier отображает дескрипторы, которые предоставит хосту high-speed устройство при подключении на других скоростях (full-speed\low-speed) [9]. Значения полей idVendor, idProduct, bcdDevice, iManufacturer, iProduct, iSerial не будут отображены, так как эти значения одинаковы для всех скоростей подключения.
Понимая структуру и назначение основных дескрипторов устройства, можно всегда быть уверенным в том, каким функционалом оно обладает. Структура устройства USB Device, которую описывают дескрипторы.
В спецификации USB существуют следующие стандартные дескрипторы:
Устройство (Device Descriptor).
Дескриптор устройства описывает общую информацию для всего устройства USB. Она включает данные, которые применяются устройством глобально и во всех его конфигурациях. USB-устройство имеет только один дескриптор устройства.
2. Конфигурация (Configuration Descriptor).
Дескриптор конфигурации описывает информацию о текущей конфигурации устройства. Дескриптор содержит поле bConfigurationValue, значение которого используется как параметр в запросе Set Configuration, который заставляет устройство переходить в описанную конфигурацию. Устройства могут иметь несколько дескрипторов конфигураций и как минимум одну. В определённый момент времени только одна конфигурация может быть активна. Configuration Descriptor содержит требования к мощности порта хост-контроллера, пропускной способности, а также поддерживаемые функции приостановки и возобновления работы.
3. Интерфейс (Interface Descriptor).
Дескриптор интерфейса описывает набор конечных точек (Endpoint Descriptor), включенных в интерфейс для заданной конфигурации. Дескриптор интерфейса всегда возвращается как часть дескриптора конфигурации. В дескрипторе интерфейса никогда не отображается нулевая конечная точка. Но она есть. Наличие нескольких дескрипторов конфигурации как раз и позволяет одному физическому устройству (МФУ) представляться несколькими логическими (принтером, сканером, накопителем) одновременно через один USB-порт. Каждый активный интерфейс может использовать отдельный модуль (драйвер). И что ещё интересно, каждый интерфейс может иметь альтернативные настройки bAlternateSetting, которые позволяют изменять часть конфигурации устройства, не затрагивая другие интерфейсы. К примеру, у трех интерфейсов Pantum есть альтернативные настройки bAlternateSetting. Эти настройки задействуются в режиме IPP-over-USB [7]. Подробнее - в третьей части "Разбираемся со сканерами в Linux: практики использования SANE. ipp-usb"):
Pantum alternative
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 2 Bidirectional
iInterface 5 BM5100ADW series
Endpoint Descriptor:
<...>
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 4
iInterface 6 IPP Printer
Endpoint Descriptor:
<...>
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 7 Scanner Data
Endpoint Descriptor:
<...>
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 4
iInterface 8 IPP Printer
<...>
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 1
bInterfaceProtocol 1
iInterface 9 EWS
Endpoint Descriptor:
<...>
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7 Printer
bInterfaceSubClass 1 Printer
bInterfaceProtocol 4
iInterface 10 IPP Printer
Endpoint Descriptor:
<...>
4. Конечная точка (Endpoint Descriptor).
Дескриптор конечной точки содержит информацию, требуемую хостом, чтобы определить требования по пропускной способности каждой конечной точки. Дескриптор конечной точки всегда возвращается как часть дескриптора конфигурации. Нулевая конечная точка (EP 0) не имеет дескриптора.
5. Строка (String Descriptor).
Строковые дескрипторы описывают характеристики устройства текстовыми строками в удобочитаемом виде. В то же время строковые дескрипторы необязательны. Если устройство не поддерживает строковые дескрипторы, все ссылки к строковым дескрипторам внутри устройства, конфигурации и дескрипторах интерфейса должны быть установлены в нулевое значение. При запросе строкового дескриптора хост определяет требуемый язык (LANGID).
Каждый дескриптор начинается с байта, в котором указана общая длина дескриптора (bLength). Она следует за байтом, обозначающим тип дескриптора (bDescriptorType).
Текущего описания будет достаточно. Если сухой язык исчерпывающей технической документации Universal Serial Bus Specification [6] на английском языке сложен для восприятия, более подробно о дескрипторах можно узнать из статьи коллег по цеху на хабре [8].
Как полюбить вывод lsusb -v и получать максимум ценной информации из него?! Понять значение каждого дескриптора! Расшифруем вывод для планшетного сканера Mustek BearPaw 2448 TA Plus.
lsusb -d 055f:021a -v
Bus 003 Device 004: ID 055f:021a Mustek Systems, Inc. BearPaw 2448 TA Plus
Device Descriptor:
bLength 18 # размер в байтах дескриптора устройства
bDescriptorType 1 # константа, тип дескриптора для устройства: 1
bcdUSB 1.10 # спецификация USB. Устройство поддерживает USB 1.1. Не путать с аппаратной ревизией bcdDevice
bDeviceClass 0 # значения bDeviceClass,
bDeviceSubClass 0 ## bDeviceSubClass,
bDeviceProtocol 0 ### bDeviceProtocol для дескриптора устройства могут отсутствовать. В таком случае эти значения будут указаны в дескрипторе интерфейса
bMaxPacketSize0 64 # максимальный размер пакета в байтах для управляющей (EP 0) конечной точки устройства
idVendor 0x055f Mustek Systems, Inc. # идентификатор производителя устройства
idProduct 0x021a BearPaw 2448 TA Plus # идентификатор модели устройства
bcdDevice 1.00 # аппаратная ревизия устройства
iManufacturer 0 # индекс строкового дескриптора с описанием производителя устройства: 0
iProduct 1 USB Scanner # индекс строкового дескриптора с описанием наименования устройства: 1
iSerial 0 # индекс строкового дескриптора с описанием серийного номера устройства: 0
bNumConfigurations 1 # количество конфигураций устройства
Configuration Descriptor:
bLength 9 # размер в байтах дескриптора конфигурации
bDescriptorType 2 # константа, тип дескриптора для конфигурации: 2
wTotalLength 0x0020 # сумма в байтах всех дескрипторов (конфигурации, интерфейса, конечной точки и class- или vendor-specific), возвращенных для этой конфигурации: (9+9+7+7)
bNumInterfaces 1 # количество интерфейсов в текущей конфигурации: 1
bConfigurationValue 1 # значение дескриптора, используемое как параметр в запросе Set Configuration, который заставляет устройство переходить в описанную конфигурацию
iConfiguration 0 # индекс строкового дескриптора, описывающего текущую конфигурацию
bmAttributes 0xa0 # битовое поле, определяет свойства питания устройства*
(Bus Powered) # соответствует значению 0
Remote Wakeup # соответствует значению 1
MaxPower 500mA # максимальный ток потребления (в миллиамперах), который устройство в этой конфигурации может использовать от шины. Задается в единицах, кратных 2 миллиамперам. lsusb уже перевёл значение 250 в миллиамперы: (250х2)
Interface Descriptor:
bLength 9 # размер в байтах дескриптора интерфейса
bDescriptorType 4 # константа, тип дескриптора для интерфейса: 4
bInterfaceNumber 0 # номер интерфейса в массиве параллельных интерфейсов, поддерживаемых данной конфигурацией. Если бы ниже был второй интерфейс, его bInterfaceNumber равнялся бы 1
bAlternateSetting 0 # номер альтернативной конфигурации для интерфейса, указанного в bInterfaceNumber
bNumEndpoints 2 # количество конечных точек (EP) в интерфейсе, не считая управляющей (EP0) конечной точки, EP0 не имеет дескриптора
bInterfaceClass 255 Vendor Specific Class # класс, подкласс и протокол интерфейса**
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 0 # индекс строкового дескриптора интерфейса
Endpoint Descriptor: # описание 1 из 2 конечных точек
bLength 7 # размер в байтах дескриптора конечной точки
bDescriptorType 5 # константа, тип дескриптора для конечной точки: 5
bEndpointAddress 08x1 EP 1 IN # порядковый номер и направление передачи конечной точки***: 1 IN
bmAttributes 2 # тип конечной точки - 00(0): control, 01(1): Isochronous, 10(2): Bulk, 11(3): Interrupt
Transfer Type Bulk # тип передачи: Bulk (передача данных)
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes # максимальный размер пакета данных, используемый конечной точкой
bInterval 0 # интервал при опросе конечной точки в миллисекундах. Игнорируется для конечных точек типа Bulk и Control
Endpoint Descriptor: # описание 2 из 2 конечных точек
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT # порядковый номер и направление передачи конечной точки***: 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0040 1x 64 bytes
bInterval 0
*все USB-устройства делятся на 3 класса по потребляемой энергии: питаемые от шины, питаемые от шины с высоким потреблением энергии и имеющие собственный источник энергии. USB-устройства, подключаемые к шине USB, могут получать энергию по самой шине.
Класс с низким потреблением (low‑power) — потребление меньше 100 mA.
Класс мощных потребителей (high‑power) — потребление до 500 mA от шины.
Класс с собственным источником питания (self‑powered) — также могут потреблять энергию дополнительно от шины, но не боле 100 миллиампер.
Значение 0xa0=10100000. Первые пять бит (D0..D4) зарезервированы и всегда равны 0. Если установлен бит 5 (D5), значит USB-устройство может являться источником сигнала пробуждения. Если установлен бит 6 (D6), то это указывает, что USB-устройство имеет собственный источник питания. Бит 7 (D7) зарезервирован и установлен в значение 1.
Несмотря на то, что Mustek BearPaw 2448 TA Plus имеет отдельный блок питания на 1.0А и без него просто не работает, значение D6=0 (Bus Powered).
**для интерфейсов сканера отсутствует отдельный класс, в его роли выступает 255 Vendor Specific. В то же время класс bInterfaceClass 255 не означает, что перед нами интерфейс для сканирования. Если bInterfaceProtocol установлен в 0, устройство не использует протокол, определяемый классом на этом интерфейсе. Описание [10] всех классов интерфейсов.
***значение 0х81=10000001. Первые 4 бита (D0..D3) номер конечной точки (1), биты D4-D6 зарезервированы, D7 - направление.
Обратите внимание: класс питания для одного устройства может меняться при выборе активной конфигурации этого же устройства.
С планшетным сканером Mustek с точки зрения структуры дескрипторов все довольно примитивно — один интерфейс bInterfaceClass=255 с приемом bEndpointAddress=08x1 EP 1 IN и отправкой данных bEndpointAddress=0x02 EP 2 OUT. Оно и понятно: главная функциональная задача — сканирование.
Получив информацию о значении каждого дескриптора, взглянем на вывод lsusb -v трех устройств выше:
Xerox WorkCentre 3345. К одному интерфейсу сканера bInterfaceClass=255 присоединяется второй bInterfaceClass=7 Printer с двунаправленным интерфейсом bInterfaceProtocol=2 с отправкой данных на принтер в bEndpointAddress=0x02 EP 2 OUT и получением от него статуса и другой информации из bEndpointAddress=0x81 EP 1 IN. Очевидно, что это многофункциональное устройство.
Kyocera FS-1020MFP. Все аналогично с Xerox WorkCentre 3345, но некоторые дескрипторы заданы и имеют значение:
bcdDevice 3.27 — если производитель следит за актуальностью дескриптора, то по нему можно различать аппаратные ревизии устройства.
iInterface 4 MFP Printer — строковый дескриптор с описанием назначения интерфейса. Мелочь, а приятно.
iInterface 5 MFP Scanner — аналогично, с описанием интерфейса для сканирования.
Pantum BM5100ADW series — попробуйте считать самостоятельно.
Приходилось сталкиваться и с более сложными по структуре дескрипторов устройствами. Например, протяжным сканером Kodak Alaris S2040, у которого две конфигурации Configuration Descriptor, заботливо именованных как Config 1 и Config 2. Каждая конфигурация предназначена для работы с определенной спецификацией порта - USB 2.0 (high-speed, low-speed) или USB 3.0 (super-speed). Для Config 1 ( super-speed) используется единственный интерфейс с 6 EP (Endpoints), для high-speed - первый интерфейс Config 2 с 4 EP, для low-speed - второй интерфейс Config 2 с 2 EP.
Alaris S2040
Bus 001 Device 005: ID 29cc:1026
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.10
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x29cc
idProduct 0x1026
bcdDevice 0.01
iManufacturer 1 Kodak Alaris Inc.
iProduct 2 Alaris S2040 Scanner
iSerial 3 67876187
bNumConfigurations 2
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x003c
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 4 Config 1
bmAttributes 0xc0
Self Powered
MaxPower 80mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 6
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 5 Cfg1Intf0FFS
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0045
bNumInterfaces 2
bConfigurationValue 2
iConfiguration 6 Config 2
bmAttributes 0xc0
Self Powered
MaxPower 80mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 4
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 7 Cfg2Intf0FFS
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 EP 1 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 EP 2 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 8 Cfg2Intf1FFS
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 EP 3 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 EP 3 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 0
Binary Object Store Descriptor:
bLength 5
bDescriptorType 15
wTotalLength 0x0016
bNumDeviceCaps 2
USB 2.0 Extension Device Capability:
bLength 7
bDescriptorType 16
bDevCapabilityType 2
bmAttributes 0x00000002
HIRD Link Power Management (LPM) Supported
SuperSpeed USB Device Capability:
bLength 10
bDescriptorType 16
bDevCapabilityType 3
bmAttributes 0x00
wSpeedsSupported 0x000e
Device can operate at Full Speed (12Mbps)
Device can operate at High Speed (480Mbps)
Device can operate at SuperSpeed (5Gbps)
bFunctionalitySupport 1
Lowest fully-functional device speed is Full Speed (12Mbps)
bU1DevExitLat 1 micro seconds
bU2DevExitLat 500 micro seconds
Device Status: 0x0001
Self Powered
Вы будете удивлены, но наличие возможности подключения МФУ по USB не равнозначно возможности выполнять печать и сканировать по этому интерфейсу. В конфигурации просто-напросто может отсутствовать интерфейс bInterfaceClass 255. Неочевидный факт, особенно учитывая, что в Windows такое устройство будет успешно и печатать, и сканировать.
usb-devices (usbutils)
В более сжатом виде информацию о дескрипторах представляет usb-devices.
usb-devices
T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#= 3 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=232b ProdID=2732 Rev=01.00
S: Manufacturer=Pantum
S: Product=BM5100ADW series
S: SerialNumber=CK1A046238
C: #Ifs= 3 Cfg#= 1 Atr=c0 MxPwr=2mA
I: If#=0x0 Alt= 0 #EPs= 2 Cls=07(print) Sub=01 Prot=02 Driver=usblp
I: If#=0x1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
I: If#=0x2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none)
Обратите внимание: в выводе учтены индексы строковых дескрипторов Mfr=1, Product=2, SerialNumber=3, и они расположены в верном порядке. Разработчик сделал [11] все правильно:
printf "P: Vendor=%s ProdID=%s Rev=%s.%s\n" \
"$vendid" "$prodid" "$revmajor" "$revminor"
print_string manufacturer "Manufacturer"
print_string product Product
print_string serial SerialNumber
usb-devices использует сокращения из документа proc_usb_info [12].
T = Topology (etc.)
B = Bandwidth (applies only to USB host controllers, which are
virtualized as root hubs)
D = Device descriptor info.
P = Product ID info. (from Device descriptor, but they won't fit
together on one line)
S = String descriptors.
C = Configuration descriptor info. (* = active configuration)
I = Interface descriptor info.
E = Endpoint descriptor info.
Вывод довольно информативен и не занимает много места в консоли. Отображена структура устройства, перечислены только активные интерфейсы, а также появилась информация об использованном первым интерфейсом драйвере (модуле ядра) usblp.
Вывод содержимого файла /sys/kernel/debug/usb/devices дополнительно выведет информацию об альтернативных конфигурациях каждого интерфейса.
cat /sys/kernel/debug/usb/devices
T: Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=480 MxCh= 0
D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
P: Vendor=232b ProdID=2732 Rev= 1.00
S: Manufacturer=Pantum
S: Product=BM5100ADW series
S: SerialNumber=CK1A046238
C:* #Ifs= 3 Cfg#= 1 Atr=c0 MxPwr= 2mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=07(print) Sub=01 Prot=02 Driver=usblp
E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=1250us
I: If#= 0 Alt= 1 #EPs= 2 Cls=07(print) Sub=01 Prot=04 Driver=usblp
E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=82(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 1 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=(none)
E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=1250us
E: Ad=84(I) Atr=03(Int.) MxPS= 12 Ivl=64ms
I: If#= 1 Alt= 1 #EPs= 2 Cls=07(print) Sub=01 Prot=04 Driver=(none)
E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=01 Prot=01 Driver=(none)
E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
I: If#= 2 Alt= 1 #EPs= 2 Cls=07(print) Sub=01 Prot=04 Driver=(none)
E: Ad=06(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
Файл /sys/kernel/debug/usb/devices может отсутствовать, в случае если не смонтирована debugfs.
Пришло время размяться и посмотреть, как выглядит обмен данными с устройством при помощи осциллографа [13]. К сожалению, у меня не нашлось под рукой Keysight dsox4024a, поэтому остается довольствоваться дампами USB-трафика.
Как собрать дампы
Загрузить модуль usbmon.
sudo modprobe usbmon
sudo apt install wireshark -y
sudo wireshark
Выполнить запуск wireshark с правами суперпользователя и выбрать активный интерфейс, например usbmon0.
Или прямо из консоли.
sudo apt install tcpdump
sudo modprobe usbmon
sudo tcpdump -i usbmon0 -s0 -w scan.pcap
Подробное описание usbmon [14]
Если под рукой нет wireshark, можно смотреть поток данных сразу из /sys/kernel/debug/usb/usbmon/1u. Внимание! Расшифровка вывода требует особых знаний и навыков.
Инициализация является линейным процессом, в котором устройство последовательно должно пройти через 5 своих состояний Attached > Powered > Default > Adress > Configured. На схеме ниже описан заключительный процесс установки конфигурации Mustek BearPaw 2448 TA Plus.
В wireshark этот этап уместился в 12 строк и выполнил расшифровку дескрипторов (Setup Data) за нас.
Но ведь у Pantum есть ещё одна конечная точка - E: Ad=84(I) Atr=03(Int.) MxPS= 12 Ivl=64ms с типом Interrupt, которая в дампе не оставила никаких следов. Это тип каналов прерываний (Interrupt), которые обеспечивают своевременную и надежную передачу небольших объемов неструктурированных данных. 64ms — частота опроса хостом устройства. Для low-, full-speed устройств значение может принимать [15] от 1 мс до 255 мс, high-speed — от 125 мкс до 4096 мс. Каналы прерываний бывают только однонаправленные (IN). Максимальный размер пакета в байтах для low-, full-speed — 64; high-speed — 1024.
Да-да, но где же этот пакет в дампе wireshark? Его удалось поймать, только собрав данные из потока /sys/kernel/debug/usb/usbmon/1u (все устройства на 001 шине)
cat /sys/kernel/debug/usb/usbmon/1u
ffff8e35b8600240 311439430 S Ci:1:001:0 s a3 00 0000 0001 0004 4 <
ffff8e35b8600240 311439456 C Ci:1:001:0 0 4 = 01050100
ffff8e35b8600240 311439460 S Co:1:001:0 s 23 01 0010 0001 0000 0
ffff8e35b8600240 311439489 C Co:1:001:0 0 0
ffff8e35b8600240 311439492 S Ci:1:001:0 s a3 00 0000 0002 0004 4 <
ffff8e35b8600240 311439495 C Ci:1:001:0 0 4 = 00010000
ffff8e35b8600240 311439496 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
ffff8e35b8600240 311439499 C Ci:1:001:0 0 4 = 00010000
ffff8e35b8600240 311439500 S Ci:1:001:0 s a3 00 0000 0004 0004 4 <
ffff8e35b8600240 311439503 C Ci:1:001:0 0 4 = 00010000
ffff8e35b8600240 311439504 S Ci:1:001:0 s a3 00 0000 0005 0004 4 <
ffff8e35b8600240 311439507 C Ci:1:001:0 0 4 = 00010000
ffff8e35b8600240 311439508 S Ci:1:001:0 s a3 00 0000 0006 0004 4 <
ffff8e35b8600240 311439510 C Ci:1:001:0 0 4 = 00010000
ffff8e35be675900 311544673 S Ii:1:001:1 -115:2048 4 < # Вот он!
ffff8e35b8600240 311544707 S Ci:1:001:0 s a3 00 0000 0001 0004 4 <
ffff8e35b8600240 311544718 C Ci:1:001:0 0 4 = 01050000
ffff8e35b8600240 311544728 S Co:1:001:0 s 23 03 0004 0001 0000 0
ffff8e35b8600240 311544797 C Co:1:001:0 0 0
<...>
60 событий
Wireshark фиксирует только 59 событий. Расшифровка строки:
ffff8e35be675900 311544673 S Ii :1 :001 :1 -115 :2048 4 <
URB Tag Timestamp Event type Pipe type Bus Number Device Endpoint URB status Interval Start Frame
URB-статус "-115" соответствует [16] Operation now in progress (-EINPROGRESS)
sane-find-scanner (sane-utils)
И вот только сейчас приступаем к использованию утилит, имеющих непосредственное отношение к сканирующим устройствам. Из-за отсутствия отдельного класса для устройства Scanner в дескрипторах из спецификации USB не все утилиты могут с высокой точностью определить устройство как сканер; sane-find-scanner из состава SANE был написан как раз для одной этой задачи. Обнаружив в её выводе строку «found possible USB scanner», можно сказать, что перед вами устройство с функцией сканирования. Но этого всё ещё недостаточно, чтобы заявить, что оно будет работать в ОС. С пользовательской точки зрения, sane-find-scanner бережёт нервы при расшифровке дескрипторов.
Интересный синтаксис использования вывода подробной диагностической информации.
Вот так выглядит запуск с максимальным выводом (да, именно -v -v, но не -vv. Иначе не все увидите):
sudo sane-find-scanner -v -v
<...>
<device descriptor of 0x232b/0x2732 at 001:010>
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 64
idVendor 0x232B
idProduct 0x2732
bcdDevice 1.00
iManufacturer 1 ()
iProduct 2 ()
could not fetch string descriptor: Pipe error
iSerialNumber 3 ()
bNumConfigurations 1
<configuration 0>
bLength 9
bDescriptorType 2
wTotalLength 154
bNumInterfaces 3
bConfigurationValue 1
iConfiguration 0 ()
bmAttributes 192 (Self-powered)
MaxPower 2 mA
<interface 0>
<altsetting 0>
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 7
bInterfaceSubClass 1
bInterfaceProtocol 2
could not fetch string descriptor: Pipe error
iInterface 5 ()
<endpoint 0>
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 (in 0x01)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 10 ms
bRefresh 0
bSynchAddress 0
<endpoint 1>
bLength 7
bDescriptorType 5
bEndpointAddress 0x01 (out 0x01)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 10 ms
bRefresh 0
bSynchAddress 0
<altsetting 1>
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7
bInterfaceSubClass 1
bInterfaceProtocol 4
could not fetch string descriptor: Pipe error
iInterface 6 ()
<endpoint 0>
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 (out 0x02)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<endpoint 1>
bLength 7
bDescriptorType 5
bEndpointAddress 0x82 (in 0x02)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<interface 1>
<altsetting 0>
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 3
bInterfaceClass 255
bInterfaceSubClass 255
bInterfaceProtocol 255
could not fetch string descriptor: Pipe error
iInterface 7 ()
<endpoint 0>
bLength 7
bDescriptorType 5
bEndpointAddress 0x83 (in 0x03)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 10 ms
bRefresh 0
bSynchAddress 0
<endpoint 1>
bLength 7
bDescriptorType 5
bEndpointAddress 0x03 (out 0x03)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 10 ms
bRefresh 0
bSynchAddress 0
<endpoint 2>
bLength 7
bDescriptorType 5
bEndpointAddress 0x84 (in 0x04)
bmAttributes 3 (interrupt)
wMaxPacketSize 12
bInterval 10 ms
bRefresh 0
bSynchAddress 0
<altsetting 1>
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7
bInterfaceSubClass 1
bInterfaceProtocol 4
could not fetch string descriptor: Pipe error
iInterface 8 ()
<endpoint 0>
bLength 7
bDescriptorType 5
bEndpointAddress 0x04 (out 0x04)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<endpoint 1>
bLength 7
bDescriptorType 5
bEndpointAddress 0x85 (in 0x05)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<interface 2>
<altsetting 0>
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255
bInterfaceSubClass 1
bInterfaceProtocol 1
could not fetch string descriptor: Pipe error
iInterface 9 ()
<endpoint 0>
bLength 7
bDescriptorType 5
bEndpointAddress 0x86 (in 0x06)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<endpoint 1>
bLength 7
bDescriptorType 5
bEndpointAddress 0x05 (out 0x05)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<altsetting 1>
bLength 9
bDescriptorType 4
bInterfaceNumber 2
bAlternateSetting 1
bNumEndpoints 2
bInterfaceClass 7
bInterfaceSubClass 1
bInterfaceProtocol 4
could not fetch string descriptor: Pipe error
iInterface 10 ()
<endpoint 0>
bLength 7
bDescriptorType 5
bEndpointAddress 0x06 (out 0x06)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<endpoint 1>
bLength 7
bDescriptorType 5
bEndpointAddress 0x87 (in 0x07)
bmAttributes 2 (bulk)
wMaxPacketSize 512
bInterval 0 ms
bRefresh 0
bSynchAddress 0
<trying to find out which USB chip is used>
could not claim USB device interface
found possible USB scanner (vendor=0x232b, product=0x2732) at libusb:001:010
<...>
Как видно, вывод на 95% совпадает с lsusb -v. В целом после прочтения части про дескрипторы этот вывод должен быть понятен. Интересна логика определения устройства (точнее будет сказать интерфейса устройства) как сканера. Часть кода [17], в которой это описано:
<...>
/* Some heuristics, which device may be a scanner */
if (dev->descriptor.idVendor == 0) /* hub */
--is_scanner;
if (dev->descriptor.idProduct == 0) /* hub */
--is_scanner;
for (interface_nr = 0; interface_nr < dev->config[0].bNumInterfaces && is_scanner <= 0; interface_nr++)
{
switch (dev->descriptor.bDeviceClass)
{
case USB_CLASS_VENDOR_SPEC:
++is_scanner;
break;
case USB_CLASS_PER_INTERFACE:
if (dev->config[0].interface[interface_nr].num_altsetting == 0 ||
!dev->config[0].interface[interface_nr].altsetting)
break;
switch (dev->config[0].interface[interface_nr].altsetting[0].bInterfaceClass)
{
case USB_CLASS_VENDOR_SPEC:
case USB_CLASS_PER_INTERFACE:
case 16: /* data? */
++is_scanner;
break;
}
break;
}
}
<...>
Отсюда следует, что sane-find-scanner примет устройство за сканер, если:
устройство bDeviceClass=0 и его интерфейс имеют значение bInterfaceClass=255;
один из его интерфейсов или альтернативных конфигураций имеет параметр bInterfaceClass и он соответствует значению 255 (Vendor Specific Class);
bInterfaceClass указывает, что класс для каждого интерфейса определяется отдельно;
или 16.
Хотелось бы получить комментарий разработчика о предназначении «16». Мне не удалось разыскать ни одного применения bInterfaceClass со значением 16. Есть наивное предположение, что проверяется любое (не нулевое) значение bInterfaceClass.
sane-find-scanner vs kyocera
Этот алгоритм не проходят некоторые устройства Kyocera даже при наличии интерфейса bInterfaceClass=255. Яркий пример — Kyocera FS-1025MFP, у которого bDeviceClass=239 не попадает под условия 0 или 255.
lsusb -d 0482:0497 -v
Bus 001 Device 002: ID 0482:0497 Kyocera Corp. FS-1025MFP
Couldn't open device, some information will be missing
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x0482 Kyocera Corp.
idProduct 0x0497
bcdDevice 3.27
iManufacturer 1
iProduct 2
iSerial 3
bNumConfigurations 1
<...>
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 0
bNumEndpoints 2
bInterfaceClass 255 Vendor Specific Class
bInterfaceSubClass 255 Vendor Specific Subclass
bInterfaceProtocol 255 Vendor Specific Protocol
iInterface 5
<...>
Порядок определения одинаков как для libusb-0.1-4, так и для libusb-1.0-0. Также методом перебора [18] выполняется поиск по всем интерфейсам и альтернативным конфигурациям на соответствие их значений, свойственных для устройств с чипом (актуально для планшетных сканеров), которые можно получить из дескрипторов. Пример вывода неудачного результата поиска для планшетного сканера Epson Perfection V39II.
<...>
<trying to find out which USB chip is used>
checking for LM983[1,2,3] ...
this is not a LM983x (bcdUSB = 0x200)
checking for GT-6801 ...
this is not a GT-6801 (bcdUSB = 0x200)
checking for GT-6816 ...
this is not a GT-6816 (bDeviceClass = 255, bInterfaceClass = 255)
checking for GLxxx ...
this is not a GL646 (bDeviceClass = 255, bInterfaceClass = 255)
this is not a GLxxx (bNumEndpoints = 2)
<Couldn't determine the type of the USB chip (result from sane-backends 1.0.32-debian)>
И пример с обнаруженным чипом GT-6816 для планшетного сканера Mustek BearPaw 2448 TA Plus.
<...>
<trying to find out which USB chip is used>
checking for LM983[1,2,3] ...
this is not a LM983x (bNumEndpoints = 2)
checking for GT-6801 ...
this is not a GT-6801 (bDeviceClass = 0)
checking for GT-6816 ...
<This USB chip looks like a GT-6816 (result from sane-backends 1.1.1-debian)>
Фрагмент кода [18], проверяющий НЕсовпадение значений дескрипторов для GT-6816 и получения верного ответа на контрольный запрос.
код
<...>
/* Check for Grandtech GT-6816 */
static char *
check_gt6816 (struct usb_device *dev)
{
char req[64];
usb_dev_handle *handle;
int result;
int i;
if (verbose > 2)
printf (" checking for GT-6816 ...\n");
/* Check device descriptor */
if ((dev->descriptor.bDeviceClass != USB_CLASS_PER_INTERFACE) # false, bInterfaceClass=255 Vendor Specific Class
|| (dev->config[0].interface[0].altsetting[0].bInterfaceClass !=
USB_CLASS_VENDOR_SPEC))
{
if (verbose > 2)
printf
(" this is not a GT-6816 (bDeviceClass = %d, bInterfaceClass = %d)\n",
dev->descriptor.bDeviceClass,
dev->config[0].interface[0].altsetting[0].bInterfaceClass);
return 0;
}
if (dev->descriptor.bcdUSB != 0x110) # false, bcdUSB=1.10
{
if (verbose > 2)
printf (" this is not a GT-6816 (bcdUSB = 0x%x)\n",
dev->descriptor.bcdUSB);
return 0;
}
if (dev->descriptor.bDeviceSubClass != 0x00) # false, bDeviceSubClass=0
{
if (verbose > 2)
printf (" this is not a GT-6816 (bDeviceSubClass = 0x%x)\n",
dev->descriptor.bDeviceSubClass);
return 0;
}
if (dev->descriptor.bDeviceProtocol != 0x00) # false, bDeviceProtocol=0
{
if (verbose > 2)
printf (" this is not a GT-6816 (bDeviceProtocol = 0x%x)\n",
dev->descriptor.bDeviceProtocol);
return 0;
}
..... # ещё несколько проверок
/* Now we send a control message */
memset (req, 0, 64);
req[0] = 0x2e; /* get identification information */
req[1] = 0x01;
result = libusb_control_transfer (handle, 0x40, 0x01, 0x2010, 0x3f40, req, 64, TIMEOUT);
if (result <= 0)
{
if (verbose > 2)
printf (" Couldn't send write control message (%s)\n",
strerror (errno));
return NULL;
}
result = libusb_control_transfer (handle, 0xc0, 0x01, 0x2011, 0x3f00, req, 64, TIMEOUT);
if (result <= 0)
{
if (verbose > 2)
printf (" Couldn't send read control message (%s)\n",
strerror (errno));
return NULL;
}
if (req[0] != 0 || (req[1] != 0x2e && req[1] != 0)) # если устройство правильно ответит на контрольный запрос...
{
if (verbose > 2)
printf (" Unexpected result from control message (%0x/%0x)\n",
req[0], req[1]);
return NULL;
}
return "GT-6801";
}
Для каждого устройства с чипом контрольный запрос уникальный.
Поиск SCSI-устройств в коде выполняется довольно примитивно: по наличию файлов символьных устройств в /dev/* для всевозможных Linux-дистрибутивов [18].
#if defined(__sgi)
"/dev/scsi/sc0d1l0", "/dev/scsi/sc0d2l0",
"/dev/scsi/sc0d3l0", "/dev/scsi/sc0d4l0",
"/dev/scsi/sc0d5l0", "/dev/scsi/sc0d6l0",
...
#elif defined(__linux__)
"/dev/scanner",
"/dev/sg0", "/dev/sg1", "/dev/sg2", "/dev/sg3",
"/dev/sg4", "/dev/sg5", "/dev/sg6", "/dev/sg7",
"/dev/sg8", "/dev/sg9",
"/dev/sga", "/dev/sgb", "/dev/sgc", "/dev/sgd",
"/dev/sge", "/dev/sgf", "/dev/sgg", "/dev/sgh",
"/dev/sgi", "/dev/sgj", "/dev/sgk", "/dev/sgl",
"/dev/sgm", "/dev/sgn", "/dev/sgo", "/dev/sgp",
"/dev/sgq", "/dev/sgr", "/dev/sgs", "/dev/sgt",
"/dev/sgu", "/dev/sgv", "/dev/sgw", "/dev/sgx",
"/dev/sgy", "/dev/sgz",
...
#if defined(__linux__)
"/dev/usb/scanner",
"/dev/usb/scanner0", "/dev/usb/scanner1",
"/dev/usb/scanner2", "/dev/usb/scanner3",
"/dev/usb/scanner4", "/dev/usb/scanner5",
"/dev/usb/scanner5", "/dev/usb/scanner7",
"/dev/usb/scanner8", "/dev/usb/scanner9",
...
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
"/dev/uscanner",
"/dev/uscanner0", "/dev/uscanner1",
"/dev/uscanner2", "/dev/uscanner3",
"/dev/uscanner4", "/dev/uscanner5",
...
Помимо успешного обнаружения планшетного сканера на чипе, необходимо также иметь специальный файл прошивки для него. Иначе попытка сканирования завершится ошибкой:
[gt68xx] Couldn't open firmware file (`/usr/share/sane/gt68xx/A2Nfw.usb'): No such file or directory
scanimage: open of device gt68xx:libusb:002:005 failed: Invalid argument
Все доступные файлы прошивок можно найти на странице бэкенда GT68xx [19]. Пошаговая инструкция [20] по настройке Mustek BearPaw 2448 TA Plus и установке файла прошивки есть в базе знаний Astra Linux.
Важный момент для успешного отображения объективной информации о результатах поиска устройства. Так как sane-find-scanner считывает данные из символьного файла устройства в /dev/bus/usb/00Х/00Х, то у пользователя, выполняющего запуск, должны быть соответствующие права на чтение и запись (rw) этого файла.
ls -lha /dev/bus/usb/004/002
crw-rw-r-- 1 root lp 189, 385 мар 22 10:42 /dev/bus/usb/004/002
Необходимые атрибуты для пользователя назначаются udev-правилом, которое входит в состав пакета драйвера. Правила могут не отработать или отсутствовать. Это свойственно для старых версий драйверов. Именно поэтому до установки драйвера и даже после запуск sane-find-scanner рекомендуется выполнять с правами суперпользователя.
Но и это ещё не всё. Для ALSE в режиме защищенности «Смоленск» возможны ситуации, когда на файл символьного устройства может быть установлена высокая целостность. В этом случае уникальные атрибуты покажет pdp-ls.
pdp-ls -Mal /dev/bus/usb/001/004
crw-rw-rw-+--- 1 root lp Уровень_0:Высокий:Нет:0x0 /dev/bus/usb/001/004
Бэкенд
Проект SANE [21] представляет собой полноценный API для доступа к устройствам при помощи бэкендов (backends, драйверы). Не только к сканерам, но и к видеофотокамерам. В ALSE(Fly) уже предустановлены пакеты libsane1, libsane-common, в которых содержатся основные [22] комплекты (.conf и .so файлы) бэкендов. Список совместимых устройств довольно большой, но для некоторых моделей существует поддержка только со стороны вендора. Например Kyocera, Pantum, Plustek и др. Именно поэтому для них нужно ставить свои пакеты. Бэкенд может содержать в себе поддержку как локально подключенных устройств, так и сетевых. Раннее было сказано, что наличие в выводе sane-find-scanner совместимого устройства не гарантирует, что устройство будет сканировать в ОС. В случае обнаружения устройства бэкендом шансы выполнить успешное сканирование увеличиваются до 90%.
Как известно, всякий драйвер является модулем, но не всякий модуль является драйвером. Применительно к SANE в большинстве случаев драйвером будет являться библиотека libusb, которая взаимодействует по SANE API с драйвером к устройству, представленному в виде библиотеки .so. Модуль ядра scanner более не используется. libusb работает в пространстве пользователя. Да, производительность не такая высокая по сравнению с модулем ядра за счет дополнительных прослоек libusb API и SANE API, но ошибка сегментирования при поиске устройства не будет приводить к вынужденной перезагрузке компьютера. Бэкенды всегда работают с bInterfaceClass=255 (Vendor Specific Interface).
Главное отличие типового бэкенда из состава SANE от привычного драйвера в OC Windows — отсутствие графического приложения для настроек сканирования. Преимуществом такого ограничения является высокая производительность и малый размер, что может быть критично для тонких клиентов. Еще одно преимущество — отсутствие многочисленных зависимостей, необходимых для работы графических приложений.
Образцовый драйвер представляет собой deb-пакет и включает:
конфигурационные (.conf) файлы бэкенда;
файлы (.rules) с udev-правилами;
основные и вспомогательные (.so) библиотеки;
приложение (графическое или консольное), опционально.
Рассмотрим каждый компонент на примере deb-пакета pantum_1.1.108-1astra1_amd64.deb.
tree -F
.
├── etc/
│ ├── sane.d/
│ │ ├── dll.d/
│ │ │ ├── pantum6500
│ │ │ └── pantum_mfp
│ │ ├── pantum6500.conf
│ │ └── pantum_mfp.conf
│ └── udev/
│ └── rules.d/
│ └── 60-pantum_mfp.rules
└── usr/
├── lib/
│ ├── cups/
│ │ └── filter/
│ │ ├── pt2800Filter*
│ │ ├── ptm6700Filter*
│ │ └── ptps*
│ └── x86_64-linux-gnu/
│ └── sane/
│ ├── libsane-pantum6500.so -> libsane-pantum6500.so.1.0.24
│ ├── libsane-pantum6500.so.1 -> libsane-pantum6500.so.1.0.24
│ ├── libsane-pantum6500.so.1.0.24
│ ├── libsane-pantum_mfp.so -> libsane-pantum_mfp.so.1.0.24
│ ├── libsane-pantum_mfp.so.1 -> libsane-pantum_mfp.so.1.0.24
│ └── libsane-pantum_mfp.so.1.0.24
├── local/
│ ├── etc/
│ │ └── sane.d/
│ │ ├── dll.d/
│ │ │ ├── pantum6500
│ │ │ └── pantum_mfp
│ │ ├── pantum6500.conf
│ │ └── pantum_mfp.conf
│ └── lib/
│ └── sane/
│ ├── libsane-pantum6500.so -> libsane-pantum6500.so.1.0.24
│ ├── libsane-pantum6500.so.1 -> libsane-pantum6500.so.1.0.24
│ ├── libsane-pantum6500.so.1.0.24
│ ├── libsane-pantum_mfp.so -> libsane-pantum_mfp.so.1.0.24
│ ├── libsane-pantum_mfp.so.1 -> libsane-pantum_mfp.so.1.0.24
│ └── libsane-pantum_mfp.so.1.0.24
└── share/
├── cups/
│ └── model/
│ └── Pantum/
│ ├── Pantum BM5100ADN Series PS.ppd
│ ├── Pantum BM5100ADW Series PS.ppd
<...>
│ └── Pantum P3308DN Series PS.ppd
└── doc/
└── pantum/
├── changelog.gz
└── copyright
23 directories, 75 files
Конфигурационные файлы
Конфигурационный файл представляет собой список idVendor и idProduct всех поддерживаемых устройств в особом формате. Наряду с устройствами для локального подключения в нём могут присутствовать строки с использованием сетевого бэкенда для поиска устройств по локальной сети. Например, драйвер pantum6500 будет выполнять поиск сетевых устройств через tcp M6500 9200.
pantum6500.conf
<...>
usb 0x232b 0xFF52 M6518NW
usb 0x232b 0xFF53 M7170DW
tcp M6500 9200
usb 0x232b 0xa421 M6500-Series
usb 0x232b 0xa422 M6500N-Series
<...>
Традиционно конфигурационный файл (или несколько) размещается в /etc/sane.d/. Название файла соответствует названию бэкенда. Помимо списка поддерживаемых моделей, .conf-файлы могут быть предназначены для настройки бэкенда как в части поиска устройства, так и в части конфигурирования самого сканирования. Например, у Kyocera «из коробки» к четырем конфигурационным файлам со списками поддерживаемых моделей, разделенных на разные типы устройств (планшетные, МФУ, формата А3, корпоративные), прилагается ещё один — kyocera_devices.conf. В нем задаются настройки поиска сетевых устройств. Каждому конфигурационному файлу соответствует отдельный файл библиотеки .so, с которой работает бэкенд. Кроме kyocera_devices.conf, это не бэкенд.
kyocera.conf
kyocera_gdi_a3.conf
kyocera_wc3.conf
kyocera_wc3_usb.conf
Не передать словами, как приятно, когда в .conf-файлах разработчики оставляют комментарии с описанием назначения конфигурационного файла и описания всех параметров. Samsung (HP) считает, что в /etc/sane.d/smfp-samsung.conf все очевидно:
<model vendor="samsung" id="scx6x20" modelstring="SCX-6x20 Series" type="usb">
<hwoption name="twainspec" type="enum">3</hwoption>
<hwoption name="sleep_after_scan_ms" type="int">0</hwoption>
<hwoption name="adf" type="enum">duplex</hwoption>
<hwoption name="flatbed" type="enum">yes</hwoption>
<...>
и вот для сравнения /etc/sane.d/kyocera_devices.conf:
# To add a network device, input the IP ADDRESS or HOSTNAME of the device and DEVICE NAME with a space in between.
# Format:
# <IP_ADDRESS or HOSTNAME> <DEVICE_NAME>
# Example:
# KM9175E4 FS-1135MFP
# 10.191.20.77 TASKalfa 7550ci
# ------------- Add devices here -------------
# ------- List of supported devices ----------
# FS-C2026MFP+
# FS-C2126MFP+
Как узнать, поддерживается ли устройство бэкендом, не устанавливая сам пакет? Всё просто. Необходимо проверить наличие строк с упоминанием idVendor и idProduct в конфигурационных файлах .conf к бэкенду. Помимо основного конфигурационного файла, можно найти уникальные идентификаторы в иных расположениях. Например, Unified Linux Driver для Xerox WorkCentre 3025 содержит список поддерживаемых idVendor и idProduct устройств в файле /uld/noarch/oem.conf пакета. 42da — нужный PID.
VENDOR=Xerox
VID=0924
PIDS="4268 4293 4294 3cf7 3cf8 4295 420c 4237 420f 42da 42db 42dc"
В то же время эта модель отсутствует в /etc/sane.d/smfp.conf.
<?xml version="1.0" ?>
<smfpconfig>
<option name="network" type="enum">yes</option>
<model vendor="xerox" id="wc3210" modelstring="WorkCentre 3210"/>
<model vendor="xerox" id="wc3220" modelstring="WorkCentre 3220"/>
<model vendor="xerox" id="pe120" modelstring="WorkCentre PE120 Series" type="usb">
<...>
Доступ из РФ
На официальной странице Xerox (доступ к ресурсу из РФ только через VPN) Linux-драйвер для Xerox WorkCentre 3025 отсутствует. Его можно увидеть, выбрав Country>United States.
Заглядывать в deb-пакет и изучать его содержимое — нормальная практика. В некоторых случаях слепая установка может повредить ОС, заменяя системные библиотеки на более старые версии, и сломать пакетную базу. Часто администратор системы, цепляясь за любую ниточку в поисках нужного драйвера, устанавливает первый попавшийся пакет с официального сайта производителя, напротив которого значится Linux. Например, драйвер A3F2400N_050D_1.0.23-1_amd64.deb [23], который заботливо заменит пакеты sane и sane-utils версиями 2017 года. Уверяю, по-прежнему неработающий сканер после этой установки — последнее, что будет его волновать. Даже инструкция по установке, включающая пункты:
sudo apt-get remove sane-backends
sudo dpkg --force-overwrite -i A3F2400N_050D_1.0.23-1_amd64.deb
должна напомнить как минимум о наличии резервной копии ОС перед её выполнением.
И всегда найдется deb-пакет, в котором всё будет «по‑своему». Разумеется, описания в файле README внутри не будет. Если для Katusha M247 [24] вендор собрал пакет и выложил в публичный доступ в приемлемом виде, то у Katusha M130 [25] творится сущий бардак: deb-пакет даже распаковать не удастся.
unzip Linux_m130.zip
Archive: Linux_m130.zip
inflating: Linux/M130_P130_drivers_Sep_09_2022_(Win_Linux).zip
error: invalid zip file with overlapped components (possible zip bomb)
А если и удастся, внутри ожидает совершенно другая организация файловой структуры.
tree KatushaM130
.
├── etc
│ ├── sane.d
│ │ └── DeviceList_katusha_m130.conf
│ └── udev
│ └── rules.d
│ └── 99-katusha_m130_sane.rules
├── opt
│ └── apps
│ └── printer-driver-katusha-m130
│ └── sane
│ ├── katusha_m130
│ │ ├── libjpeg-turbo.so
│ │ ├── libSmartImage.so_x64_1.0_katusha_m130
│ │ ├── libSmartImage.so_x64_katusha_m130
│ │ ├── M130_x64.so
│ │ ├── M130_x64.so.1.0
│ │ └── SmartImage.ini
│ ├── libsane-katusha_m130.so.1.0.22
│ └── libsane-katusha_m130.so.1.0.22_1.0
└── usr
├── lib
│ └── cups
│ └── filter
│ └── Katusha
│ └── Katusha_M130
└── share
└── cups
└── model
└── Katusha
└── M130.ppd
18 directories, 12 files
Имя бэкенда katusha_m130 (так он будет записан в /etc/sane.d/dll.conf), а имя конфигурационного файла со списком поддерживаемых устройств — DeviceList_katusha_m130.conf, его не сразу найдешь в /etc/sane.d/. Настройки спрятаны аж в /opt/apps/printer-driver-katusha-m130/sane/katusha_m130/SmartImage.ini. И как в случае с Samsung, в нем всё очевидно:
; Priority: [########(8 digits HEX number of PIDVID)] > {if ((ModelSeries) exists) ? goto [(ModelSeries)] : continue} > [SmartImage]
; Common settings for all models
[SmartImage]
BackgroundLumDiffStep=3
BackgroundLumVDiffStep=3
BackgroundChrDiffStep=0
BlackBackgroundLumDiffStep=6
BlackBackgroundLumVDiffStep=12
ExtraAdjCrop_Top=-6
ExtraAdjCrop_Left=-6
; The right/bottom side should be smaller than left.
ExtraAdjCrop_Right=-4
ExtraAdjCrop_Bottom=-4
; Separate image width into 8 regions (A-H). Bit control, unsigned char type.
; Region A = 1, Region B = 2, Region C = 4, Region D = 8, Region E = 16, Region F = 32, Region G = 64, Region H = 128
; ------------------------W-------------------------
; | | | | | | | | |
; | A=1 | B=2 | C=4 | D=8 | E=16 | F=32| G=64|H=128|
; H | | | | | | | H
; | | | | Image | | | |
; | | | | | | | | |
; ------------------------W-------------------------
; Ex.
; Bad pixels are located at front side 58, 1130, 1469 with image width = 1728.
; Region Size = 1728 / 8 = 216
; 1st bad pixel: Find region = 58 / (Region Size) = 58 / 216 = 0 (Region A)
; 2nd bad pixel: Find region = 1130 / (Region Size) = 1130 / 216 = 5.23 ~= 5 (Region F)
; 3rd bad pixel: Find region = 1469 / (Region Size) = 1469 / 216 = 6.80 ~= 6 (Region G)
; Set value: BadPixelsSetRegionThr_F = 1 + 32 + 64 = 97;
BadPixelsSetRegionThr_F=0
BadPixelsSetRegionThr_R=0
; -----------------------------------------------------------------------------
Впрочем, это не мешает драйверу успешно инициализироваться в системе.
Разработчики из Panasonic в драйвере libsane-panakvs-1.7.0-x86_64 [26] применили иную схему использования конфигурационных файлов:
tree config
config/
├── kvn1058.conf
├── kvs1037.conf
├── kvs1037x.conf
├── kvs1058.conf
├── kvs5076.conf
├── kvs5078.conf
├── kvs6120.conf
├── kvs8147.conf
├── kvsl3066.conf
└── panakvs.conf
В основном файле panakvs.conf перечислены idVendor и idProduct поддерживаемых устройств, а также имя конфигурационного файла для его детальной настройки.
#Panasonic document scanner driver.
#2021/02/25.
DBG_LEVEL 0
#DBG_LEVEL 128
#KV-S8147
usb 0x04da 0x0fa0
conf kvs8147.conf
#KV-S8127
usb 0x04da 0x0faa
conf kvs8147.conf
<...>
Здесь же можно увеличить уровень логирования DBG_LEVEL. Вот только не указано, по какому пути будет храниться эта информация.
Идеально будет разместить строку с упоминанием бэкенда в одноименный файл в директории для проприетарных драйверов /etc/sane.d/dll.d/ . Открытые бэкенды в одной локации, проприетарные — в другой. Но не все производители придерживаются этого правила и просто добавляют запись к стандартным бэкендам в /etc/sane.d/dll.conf. SANE смотрит в обе директории и считывает сперва /etc/sane.d/dll.d/, затем — /etc/sane.d/dll.conf. Все бэкенды, перечисленные в этих файлах, будут вызваны при запуске поиска устройств из фронтенда. Далее буду приводить в пример консольный вариант фронтенда - scanimage (sane-utils). Надо учитывать тот факт, что каждый конфигурационный файл бэкенда содержит с десяток, а то и с сотню записей о idVendor и idProduct поддерживаемых устройств. Более того, каждый бэкенд может содержать ещё и поддержку поиска сетевых устройств. В связи с этим простой запуск графического фронтенда в некоторых окружениях может затянутся до неприличных 15 минут только из-за того, что под капотом выполняется поиск устройств всеми возможными способами. В Astra Linux Special Edition 1.7.6 в отладочную информацию добавлены события о затраченном на операцию sane_get_devices времени. Это можно увидеть, выполнив поиск устройств с переменной окружения SANE_DEBUG_DLL=3 или уровнем 4.
SANE_DEBUG_DLL=3 scanimage -L
<...>
[22:27:29.788118] [dll] init: backend `pantum6500' initialization completed in 686 ms
<...>
[22:27:36.845015] [dll] init: backend `airscan' initialization completed in 4 ms
<...>
[23:12:39.078785] [dll] sane_get_devices: found 2 devices in 9437 ms
<...>
Различные дистрибутивы, отсутствие единого стандарта размещения файлов и наличие множества параметров сборки sane приводит к тому, что каждый производитель выкручивается как может. В данном примере с pantum:
конфигурационные файлы /etc/sane.d/ продублированы в /local/etc/sane.d;
файлы библиотек /usr/lib/x86_64-linux-gnu/sane продублированы в /usr/local/lib/sane.
Яркий пример
Kodak, например, с бэкендом kds_s2000 решил проблему изящнее. Пакет [28] содержит установочный скрипт setup на ~2500 строк, который предусматривает возможность установки на дюжину (не вру, их 12) дистрибутивов. Да что говорить, там даже есть eklog.sh, который собирает диагностическую информацию о работе сканера. Моё почтение.
Между тем установка пакета на ALSE генерирует некорректное udev-правило 55-kodakdi.rules, из-за которого в ALSE могут возникнуть проблемы с доступом к устройству из фронтендов.
ATTRS{idProduct}=="1026", ATTRS{idVendor}=="29cc", MODE:="666", OWNER="xxx", GROUP="users", RUN+="/opt/kodak/kds_s2000/pnphelper action:add"
Правило это создает скрипт postinst в deb-пакете.
################################################################################
# Put scanner info into the manufacturer rules file in /etc/udev/rules.d
################################################################################
rulesfile="/etc/udev/rules.d/55-${mfgdir}di.rules"
echo " configuring $rulesfile"
# if the file doesn't exist, then create it
if [ ! -f "$rulesfile" ]; then
echo "# This file contains rules to make the scanner device node" >> $rulesfile
echo "# readable and writable by the owner and members of the users group" >> $rulesfile
echo "# If you want to restrict or relax these permission you should do so" >> $rulesfile
echo "# by changing the mode and group below, as neccessary." >> $rulesfile
echo >> $rulesfile
fi
# list of lines for the rule file.
RULES_LINE[0]="# KODAK S2040 Scanner"
RULES_LINE[1]="ATTRS{idProduct}==\"1026\", ATTRS{idVendor}==\"29cc\", MODE:=\"666\", OWNER=\"xxx\", GROUP=\"users\", RUN+=\"/opt/kodak/kds_s2000/pnphelper action:add\""
<...>
Да-да, вы не ослышались:
If you want to restrict or relax these permission you should do so by changing the mode and group below, as neccessary.
Скажите честно, кто заглядывает в postinst перед установкой пакета драйвера к устройству?
Подобные проблемы удается отследить только после просмотра отладочной информации udevadm (systemd).
Правила udev
Udev-правила в составе пакета предназначены для:
назначения (изменения) прав доступа в привычном формате атрибута, владельца, группы, всех остальных (MODE="") для файла символьного устройства;
запуска программы или скрипта до создания имени устройства (PROGRAM="");
запуска специализированного скрипта или служебной программы после создания имени устройства (RUN="").
Программы запускаются самые разные, начиная от echo параметра в файл для отключения autosuspend устройства, заканчивая setfacl для назначения ACL файлу устройства. Например, правила HP, помимо назначения прав на устройство, выполняют также установку переменной hp_test, уведомление в системный журнал о событии подключения устройства и следом - выполнение программы hp-config_usb_printer.
# This rule will check the smart install feature, plugin status and firmware download for the required printers.
ENV{hp_test}=="yes", PROGRAM="/bin/sh -c 'logger -p user.info loading HP Device env{DEVNUM}'"
В некоторых пакетах драйверов можно встретить довольно сложные конструкции, которые даже учитывают версию запущенного ядра для выполнения на этот случай скриптов.
Чтобы правила отработали, после успешной установки драйвера следует переподключить устройство или выполнить команду sudo udevadm trigger. По-прежнему не во всех deb-пакетах postinst содержит выполнение этой команды.
Хорошим тоном в файлах .rules считается использование комментариев с указанием модели, а также идентификация устройства по сочетанию idVendor и idProduct. Например:
# Katusha M247
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="3197", ATTRS{idProduct}=="1102", MODE="0666", OWNER="root", GROUP="lp", ENV{libsane_matched}="yes"
Пожалуй, самый неудачный пример udev-правил демонстрирует kyocera (о других проблемах пакетов kyocera-sane, более существенных, упомянем в части «Отсутствие драйвера»). Все пакеты, начиная с самой первой версии kyocera-sane_1.1.0228_amd64, включают в себя установку излишних привилегий 0666 для всех устройств по фильтру SUBSYSTEM=="usb_device" правилом 40-scanner-permissions.rules. Всё бы ничего, но использование MODE:="0666" запрещает следующим udev-правилам менять "0666".
# usb scanner
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE:="0666"
SUBSYSTEM=="usb_device",MODE:="0666"
Это может сыграть с администратором злую шутку, в случае если есть необходимость установить ограничения доступа к устройству. При наличии на рабочем месте пакетов от других вендоров их udev-правила могут просто не отработать.
Помимо обычных атрибутов доступа, udev-правила также используются в ALSE для назначения мандатных атрибутов к символьному файлу устройства для разграничения доступа по уровню конфиденциальности (УК). Подробное описание есть в статье Мандатное управление доступом и мандатный контроль целостности [27] справочного центра Astra Linux. Управления правилами выполняется через fly-admin-smc>Устройства и правила>Устройства. Регистрация нового устройства приводит к созданию /etc/udev/rules.d/99zz_PDAC_LOCAL_*.udev правила:
Важно!
Применимо для Astra Linux Special Edition 1.7.6
#Parsec DevAC udev rule 4 device "not descripted"
ENV{PRODUCT}=="232b/2732/100", OWNER="root", GROUP="scan", MODE="660", PDPL="1:0:0x0:0x0!", AUDIT="o:0x0:0x0", GOTO="BLOCK_DEV"
GOTO="END"
LABEL="BLOCK_DEV"
SUBSYSTEM=="block", ENV{ID_FS_TYPE}=="?*", SYMLINK+="%k_$env{ID_FS_TYPE}", RUN+="/bin/ln -f /dev/%k /dev/%k_$env{ID_FS_TYPE}"
LABEL="END"
Параметр PDPL="1:0:0x0:0x0!" устанавливает атрибуты для доступа к устройству (ENV{PRODUCT}=="232b/2732/100") пользователям в группе (GROUP="scan") первого УК. Служба udevd в режиме отладки отразит это в событиях строками
systemd-udevd[6974]: Reading rules file: /etc/udev/rules.d/99zz_PDAC_LOCAL_pantum.rules
systemd-udevd[6974]: TK_A_PDPL found, value '1:0:0x0:0x0!'
Библиотеки
В состав каждого пакета включены библиотеки для работы бэкенда. Обычно это файлы с расширением .so. Именно они являются драйверами в привычном понимании. Основные проблемы связаны с установкой файлов в директории, которые не считывает sane, или недействительными символьными ссылками на них. В ALSE, libsane1 собран с использованием поиска в трех директориях через переменную --libdir=/usr/lib/x86_64-linux-gnu/sane:/usr/lib/sane:/usr/lib64/sane. А /usr/lib64/sane еще и является ссылкой на /usr/lib/x86_64-linux-gnu/sane.
Бэкенд, работающий по SANE API, не создает файлы сканированного изображения самостоятельно. Он передает несжатые данные в память, а за дальнейшую обработку изображения отвечает приложение, инициирующее процесс сканирования. Итоговую конвертацию в нужный формат выполняют программы, как раз использующие эти библиотеки либо в составе ОС, либо из пакета драйвера.
Пакет может содержать свои собственные протестированные версии общих библиотек, например libjpeg8, которые могут быть перезаписаны без предупреждения при установке.
При работе с устройством в ALSE в режиме замкнутой программной среды (ЗПС) [29] все файлы .so будут подписаны ключом.
bsign: processing /home/u/deb_sign/tmp28634/data/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1.0.24
bsign: processing /home/u/deb_sign/tmp28634/data/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1.0.24
version: 1
id: bsign v1.0
hash: {GOST R34.11-2012} 7141...8be4
signature_size: 119
signature:
...
01 18 2d 76 b5 6d f5 fe c3 53 fb 90 76 4f 7a e7
26 ba 5e c1 13 b3 2d fe 2f c9 a6 ef a5 f1 93 1e
...
signer: 182FDD8B2C01DFEA
timestamp: 07 May 2024 09:56:33 (1715064993)
bsign: good hash found in '/home/u/deb_sign/tmp28634/data/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1.0.24'
version: 1
id: bsign v1.0
xattr hash: {GOST R34.11-2012} 543e...49dd
xattr signature_size: 119
xattr signature:
...
a2 e3 b6 5b f7 75 fe 61 72 a4 f7 00 4a 33 55 17
89 4b b7 a6 c8 6c 43 d2 a5 f1 93 1e 76 b5 6d d2
signer: 182FDD8B2C01DFEA
timestamp: 07 May 2024 09:56:33 (1715064993)
bsign: good xattr hash found in '/home/u/deb_sign/tmp28634/data/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1.0.24'
bsign: no externalSign hash found in '/home/u/deb_sign/tmp28634/data/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1.04'
При попытке сканирования вызов неподписанных файлов (или при отсутствии закрытого ключа) будет сопровождаться событиями от DIGSIG в dmesg. Например, поиск устройств panasonic с включенным режимом ЗПС.
[ 558.405742] DIGSIG:[ERROR] NOT SIGNED: path=/usr/lib/x86_64-linux-gnu/sane/libsane-panakvs.so.1.0.22 uid=0 gid=0
[ 558.405996] DIGSIG:[ERROR] NOT SIGNED: path=/usr/lib/x86_64-linux-gnu/sane/libsane-kvs7097.so.1.0.22 uid=0 gid=0
[ 558.406164] DIGSIG:[ERROR] NOT SIGNED: path=/usr/lib/x86_64-linux-gnu/sane/libsane-kvs2087.so.1.0.22 uid=0 gid=0
[ 558.406341] DIGSIG:[ERROR] NOT SIGNED: path=/usr/lib/x86_64-linux-gnu/sane/libsane-kvs1057.so.1.0.22 uid=0 gid=0
[ 558.407606] DIGSIG:[ERROR] NOT SIGNED: path=/usr/lib/x86_64-linux-gnu/sane/libsane-kvs1026.so.1.0.22 uid=0 gid=0
[ 558.407837] DIGSIG:[ERROR] NOT SIGNED: path=/usr/lib/x86_64-linux-gnu/sane/libsane-kvs10_series.so.1.0.22 uid=0 gid=0
Или наличием ошибок "failed to map segment from shared object" в отладочной информации.
SANE_DEBUG_DLL=255 scanimage -L
[23:25:35.174017] [dll] load: searching backend `pantum6500' in `/usr/lib/x86_64-linux-gnu/sane:/usr/lib/sane:/usr/lib64/sane'
[23:25:35.174226] [dll] load: trying to load `/usr/lib/x86_64-linux-gnu/sane/libsane-pantum6500.so.1'
[23:25:35.174581] [dll] load: dlopen()ing `/usr/lib/x86_64-linux-gnu/sane/libsane-pantum6500.so.1'
[23:25:35.176568] [dll] load: dlopen() failed (/usr/lib/x86_64-linux-gnu/sane/libsane-pantum6500.so.1: failed to map segment from shared object)
[23:25:35.176688] [dll] load: searching backend `pantum_mfp' in `/usr/lib/x86_64-linux-gnu/sane:/usr/lib/sane:/usr/lib64/sane'
[23:25:35.176796] [dll] load: trying to load `/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1'
[23:25:35.177108] [dll] load: dlopen()ing `/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1'
[23:25:35.178770] [dll] load: dlopen() failed (/usr/lib/x86_64-linux-gnu/sane/libsane-pantum_mfp.so.1: failed to map segment from shared object)
Стандартные бэкенды в составе libsane1 в ALSE уже имеют подпись и дополнительных действий не требуют.
Приложение
Некоторые производители могут использовать свои собственные приложения для сканирования (фронтенды). Программы содержат удобное представление настроек сканирования, задействовавших весь функционал драйвера. Также в пакете может присутствовать дополнительное ПО для работы с устройством. Например, демон (фоновая служба) Scan Button monitoring tool [30] от Fujitsu (Ricoh), отслеживающая физическое нажатие кнопки сканирования на устройстве. Под капотом службы — выполнение scanimage с настройками из конфигурационного файла /opt/pfusp/etc/pfuspscanbutton.conf.
Другим примером может послужить графическое приложение Image Scan! for Linux (iscan), устанавливаемое из пакета драйвера к устройствам Epson. Сопровождается неплохим руководством [48].
На этом первая часть подходит к концу. Мы научились с высокой точностью идентифицировать сканирующее устройство в системе и определять все аппаратные возможности из дескрипторов. Заглянули в пакет драйвера и, выполнив установку, готовы наконец сканировать. Или нет?
Об этом поговорим в продолжении «Разбираемся со сканерами в Linux: Установка и конфигурирование устройства».
Приложение А. Схема взаимодействия сканирующего устройства с пользовательским пространством
Схематичное представление взаимодействия сканирующих устройств с пользовательским окружением на одном изображении. В примере описаны устройства, упоминаемые в тексте Pantum BM5100ADW и Mustek BearPaw 2448 TA Plus.
*в схеме отмечены только активные интерфейсы и их конечные точки
**для устройства отсутствует отдельный пакет, udev-правила входят в состав libsane1
Приложение B. Список использованных источников
Скрытый текст
[1] Код hub.c (https://github.com/torvalds/linux/blob/c85af715cac0a951eea97393378e84bb49384734/drivers/usb/core/hub.c)
[2] USB string order in dmesg (https://bugzilla.kernel.org/show_bug.cgi?id=218762)
[3] usb_register_driver (https://github.com/torvalds/linux/blob/026e680b0a08a62b1d948e5a8ca78700bfac0e6e/drivers/usb/core/driver.c#L1034)
[4] sane dll.aliases (http://www.sane-project.org/man/sane-dll.5.html)
[5] Настройка механизмов защиты и блокировок (https://wiki.astralinux.ru/x/LoKhAQ)
[6] 9.5 Descriptors (http://sdpha2.ucsd.edu/Lab_Equip_Manuals/usb_20.pdf)
[7] IPP-over-USB (https://github.com/OpenPrinting/ipp-usb)
[8] Поддержка USB в KolibriOS: что внутри? Часть 5: уровень логического устройства (https://habr.com/ru/companies/kolibrios/articles/200172/)
[9] 9.6.2 Device_Qualifier (http://sdpha2.ucsd.edu/Lab_Equip_Manuals/usb_20.pdf)
[10] Defined Class Codes (https://www.usb.org/defined-class-codes)
[11] usb-devices (https://github.com/gregkh/usbutils/blob/153d41d2d1c05f783918a0a837f4f255c92b8e3a/usb-devices#L154)
[12] usbfs filesystem output (https://www.kernel.org/doc/Documentation/usb/proc_usb_info.txt)
[13] How does USB device discovery work? (https://www.youtube.com/watch?v=N0O5Uwc3C0o&ab_channel=BenEater)
[14] usbmon (https://www.kernel.org/doc/Documentation/usb/usbmon.txt)
[15] 5.7 Interrupt Transfers (http://sdpha2.ucsd.edu/Lab_Equip_Manuals/usb_20.pdf)
[16] USB Error codes (https://www.kernel.org/doc/html/latest/driver-api/usb/error-codes.html)
[17] sane-find-scanner.c (https://gitlab.com/sane-project/backends/-/blob/master/tools/sane-find-scanner.c?ref_type=heads)
[18] check-usb-chip.c (https://gitlab.com/sane-project/backends/-/blob/master/tools/check-usb-chip.c?ref_type=heads)
[19] SANE GT68xx Backend Homepage (http://www.meier-geinitz.de/sane/gt68xx-backend/)
[20] Mustek BearPaw 2448TA Plus (https://wiki.astralinux.ru/display/sdkb/Mustek+BearPaw+2448TA+Plus)
[21] SANE - Introduction (https://www.sane-project.org/intro.html)
[22] SANE: Backends (Drivers) (https://www.sane-project.org/sane-backends.html)
[23] Mustek Driver & Manual Downloads (https://www.mustek.com.tw/)
[24] Katusha M247 Driver (https://katusha-it.ru/storage/filemanager/downloads/Katusha%20Devices/m247/Linux%20%D0%B4%D1%80%D0%B0%D0%B8%CC%86%D0%B2%D0%B5%D1%80%20%D1%81%D0%BA%D0%B0%D0%BD%D0%B8%D1
%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F%20%D0%9A%D0%B0%D1%82%D1%8E%D1%88%D0%B0%20M247.zip)
[25] Katusha M130 Driver (https://katusha-it.ru/storage/filemanager/downloads/Katusha%20Devices/m130/Linux_m130.zip)
[26] Panasonic Driver (https://docs.connect.panasonic.com/pcc/support/scanner/attention.html?https://www.psn-web.net/cs-im/Japan/Scanner/cojp/data_cmns/linux/libsane-panakvs-1.7.0-x86_64.tar.gz)
[27] Мандатное управление доступом и мандатный контроль целостности (https://wiki.astralinux.ru/x/sgImCQ)
[28] Kodak s2000 Driver(https://resources.kodakalaris.com/docimaging/drivers/Kodak_branded_drivers/s2000/LinuxSoftware_s2000_v8.2.x86_64.deb.tar.gz)
[29] Astra Linux: Режим замкнутой программной среды (https://wiki.astralinux.ru/x/6oR0Ag)
[30] Image Scanner Driver for Linux. Scan Button Monitoring Tool (https://origin.pfultd.com/downloads/IMAGE/fi/ubuntu/250/P2U3-0200-04ENZ0.pdf)
[31] Списки управления доступом к файловым объектам (ACL) в Astra Linux (https://wiki.astralinux.ru/x/gR4zC)
[32] Динамическое управление устройствами с помощью udev (https://www.opennet.ru/base/sys/udev_dynamic.txt.html)
[33] The problem of scanning using USB multi-function printers in Linux (continued) (https://fitzcarraldoblog.wordpress.com/2015/07/24/the-problem-of-scanning-using-usb-multi-function-printers-in-linux-continued-2/)
[34] The Samsung Unified Linux Driver Repository (https://www.bchemnet.com/suldr/)
[35] Kodak S2040 Series Driver (https://resources.kodakalaris.com/docimaging/drivers/Kodak_branded_drivers/s2000/LinuxSoftware_s2000_v8.2.x86_64.deb.tar.gz)
[36] SANE backend for AirScan (eSCL) and WSD document scanners (https://github.com/alexpevzner/sane-airscan)
[37] SANE backend for Avision branded and Avision OEM (HP, Minolta, Mitsubishi, UMAX and possibly more) flatbed and film scanners (https://www.sane-project.org/man/sane-avision.5.html)
[38] SANE backend for Canon Multi-Function Printers and CanoScan Scanners (https://www.sane-project.org/man/sane-pixma.5.html)
[39] Missing line/alias in pixma_imageclass.c for imageRUNNER 1133 (https://gitlab.com/sane-project/backends/-/issues/519)
[40] sane-test - SANE backend for testing frontends (https://www.sane-project.org/man/sane-test.5.html)
[41] libusb Documentation, Claim an interface on a given device handle (https://libusb.sourceforge.io/api-1.0/group__libusb__dev.html#gaee5076addf5de77c7962138397fd5b1a)
[42] ipp-usb -- HTTP reverse proxy, backed by IPP-over-USB connection to device (https://github.com/OpenPrinting/ipp-usb)
[43] sane-net - SANE network backend (https://www.sane-project.org/man/sane-net.5.html)
[44] Saned. Подключение удаленного сканера (https://wiki.astralinux.ru/x/dx4mE)
[45] Совместимое оборудование (https://astralinux.ru/ready-for-astra/compatible-hardware/)
[46] document udevadm info output prefixes (https://github.com/systemd/systemd/pull/19730/files/f62db51eb253af711e74a2d9fd3b680096c6ab74)
[47] Как делается OpenSource: личный опыт (https://habr.com/ru/articles/751214/)
[48] Image Scan! for Linux Manual (https://download.ebz.epson.net/man/linux/iscan_e.html)