Подключение сканеров, принтеров и др. при помощи usbip в thinstation 5 | Thinstation по русски

Подключение сканеров, принтеров и др. при помощи usbip в thinstation 5


Если достаточно пробросить одно устройство с тонкого клиента на сервер, то можно попробовать софтину из этого поста на форуме.
Ну а если чем то она не устраивает, то поехали дальше.

Итак, начнём с самого главного. Серверная часть этого пакета лежит в ядре в виде модулей. Скажу больше, вам не нужно перекомпилировать ядро в thinstation, потому как они уже включены по-умолчанию, это три модуля usbip-core.ko, usbip-host.ko и vhci-hcd.ko. Для того, чтобы эти модули были собраны в ваш образ вам необходимо добавить эти модули в build.conf вручную тремя строчками, потому что эти модули вспомогательные и не учтутся скриптом hwlister.sh:

( HTML ломает некоторые знаки. Для точного копирования команд справа от листинга есть кнопка <>)

module usbip-core
module usbip-host
module vhci-hcd

Теперь нам необходима клиентская часть, несмотря на то, что наш ТК выступает в роли сервера. Клиентская часть находится всё в том же ядре, только в userspace части. Для этого нам понадобятся исходники нашего ядра. Итак, скачиваем их:

cd thinstation
su
./setup-chroot
cd /ts/ports/kernel-modules/kernel-TS
pkgmk -d -kw

После применения патчей прерываем компиляцию, нажав Ctrl+C

Теперь нам необходимо скомпилировать клиентскую часть вручную - она не собирается вместе с остальным ядром, но для начала мы проведём несколько манипуляций.

 Итак, перейдём в каталог с исходниками клиентской части (обратите внимание на версию ядра, она скорее всего уже будет иная):

cd /ts/ports/kernel-modules/kernel-TS/work/src/linux-4.6.3/tools/usb/usbip

Находим файл configure.ac и меняем в строке указания версии значение 0x00000111 на 0x00000106

AC_DEFINE([USBIP_VERSION], [0x00000106], [binary-coded decimal version number])

Теперь перейдём в каталог src

cd src

и добавим в шапку файла usbip_network.h следующие директивы

#define USBIP_CMD_SUBMIT 0x0001
#define USBIP_CMD_UNLINK 0x0002
#define USBIP_RET_SUBMIT 0x0003
#define USBIP_RET_UNLINK 0x0004
#define USBIP_DIR_OUT 0x00
#define USBIP_DIR_IN 0x01

Редактировать не обязательно в chroot-окружении, пользуйтесь любым удобным редактором (mc, vi ...), а вот команды выполнять в chroot.
Все манипуляции проведены, теперь приступим к сборке пакета. Выйдем на ступень вверх из каталога src и выполним все необходимые процедуры:

cd ..
./autogen.sh
./configure --exec-prefix=/ts/build/packages/usbip --with-usbids-dir=/lib
make install

Параметром exec-prefix я указал установщику положить библиотеки и бинарники в каталог, где лежат все пакеты, которые могут быть добавлены через параметр package файла build.conf. Перейдём в этот каталог и посмотрим содержимое:

cd /ts/build/packages/usbip
ls -l
total 8
drwxr-xr-x 2 root root 4096 Nov 22 09:51 sbin
drwxr-xr-x 2 root root 4096 Nov 22 09:51 lib

Переименуем каталог sbin в bin(точнее переместим):

mv sbin bin

Создадим в корне нашего пакета файл dependencies и добавим туда слово ids и base, чтобы наш пакет зависел от базовых утилит(они нам понадобятся в дальнейшем) и :

cat > dependencies
base <Enter>
ids
CTRL+d

CTRL+d - это сочетание клавиш, которое необходимо нажать после набора слова ids.

makedna usbip

И добавим в файл build.conf параметр:

package usbip

Теперь нам осталось совсем немного до того момента, как мы сможем закинуть наш пакет в образ и заставить его работать.
Перед нами стоит задача довести процедуру связывания устройств с сервисом usbip до автоматизма, иными словами, при подключении нового устройства или смена порта подключения наше устройство автоматически "мапилось" через usbip, без выковыривания нужных параметров usbip и дополнительной пересборки образа. В этом нам поможет udev - менеджер устройств. У нас возникает только один нюанс - средствами udev невозможно различить устройства по типам - принтеры, сканеры, накопители, hid-устройства и т.д., а это важно, потому что нам не нужно "прокидывать" мышку на сервер через usbip, тем более что устройство становится полностью недоступным для ТК после подключения к нему клиентом usbip. Выход в данной ситуации может быть следующим - перечислить в правилах производителя устройства(т.н. вендор) и, соответственно, таким образом различать устройства. Ну что ж, воплотим слова в жизнь.
В каталоге /ts/build/packages/usbip/lib/udev/rules.d мы создадим своё проавило 99-usbip.rules (всё должно быть в одной строке!):

ACTION=="add", ENV{ID_BUS}=="usb", ENV{ID_VENDOR}=="Samsung", RUN+="/bin/usbip_bind %E{ID_VENDOR_ID} %E{ID_MODEL_ID}"

Немного расшифрую смысл написанного. Первые три параметра(ACTION и два ENV) действуют по принципу логического И, т.е. при добавлении устройства (ACTION=="add") usb (ENV{ID_BUS}=="usb") производителя Samsung (ENV{ID_VENDOR}=="Samsung") должна выполнятся команда (RUN+="usbip_bind %E{ID_VENDOR_ID} %E{ID_MODEL_ID}"). Таким образом, если вам нужны ещё устройства, например, Canon, Hewlett-Packard, Epson и др., то вам нужно в этом же файле создать точно такие строки, только в ENV{ID_VENDOR} указать нужного вам производителя. Следует заметить, что название производителя не может быть взято "с потолка", его можно посмотреть, например, выполнив команду udevadm monitor --env | grep ID_VENDOR, подключившись к ТК по telnet или ssh, и подключив ваше устройство(после выполнения команды).
Как вы могли заметить, в параметре RUN присутствует usbip_bind. Это скрипт, который автоматически подвязывает наше устройство на usbip, при этом нам не нужно будет заботится об особых параметрах, которые передаются команде usbip - это всё за нас будет делать наш скрипт. Этот скрипт мы положем в /ts/5.1/packages/usbip/bin. Итак, создадим скрипт, дадим права на выполнение и заполним его:

cd /ts/5.1/packages/base/lib/udev
touch usbip_bind
chmod 0755 usbip_bind

#! /bin/sh

. /etc/thinstation.env
. $TS_GLOBAL

BUSID=`/bin/usbip list -l | /bin/grep $1:$2 | /bin/awk '{print $3}' | grep -`
USB_IP=`/bin/ps | /bin/grep -v grep | /bin/grep "usbipd -D"`

if [ -n "$USB_IP" ]; then
/bin/usbip bind -b $BUSID
/bin/usbip attach --remote=localhost --busid=$BUSID
sleep 3
/bin/usbip port
/bin/usbip detach --port=00
else
modprobe usbip-core
modprobe usbip-host
modprobe vhci-hcd
/bin/usbipd -D
/bin/usbip bind -b $BUSID
/bin/usbip attach --remote=localhost --busid=$BUSID
sleep 3
/bin/usbip port
/bin/usbip detach --port=00
fi

Рассмотрим этот скрипт подробнее. Параметр BUSID - это необходимый параметр команды usbip, его мы вычисляем автоматически с помощью параметров ID_VENDOR_ID и ID_MODEL_ID(они передаются параметрами в скрипт из нашего правила udev). Далее, как вы видите, идет условный оператор if. Всё дело в том, что если ваше устройство уже подключено на момент включения ТК, то при включении ТК вам нужно запустить службу usbipd, а если вы будете подключать устройство уже во время работы ТК, то вам нет необходимоти этого делать. Поэтому здесь мы и воспользовались условным оператором if, а условие "-n $USB_IP" определяет запущен ли процесс usbipd.

Теперь нам остаётся собрать образ, загрузится с ТК, подключится в консоль и убедится, что наше устройство готово к использованию со стороны сервера. Переходим в терминал и командуем:

lsmod | grep usbip

видим:
usbip_host 12943 0
usbip_core 5618 1 usbip_host
usbcore 124913 4 usbip_host,usbhid,uhci_hcd,ehci_hcd
ts_test_1:~# usbip list -l
Local USB devices
=================
- busid 2-1 (046d:c018)
2-1:1.0 -> usbhid
- busid 2-2 (04a9:2220)
2-2:1.0 -> usbip-host

Как видите строка "2-2:1.0 -> usbip-host" показывает, что наше устройство уже готово со стороны сервера, но подключится с винды получится если устройство было запущено или перезапущено после загрузки клиента.

Теперь остался последний штрих в нашем нелегком деле - настроить клиента. Клиентом в данном случае будет Windows 2008 R2, на других версиях Windows возможно не заведётся, но если такая уж необходимость, то надо пробовать.
Итак, идём вот сюда и скачиваем usbip_windows_v0.2.0.0_signed.zip. Распакуем это "добро" в каталог C:\ (желательно, чтобы название каталога было usbip, а не usbip_win..., это просто для дальнейшего удобства). Теперь добавим новое "старое устройство". Всё это описано в файле USAGE скачанного нами клиента. Итак, по шагам:

1. Открываем "Панель управления" - "Диспетчер устройств"
2. Правой кнопкой мыши на имени компьютера - "Установить старое устройство"
3. Откроется мастер, жмёте "Далее"
4. В следующем окне выбираете "Установка оборудования, выбранного из списка вручную" и жмёте "Далее"
5. Выбираете "Системные устройства" - "Далее"
6. "Установить с диска" указываете путь, куда распаковали клиента, и снова "Далее"
7. Выберите "USB/IP Enumerator", нажмите "ОК", "Далее" и "Готово"

Теперь необходимо подключить наше устройство, присоединенное к ТК. Открываем командную строку и пишем:

C:\Users\Administrator>cd c:\usbip
usbip -l 10.10.10.10
- 10.10.10.10
2-2: Canon, Inc. : CanoScan LIDE 25 (04a9:2220)
: /sys/devices/pci0000:00/0000:00:1d.1/usb2/2-2
: Vendor Specific Class / unknown subclass / unknown protocol (ff/00/ff)
: 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/ff)
usbip -a 10.10.10.10 2-2
new usb device attached to usbvbus port 1
Receive sequence: 3900

Командой "usbip -l 10.10.10.10" мы вывели список всех устройств с нашего ТК, "расшаренных" через usbip (10.10.10.10 - ip нашего ТК).
Командой "usbip -a 10.10.10.10 2-2" мы примапили это устройство к себе на наш терминальный сервер (2-2 это тот самый busid, необходимый для присоединения устройства).
Теперь в диспетчере устройств у вас есть новое неопознанное устройство. Ставите на него драйвера и оно должно заработать как ему и положено.
Коммандую строку закрывать нельзя, иначе связь обрывается. Есть проще решение - написать скрипт и выполнять в фоне. Самый простой вариант выглядит так:

On Error Resume Next
Set wshshell = CreateObject("wscript.shell")
WshShell.Run "cmd /c cd c:\usbip & usbip -a 10.10.10.10 2-2", 0, FALSE

Этот скрипт можно повесить в качестве logon-скрипта. Но есть один недостаток - если связь с терминалом потеряна и сессия остаётся незавершенной, то при входе вы снова попадаете в свою сессию, но скрипт уже не запустится, а связь с устройством прервана. Поэтому в таком случае есть два варианта - либо завершить сессию и войти снова, либо запустить скрипт вручную не перезапуская сессию. Конечно, можно написать скрипт как службу, которая будет постоянно мониторить доступные устройства по определенному адресу или диапазону адресов, но это уже нетривиальная задача для написания подобного решения. Простыми командами тут не обойдешься и скорее всего нужно использовать PowerShell. На данный момент у меня нет возможности написания такого скрипта, так что этот вопрос остаётся за вами. Буду рад увидеть от вас решения по автоматизации работы со стороны Windows.

Статья переработана 10.02.2017 года, в свете последних изменений в исходниках ядра.

Готовый пакет на ядре 4.6.3 можно забрать ЗДЕСЬ

Оригинал статьи здесь

Обсудить на форуме (комментариев 30).