Виртуальный последовательный порт для Linux

Мне нужно протестировать приложение с последовательным портом в Linux, однако мой тестовый компьютер имеет только один последовательный порт.

Есть ли способ добавить виртуальный последовательный порт в Linux и протестировать мое приложение, эмулируя устройство через оболочку или скрипт?

Примечание: я не могу переназначить порт, он жестко запрограммирован на ttys2, и мне нужно протестировать приложение так, как оно написано.

10 ответов

Решение

Для этого вы можете использовать pty ("псевдо-телетайп", где последовательный порт является "настоящим телетайпом"). С одного конца откройте /dev/ptyp5и затем присоедините вашу программу к /dev/ttyp5; ttyp5 будет действовать как последовательный порт, но будет отправлять / получать все, что делает через /dev/ptyp5.

Если вам действительно нужно поговорить с файлом под названием /dev/ttys2тогда просто двигай старое /dev/ttys2 и сделайте символическую ссылку из ptyp5 в ttys2,

Конечно, вы можете использовать другое число, кроме ptyp5, Возможно, выберите один с большим номером, чтобы избежать дубликатов, так как все ваши терминалы входа также будут использовать ptys.

В Википедии есть больше о ptys: http://en.wikipedia.org/wiki/Pseudo_terminal

Дополнение ответа @slonik.

Вы можете протестировать socat для создания виртуального последовательного порта, выполнив следующую процедуру (протестировано в Ubuntu 12.04):

Откройте терминал (назовем его Terminal 0) и выполните его:

socat -d -d pty,raw,echo=0 pty,raw,echo=0

Код выше возвращает:

2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/2
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/3
2013/11/01 13:47:27 socat[2506] N starting data transfer loop with FDs [3,3] and [5,5]

Откройте другой терминал и напишите (Терминал 1):

cat < /dev/pts/2

Имя порта этой команды может быть изменено в зависимости от компьютера. это зависит от предыдущего вывода.

2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/**2**
2013/11/01 13:47:27 socat[2506] N PTY is /dev/pts/**3**
2013/11/01 13:47:27 socat[2506] N starting data transfer loop with FDs 

Вы должны использовать номер, доступный в выделенной области.

Откройте другой терминал и напишите (Терминал 2):

echo "Test" > /dev/pts/3

Теперь вернитесь в Терминал 1, и вы увидите строку "Тест".

Используйте socat для этого:

Например:

socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11

Существует также tty0tty http://sourceforge.net/projects/tty0tty/ который является настоящим эмулятором нуль-модема для linux.

Это простой модуль ядра - небольшой исходный файл. Я не знаю, почему это только подействовало на sourceforge, но у меня это хорошо работает. Лучшая вещь об этом - то, что это также эмулирует аппаратные контакты (RTC/CTS DSR/DTR). Он даже реализует команды iotcl TIOCMGET/TIOCMSET и TIOCMIWAIT!

В недавнем ядре вы можете получить ошибки компиляции. Это легко исправить. Просто вставьте несколько строк вверху источника module/tty0tty.c (после include):

#ifndef init_MUTEX
#define init_MUTEX(x) sema_init((x),1)
#endif

Когда модуль загружен, он создает 4 пары последовательных портов. Устройства - от /dev/tnt0 до /dev/tnt7, где tnt0 подключен к tnt1, tnt2 подключен к tnt3 и т. Д. Вам может потребоваться исправить разрешения для файлов, чтобы иметь возможность использовать устройства.

редактировать:

Я думаю, что я был немного быстр с моим энтузиазмом. Хотя водитель выглядит многообещающе, он кажется нестабильным. Я не знаю наверняка, но я думаю, что это сломало машину в офисе, над которым я работал из дома. Я не могу проверить, пока я не вернусь в офис в понедельник.

Во-вторых, TIOCMIWAIT не работает. Код, кажется, скопирован из некоторого примера кода "крошечный tty". Обработка TIOCMIWAIT, кажется, на месте, но она никогда не просыпается, потому что отсутствует соответствующий вызов wake_up_interruptible().

редактировать:

Авария в офисе действительно была ошибкой водителя. Отсутствовала инициализация, и полностью непроверенный код TIOCMIWAIT вызвал сбой машины.

Я провел вчера и сегодня переписывая драйвер. Было много проблем, но теперь у меня это хорошо работает. Все еще отсутствует код для аппаратного управления потоком, управляемого драйвером, но он мне не нужен, потому что я сам буду управлять выводами, используя TIOCMGET/TIOCMSET/TIOCMIWAIT из кода пользовательского режима.

Если кого-то заинтересует моя версия кода, отправьте мне сообщение, и я отправлю его вам.

Возможно, вы захотите взглянуть на Tibbo VSPDL для создания виртуального последовательного порта Linux с использованием драйвера ядра - он выглядит довольно новым и доступен для скачивания прямо сейчас (бета-версия). Не уверен насчет лицензии на данный момент, или они хотят сделать ее коммерчески доступной только в будущем.

Есть и другие коммерческие альтернативы, такие как http://www.ttyredirector.com/.

В Open Source Remserial (GPL) также может делать то, что вы хотите, используя Unix PTY. Он передает последовательные данные в "сыром виде" на сетевой сокет; STTY-подобная настройка параметров терминала должна быть сделана при создании порта, изменение их позже, как описано в RFC 2217, кажется, не поддерживается. Вы должны иметь возможность запустить два экземпляра remserial для создания виртуального нуль-модема, такого как com0com, за исключением того, что вам нужно заранее настроить скорость порта и т. Д.

Socat (также GPL) похож на расширенный вариант Remserial со множеством других опций, включая метод "PTY" для перенаправления PTY на что-то другое, что может быть еще одним примером Socat. Для юнит-токов socat, вероятно, лучше, чем remserial, потому что вы можете напрямую загружать файлы в PTY. Смотрите пример PTY на странице руководства. В разделе "contrib" существует патч, обеспечивающий поддержку RFC2217 для согласования настроек последовательной линии.

Используя ссылки, размещенные в предыдущих ответах, я написал небольшой пример на C++ с использованием виртуального последовательного порта. Я вставил код в GitHub: https://github.com/cymait/virtual-serial-port-example.

Код довольно понятен. Во-первых, вы создаете мастер-процесс, запустив./main master, и он напечатает на stderr, который использует устройство. После этого вы вызываете./main slave device, где device - это устройство, напечатанное в первой команде.

И это все. У вас есть двунаправленная связь между двумя процессами.

Используя этот пример, вы можете протестировать приложение, отправив все виды данных, и посмотреть, правильно ли оно работает.

Кроме того, вы всегда можете использовать символическую ссылку на устройство, поэтому вам не нужно перекомпилировать приложение, которое вы тестируете.

Я могу придумать три варианта:

Реализация RFC 2217

RFC 2217 охватывает com-порт стандарта TCP/IP, который позволяет клиенту в одной системе эмулировать последовательный порт для локальных программ, в то же время прозрачно отправляя и получая данные и управляющие сигналы на сервер в другой системе, которая фактически имеет последовательный порт. Вот обзор высокого уровня.

Что бы вы сделали, это нашли или внедрили драйвер клиентского com-порта, который бы реализовывал клиентскую часть системы на вашем ПК - он выглядит как настоящий последовательный порт, но в действительности все переносит на сервер. Вы можете получить этот драйвер бесплатно у Digi, Lantronix и т. Д. Для поддержки их реальных автономных серверов последовательных портов.

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

Это, вероятно, нетривиально, но RFC уже существует, и вы можете найти проект с открытым исходным кодом, который реализует одну или обе стороны соединения.

Изменить драйвер последовательного порта Linux

Кроме того, источник драйвера последовательного порта для Linux легко доступен. Возьмите это, соберите элементы управления аппаратным обеспечением, и пусть один драйвер запускает два порта /dev/ttySx в качестве простой обратной петли. Затем подключите вашу настоящую программу к ttyS2, а симулятор - к другому ttySx.

Используйте два USB<->последовательных кабеля в петле

Но что проще всего сделать прямо сейчас? Потратьте $40 на два USB-устройства с последовательным портом, соедините их вместе (нуль-модем) и фактически получите два реальных последовательных порта - один для программы, которую вы тестируете, другой для вашего симулятора.

-Адам

      $ socat -d -d pty,link=/tmp/vserial1,raw,echo=0 pty,link=/tmp/vserial2,raw,echo=0

Будет генерировать символические ссылки /tmp/vserial1а также /tmp/vserial2для сгенерированных виртуальных последовательных портов в /dev/pts/*

Ресурс

Вы могли бы использовать адаптер USB->RS232? У меня есть несколько, и они просто используют драйвер FTDI. Затем вы сможете переименовать /dev/ttyUSB0 (или что-либо еще созданное) в /dev/ttyS2 .

Объединив все другие удивительно полезные ответы, я обнаружил, что приведенная ниже команда ОЧЕНЬ полезна для тестирования на различных типах дистрибутивов Linux, где нет гарантии, что вы будете получать одни и те же /dev/pts/# каждый раз и / или вы необходимо протестировать несколько псевдопоследовательных устройств и соединений одновременно.

      parallel 'i="{1}"; socat -d -d pty,raw,echo=0,link=$HOME/pty{1} pty,raw,echo=0,link=$HOME/pty$(($i+1))' ::: $(seq 0 2 3;)

Разбивая это:

parallelзапускает одну и ту же команду для каждого переданного ей аргумента. Так, например, если мы запустим его с--dryrunфлаг, который он нам дает:

      i="0"; socat -d -d pty,raw,echo=0,link=$HOME/pty0 pty,raw,echo=0,link=$HOME/pty$(($i+1))
i="2"; socat -d -d pty,raw,echo=0,link=$HOME/pty2 pty,raw,echo=0,link=$HOME/pty$(($i+1))

Это связано с$(seq x y z;)в конце, где x = начальный #, y = увеличение на и z = конечный # (или # устройств, которые вам нужно создать)

parallel 'i="{1}"; echo "make psuedo_devices {1} $(($i+1))"' ::: $(seq 0 2 3;)

Выходы:

      make psuedo_devices 0 1
make psuedo_devices 2 3

Собрав все это вместе, последняя вышеприведенная команда символически связывает нужные псевдоустройства вместе, независимо от того, что находится в /dev/pts/, в любой каталог, предоставленный socat черезlinkфлаг.

pstree -c -a $PROC_IDдает:

      perl /usr/bin/parallel i="{1}"; socat -d -d pty,raw,echo=0,link=$HOME/pty{1} pty,raw,echo=0,link=$HOME/pty$(($i+1)) ::: 0 2
  ├─bash -c i="0"; socat -d -d pty,raw,echo=0,link=$HOME/pty0 pty,raw,echo=0,link=$HOME/pty$(($i+1))
  │   └─socat -d -d pty,raw,echo=0,link=/home/user/pty0 pty,raw,echo=0,link=/home/user/pty1
  └─bash -c i="2"; socat -d -d pty,raw,echo=0,link=$HOME/pty2 pty,raw,echo=0,link=$HOME/pty$(($i+1))
      └─socat -d -d pty,raw,echo=0,link=/home/user/pty2 pty,raw,echo=0,link=/home/user/pty3

ls -l$HOME/pty* выход:

      lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty0 -> /dev/pts/4
lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty1 -> /dev/pts/6
lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty2 -> /dev/pts/7
lrwxrwxrwx 1 user user 10 Sep  7 11:46 /home/user/pty3 -> /dev/pts/8

Это было все потому, что я пытался запустить тесты на платформе, где мне нужно было сгенерировать много последовательных подключений и проверить их ввод/вывод через контейнеризацию (Docker). Надеюсь, кто-то найдет это полезным.

Другие вопросы по тегам