1. Получение информации об устройстве и поиск подходящего драйвера
    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. Библиотеки

  2. Приложение А

  3. Приложение В

Перед вами исчерпывающее руководство по идентификации, установке и настройке сканирующих устройств в операционной системе (ОС) Linux. Описанные приемы и рекомендации применимы ко всем популярным дистрибутивам лишь с незначительными отличиями в виде различных пакетных менеджеров и используемых директорий с конфигурационными файлами. За время работы с клиентскими обращениями нашей команде удалось накопить огромный опыт по методам диагностики различных бэкендов всех имеющихся на рынке вендоров. Попытаемся охватить все этапы настройки устройства, начиная от его идентификации в ОС как сканирующего устройства (далее по тексту просто «устройство»), отладки и заканчивая приемами установки неподдерживаемых драйвером устройств без вмешательства в его код (но это неточно). Всё описанное затрагивает устройства, подключаемые только по локальному интерфейсу USB (но не SCSI). Предоставленный в тексте листинг команд выполнен в ОС Astra Linux Special Edition (ALSE) 1.7.4 и 1.7.6 в режиме защищенности «Смоленск» без сторонних репозиториев. При наличии особенностей, специфичных только для ALSE, это будет отмечено в конце раздела. Упоминаемые в тексте названия программ в скобках содержат название пакета, в состав которого они входят.

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

Для удобства восприятия руководство разбито на три части:

  1. Получение информации об устройстве и поиск подходящего драйвера. Об этом и пойдет речь ниже.

    Сбор информации об устройстве при помощи системных утилит, входящих в состав ОС, а также программ от разработчиков SANE. Расскажем, как физическое устройство (например, МФУ) представляется в ОС сразу и принтером, и сканером, и много ещё чем. Обязательно затронем тему дескрипторов и детально рассмотрим значение каждого из них. База, так сказать. Заглянем в типовой пакет проприетарного драйвера и на примерах покажем, как он должен выглядеть в идеальном современном мире. Глазами разработчиков SANE, конечно. Мы не будем с ними спорить.

  2. Установка и конфигурирование устройства.

    Во второй части начнем работать со сканирующим устройством. Разберем, каким образом файлу символьного устройства назначаются права и загружается драйвер (а иногда и модуль), как выполнять диагностику и отладку в случае возникновения проблем, и сведем все возможные проблемы в один список в порядке очередности их решения. Попробуем установить устройство без драйвера.

  3. Практики использования 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
dmesg: событие подключения
dmesg: событие подключения
  • Временная метка (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=1Product=2SerialNumber=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
dmesg: событие от модуля usblp
dmesg: событие от модуля 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: расшифровка значений
lsusb: расшифровка значений

Обратите внимание: 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
Особенности редактирования hwbd
  • Буквенные значения 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 останется нетронутой. Но, как известно из вывода stracelsusb считает данные и из этой базы, и вывод всё же будет содержать обновленные данные.

Убедиться в правильности выполненных действий можно в повторном выводе 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 существуют следующие стандартные дескрипторы:

  1. Устройство (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 bAlternateSetting
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.

Расшифровка значения bmAttributes
Расшифровка значения bmAttributes

Несмотря на то, что 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 - направление.

Расшифровка значения bEndpointAddress
Расшифровка значения bEndpointAddress

Обратите внимание: класс питания для одного устройства может меняться при выборе активной конфигурации этого же устройства.

С планшетным сканером 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=1Product=2SerialNumber=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-трафика.

Как собрать дампы при помощи wireshark?

 Загрузить модуль 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) за нас.

В Wireshark всё куда компактнее
В Wireshark всё куда компактнее

Но ведь у 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

Hidden text
<...>
<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 и получения верного ответа на контрольный запрос.

Hidden text
<...>
 /* 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) уже предустановлены пакеты libsane1libsane-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 из РФ

На официальной странице 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)

Комментарии (7)


  1. noth1ng01
    16.07.2024 09:25
    +6

    Титанический труд, где собрана информация из разных источников в один цельный материал!


  1. Kamnevn
    16.07.2024 09:25
    +3

    Огромное спасибо за ваш труд! Очень актуальная и нужная тема. Будем использовать статью и для нашей технической поддержки.


  1. flamelcadet
    16.07.2024 09:25
    +1

    Надеюсь после такой статьи даже разработчики драйверов обратят внимание на Best Practice и начнут придерживаться стандартов:)


  1. MaksimTarasov
    16.07.2024 09:25

    Огромное спасибо за статью. Только сегодня бился со сканером. Благодаря статье некоторые вещи стали понятны. Подскажите, а как fly-scan находит сетевые сканеры? Почему-то некоторые мфу не попадают в список, хотя настройки у мфу одинаковые. И можно для fly-scan прописать конкретный сканер, без поиска?


    1. ddtpv Автор
      16.07.2024 09:25
      +2

      ответы вы найдёте во второй части и третьей части. Публикация в процессе.

      Работа с сетевыми устройствами ничем не отличается от локальных - поиск осуществляется через бэкенды. Ограничения возможны при поиске устройств из другой подсети.

      И можно для fly-scan прописать конкретный сканер, без поиска?

      fly-scan -d <device_name>

      где <device_name> - имя устройства из вывода `scanimage -L`


  1. leon_shtuet
    16.07.2024 09:25

    Другим примером может послужить графическое приложение Image Scan! for Linux (iscan), устанавливаемое из пакета драйвера к устройствам Epson.

    А где можно скачать для Mint Image Scan! for Linux (iscan)? Ткните носом. На сайте Epson не нашел. Но у меня сканер не Epson. И может у кого нибудь есть свежий, последний бинарник simple-scan. А то с Mint идет древняя древность, а как обновить, не догоняю. Заранее спасибо.


    1. ddtpv Автор
      16.07.2024 09:25

      Поищите в файлах к своему устройству на https://download.ebz.epson.net/dsc/search/01/search/