lanefu Posted June 16, 2021 Posted June 16, 2021 I have a bunch of UARTS attached to a USB hub, and I need to map them consistent to /dev/ttyUSB[x] I've done some mapping of the ports. but I don't quite know what I'm doing.. any help with some example rules would be great.. here's a sample /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M ID 1d6b:0002 Linux Foundation 2.0 root hub |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M ID 05e3:0608 Genesys Logic, Inc. Hub |__ Port 3: Dev 3, If 0, Class=Hub, Driver=hub/4p, 480M ID 05e3:0608 Genesys Logic, Inc. Hub |__ Port 4: Dev 4, If 0, Class=Vendor Specific Class, Driver=ch341, 12M ID 1a86:7523 QinHeng Electronics CH340 serial converter 0 Quote
tparys Posted June 19, 2021 Posted June 19, 2021 It has been my experience, that enumeration order of USB to serial devices is largely consistent boot-to-boot. At, least if they're all the same device / driver, and you're not moving cables between connectors. If that's not working, there's some udev examples HERE. You can also have udev dump everything that's worth looking at, and grep'ing for the devices you're looking for. udevadm info -e | less If they're all the same device and you need unique names, I'd probably separate them via DEVPATH, which basically works out to the appropriate position in the bus hierarchy. I'd also suggest giving them descriptive symlinks instead of renaming the devices, and address your devices that way. 0 Quote
lanefu Posted June 19, 2021 Author Posted June 19, 2021 There's consistency on the port ids of the hub im plugging into. Ill add some more samples 0 Quote
djurny Posted June 20, 2021 Posted June 20, 2021 Hi, I made a small detection script using expect. That will spit out the hosts it found per try and baudraute guesstimate. Afterwards it will query the USB port location using udevadm info and build udev rules on a name basis for the hosts it found on those ports. It's not a generic solution, let me know if you are interested, I'll share when back at my workstation. Groetjes, 1 Quote
lanefu Posted June 20, 2021 Author Posted June 20, 2021 Yeah would love to see the udevadm part 0 Quote
djurny Posted June 20, 2021 Posted June 20, 2021 Hi, See below udevadm rule: Spoiler ## ## __usb_serial ## ACTION=="remove", GOTO="__usb_serial_end" SUBSYSTEM!="tty", GOTO="__usb_serial_end" ENV{ID_VENDOR_ID}!="067b", GOTO="__usb_serial_ftdi" ENV{ID_MODEL_ID}!="2303", GOTO="__usb_serial_ftdi" LABEL="__usb_serial_pl2303" ## these devices do not have a serial number ## assignment is done based on physical location: usb-port, usb-hub ## be sure to plug in all the usb dongles in the same positions, otherwise this assignment is messed up! #[BEGIN] AUTOMATICALLY GENERATED CONTENT ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.2:1.0", SYMLINK+="serial/by-name/sinaspi" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.3:1.0", SYMLINK+="serial/by-name/orangepi" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.1.2:1.0", SYMLINK+="serial/by-name/pi2-01" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.1.3:1.0", SYMLINK+="serial/by-name/pi2-02" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.4.1:1.0", SYMLINK+="serial/by-name/nanopi0" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.1.4:1.0", SYMLINK+="serial/by-name/pi2-03" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.4.2:1.0", SYMLINK+="serial/by-name/nanopi1" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.1.1:1.0", SYMLINK+="serial/by-name/kobol0" ENV{ID_PATH}=="platform-3f980000.usb-usb-0:1.5.4.4:1.0", SYMLINK+="serial/by-name/bluebox1" #[END] AUTOMATICALLY GENERATED CONTENT LABEL="__usb_serial_ftdi" ENV{ID_VENDOR_ID}!="0403", GOTO="__usb_serial_end" ENV{ID_MODEL_ID}!="6015", GOTO="__usb_serial_end" ## these devices have a serial number ENV{ID_SERIAL_SHORT}=="DT03O6AD", SYMLINK+="serial/by-name/kobol0" ENV{ID_SERIAL_SHORT}=="DJ00JULE", SYMLINK+="serial/by-name/bluebox1" ENV{ID_SERIAL_SHORT}=="DJ00JDCX", SYMLINK+="serial/by-name/bluebox0" LABEL="__usb_serial_end" ## EOF And below bash snippet of how to generate it: generate_udev_rule() { # <dev> <name> typeset _DEV="${1:?}" typeset _NAME="${2:?}" typeset _ID_PATH _ID_PATH="$( udevadm info --query property "${_DEV}" | sed -n '/^ID_PATH/ { s/^ID_PATH=//p; q; }' )" printf "ENV{ID_PATH}==\"%s\", SYMLINK+=\"serial/by-name/%s\"\n" \ "${_ID_PATH:?}" \ "${_NAME:?}" } Hope that helps! Groetjes, configure-serial-tty 0 Quote
djurny Posted June 20, 2021 Posted June 20, 2021 Hi, sorry, I forgot to mention that the configure-serial-tty script does require some target modification: it depends on the baudrate being fixed on the target, as sending <BRK> both triggers Magic SysRq and optional baudrate switch by *getty. To get this to work on my setup with highish successrate, I reconfigured the target's serial-getty configurations to only work on one baudrate. See parts of my ansible task that sets that puppy up: Spoiler #... - name: Determine serial-getty services shell: | systemctl list-units --type=service --state=active,inactive --full --no-pager --no-legend --plain "{{item}}@*" | while read _UNIT _DONTCARE do echo "{{item}} ${_UNIT:-N/A}" done with_items: - 'serial-getty' - 'armbianmonitor' register: service_units - name: Modify serial-getty console baudrate shell: | set -x _TIMESTAMP=`date -Isec` _TMP=`mktemp` _ITEM_STDOUT="{{item.stdout}}" echo "${_ITEM_STDOUT:-}" | while read _SERVICE _UNIT do _UNITFILE=`systemctl show "${_UNIT:?}" | sed -n '/FragmentPath/ { s/^FragmentPath=//p; q; }'` sed -n 's/^ExecStart=//p;' "${_UNITFILE:?}" > "${_TMP}" read -r _CMDLINE < "${_TMP}" eval set -- ${_CMDLINE:-} OPTIND=2 # skip ARGV[0] while getopts ':o:-:' OPT do : done shift $(( ${OPTIND:-} - 1 )) _BAUDRATES="${1:?}" _BAUDRATE_NEW="${_BAUDRATES%%,*}" if [ "${_BAUDRATE_NEW:-}" != "${_BAUDRATES:-}" ] then _BACKUP="${_UNITFILE:?}.${_TIMESTAMP:?}~" echo "Making backup of '${_UNITFILE:-N/A}' to '${_BACKUP:-N/A}'." cp -ab "${_UNITFILE:?}" "${_BACKUP:?}" echo "Modifying '${_UNITFILE:-N/A}'." ## Example: ## ExecStart=-/sbin/agetty -o '-p -- \\u' --keep-baud 1500000 %I $TERM sed -i -E " /^ExecStart=/ { h; s/^[ \t]*[^#].*/#[DISABLED by Ansible] &/p; g; i #[BEGIN] Ansible managed s/ *\<-s\>//; s/--keep-baud[[:blank:]]//; s/--extract-baud[[:blank:]]//; s/${_BAUDRATES}/${_BAUDRATE_NEW}/; a #[END] Ansible managed }" "${_UNITFILE:?}" echo "# diff -y -W\${COLUMNS:-} '${_UNITFILE:-}' '${_BACKUP:-}'" echo "Changed!" systemctl daemon-reload fi done rm "${_TMP:?}" register: serial_getty_config changed_when: "'Changed!' in serial_getty_config.stdout" vars: - __item: 'serial-getty' loop: "{{ service_units.results }}" when: - __item == item.item #... The ansible task shell script will try to parse the commandline to get the currently configured baudrates. It assumes the baudrates are sorted from high to low, i.e. 1500000,115200,38400,9600 for my NanoPi R2S and Helios64 boxen. It will replace the baudrates with the most-left it had parsed (which should be the highest baudrate). It also will disable the options to *getty that skip setting the baudrate explicitly (i.e. keep-baud and extract-baud). Let me know if this worked out for you! Groetjes, 1 Quote
lanefu Posted June 20, 2021 Author Posted June 20, 2021 11 hours ago, lanefu said: There's consistency on the port ids of the hub im plugging into. Ill add some more samples lane@Ippon-LJ-M1:~$ ssh tritium-h5 lsusb --tree lane@tritium-h5's password: Permission denied, please try again. lane@tritium-h5's password: /: Bus 09.Port 1: Dev 1, Class=root_hub, Driver=musb-hdrc/1p, 480M /: Bus 08.Port 1: Dev 1, Class=root_hub, Driver=ohci-platform/1p, 12M /: Bus 07.Port 1: Dev 1, Class=root_hub, Driver=ohci-platform/1p, 12M /: Bus 06.Port 1: Dev 1, Class=root_hub, Driver=ohci-platform/1p, 12M /: Bus 05.Port 1: Dev 1, Class=root_hub, Driver=ohci-platform/1p, 12M /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M |__ Port 3: Dev 3, If 0, Class=Hub, Driver=hub/4p, 480M |__ Port 3: Dev 6, If 0, Class=Vendor Specific Class, Driver=ch341, 12M |__ Port 4: Dev 5, If 0, Class=Vendor Specific Class, Driver=ch341, 12M |__ Port 4: Dev 4, If 0, Class=Vendor Specific Class, Driver=ch341, 12M /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-platform/1p, 480M 0 Quote
djurny Posted June 20, 2021 Posted June 20, 2021 Hi @lanefu, For your usecase, best to do the following: for A in /dev/ttyUSB* do ( echo "${A:?}" udevadm info --query property "${A:?}" | egrep -- '^ID_(PATH|SERIAL_SHORT)=' ) | xargs done That will give you either the USB port location in the USB tree (ID_PATH) or any serial number (ID_SERIAL_SHORT) as provided by udev, ready to make rules with. As I bought a lot of low-end USB-serial dongles, they do not have any serial - hence the use of the USB port location. I run the script (attached in earlier post) every time I change something in the hub, or when I plug the hub into another device. Let me know if this helped you! Groetjes, 0 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.