串口綁定(或者叫串口別名)與權限
linux默認對dialout組的設備文件命名根據設備類型與插入順序確定。多個串口設備開機前插在主機上,這些設備在開機時載入順序是不確定的,所以需要綁定設備名(就是一個鏈接文件到確定的設備),非root用戶還會需要串口的讀寫權限。
a.綁定設備名:
1.具有唯一碼的設備
查看需要綁定的設備的idVendor 和idpProduct
$ lsusb -vv
$ sudo gedit /etc/udev/rules.d/10-local.rules
自定義打開文件寫入自定義規則(如:通過lsusb -vv 看到設備的idVendor與idProduct分別為0x067b Prolific Technology, Inc.,0x2303 PL2303 Serial Port)。這里最好新建一個文件,不要覆蓋以前的文件了。如果是相同設備還需要額外的辨別參數也可以添加額外的參數。我們只取前面的數值0x067b,0x2303,后面的描述不要
ACTION=="add", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SYMLINK+="MyArduino"
#####還有其他額外的設備需要則加一行,方法一樣。
參數說明:
ACTION 事件 (udevent)的行為,例如:add( 添加設備 )、remove(刪除設備 )。
SYMLINK 為 /dev/下的設備文件產生符號鏈接。由於 udev只能為某個設備產生一個設備文件,所以為了不覆蓋系統默認的 udev規則所產生的文件,推薦使用符號鏈接。
== 匹配 相等比較
+= 賦值 追加特定的值給已經存在的鍵
更多內容查看udev規則。
$ udevadm info -a -p $(udevadm info -q path -n /dev/ttyACM0)
##### 如果不止一個串口,比如還有/dev/ttyUSB0,在/dev/ttyACM0后添加 /dev/ttyUSB0
##### 只是看看串口設備信息
重啟后生效。
重啟后查看:
$ ls -l /dev/My*
應該會看到類似的輸出:
lrwxrwxrwx 1 root root 7 4月 11 10:32 MyArduino -> ttyACM0
lrwxrwxrwx 1 root root 7 4月 11 10:32 MyPL0 -> ttyUSB0 ####這是我的另一個設備
2.不具有唯一碼的設備
以下為2個以上相同串口設備情況:
在前面已經提及不同串口設備的綁定方式。如果是相同設備以上綁定方式就不能應對了,lsusb -vv顯示相同設備信息全部都是一樣的,這時/dev/MyArduino指向/dev/ttyACM1(2個相同設備分別是/dev/ttyACM0,/dev/ttyACM1,總是指向末尾數值最大的一個)。對於大多數廉價適配器它們並沒有特別的編號進行區分。
因此我們考慮一種更麻煩的綁定方式,根據usb的物理端口來區分(一般來說對於一台機器,usb硬件端口都是固定的,也因此我們考慮這種方式來解決綁定問題)。這不同於相同串口設備具有相同的硬件信息,每台計算機的usb物理端口信息可能都是不同的,所以每台計算機都得單獨配置,而且一旦串口設備綁定某個物理usb端口,換個usb端口將會導致不能正確識別。
因此我們需要設備更詳細的信息,比如我們需要/dev/ttyUSB0的信息:
$ udevadm info -a /dev/ttyUSB0
可以看到類似以下信息
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/ttyUSB0/tty/ttyUSB0':
KERNEL=="ttyUSB0"
SUBSYSTEM=="tty"
DRIVER==""
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0/ttyUSB0':
KERNELS=="ttyUSB0"
SUBSYSTEMS=="usb-serial"
DRIVERS=="pl2303"
ATTRS{port_number}=="0"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0':
KERNELS=="3-2:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="pl2303"
ATTRS{authorized}=="1"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bInterfaceClass}=="ff"
ATTRS{bInterfaceNumber}=="00"
ATTRS{bInterfaceProtocol}=="00"
ATTRS{bInterfaceSubClass}=="00"
ATTRS{bNumEndpoints}=="03"
ATTRS{supports_autosuspend}=="1"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3/3-2':
KERNELS=="3-2"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="100mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bcdDevice}=="0300"
ATTRS{bmAttributes}=="80"
ATTRS{busnum}=="3"
ATTRS{configuration}==""
ATTRS{devnum}=="2"
ATTRS{devpath}=="2"
ATTRS{idProduct}=="2303"
ATTRS{idVendor}=="067b"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Prolific Technology Inc."
ATTRS{maxchild}=="0"
ATTRS{product}=="USB-Serial Controller"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="removable"
ATTRS{speed}=="12"
ATTRS{urbnum}=="22"
ATTRS{version}==" 1.10"
looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3':
KERNELS=="usb3"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{authorized}=="1"
ATTRS{authorized_default}=="1"
ATTRS{avoid_reset_quirk}=="0"
ATTRS{bConfigurationValue}=="1"
ATTRS{bDeviceClass}=="09"
ATTRS{bDeviceProtocol}=="01"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{bMaxPower}=="0mA"
ATTRS{bNumConfigurations}=="1"
ATTRS{bNumInterfaces}==" 1"
ATTRS{bcdDevice}=="0404"
ATTRS{bmAttributes}=="e0"
ATTRS{busnum}=="3"
ATTRS{configuration}==""
ATTRS{devnum}=="1"
ATTRS{devpath}=="0"
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"
ATTRS{interface_authorized_default}=="1"
ATTRS{ltm_capable}=="no"
ATTRS{manufacturer}=="Linux 4.4.0-81-generic xhci-hcd"
ATTRS{maxchild}=="10"
ATTRS{product}=="xHCI Host Controller"
ATTRS{quirks}=="0x0"
ATTRS{removable}=="unknown"
ATTRS{serial}=="0000:00:14.0"
ATTRS{speed}=="480"
ATTRS{urbnum}=="75"
ATTRS{version}==" 2.00"
looking at parent device '/devices/pci0000:00/0000:00:14.0':
KERNELS=="0000:00:14.0"
SUBSYSTEMS=="pci"
DRIVERS=="xhci_hcd"
ATTRS{broken_parity_status}=="0"
ATTRS{class}=="0x0c0330"
ATTRS{consistent_dma_mask_bits}=="64"
ATTRS{d3cold_allowed}=="1"
ATTRS{device}=="0x8c31"
ATTRS{dma_mask_bits}=="64"
ATTRS{driver_override}=="(null)"
ATTRS{enable}=="1"
ATTRS{irq}=="26"
ATTRS{local_cpulist}=="0-1"
ATTRS{local_cpus}=="3"
ATTRS{msi_bus}=="1"
ATTRS{numa_node}=="-1"
ATTRS{subsystem_device}=="0x8c31"
ATTRS{subsystem_vendor}=="0x8086"
ATTRS{vendor}=="0x8086"
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
然后,新開一個終端,插入同樣的設備,假如系統默認命名為/dev/ttyUSB1,同樣的方式查看詳細信息(這里不粘貼了)。
1、我們可以看到之前我們用於綁定串口的參數: ATTRS{idProduct},ATTRS{idVendor},當然現在他們已經不能滿足我們的需求了。
2、然后我們來對比他們存在差異的地方,我們看到在第三段有差異:KERNELS=="3-2:1.0",SUBSYSTEMS=="usb"(這只是/dev/ttyUSB0的)。
3、這一段最上面有:looking at parent device '/devices/pci0000:00/0000:00:14.0/usb3/3-2/3-2:1.0': 。在這里我們可以看到父級關系,在其中有:pci... ,既是這個usb硬件端口是連在pci總線上的(我想應該沒有人會沒事去拔pci上的設備吧^_^)
4、這里需要注意的一點是不要跨級(跨段:即looking at parent....不能混用)。KERNEL 所在的段可以與單個KERNELS所在的段的屬性結合,但是2個KERNELS所在的段屬性結合將會導致命令無效。
修改之前的文件/etc/udev/rules.d/10-local.rules,注釋掉我們前面定義的規則。
ACTION=="add",SUBSYSTEMS=="usb",KERNELS=="3-2:1.0",SYMLINK+="Mytest0"
#ACTION=="add",ATTRS{idVendor}=="067b",ATTRS{idProduct}=="2303",SYMLINK+="MyArduino"
#ACTION=="add",ATTRS{idVendor}=="2341",ATTRS{idProduct}=="0042",SYMLINK+="MyACM0"
然后重啟查看效果 :
$ ls -l /dev/M* lrwxrwxrwx 1 root root 7 6月 22 11:24 /dev/Mytest0 -> ttyUSB1
成功完成綁定。
b.串口設備權限
可插拔的串口設備所有者和屬組一般分別為root、dialout。因此普通用戶要使用串口設備讀寫是需要root權限的。
而且我們使用的ros-kinetic的robot_upstart包執行launch文件時使用的是安裝這個包的用戶的權限(無視屬組權限),而我們的ros-kinetic是在普通用戶下安裝的,所以robot_upstart包也應該裝在普通用戶下(官網也不建議ros安裝在root用戶下,至於裝root下會出什么問題試過才知道,不建議)。
因此,完成開機自啟並使用串口,我們需要讓我們登錄的普通用戶擁有串口讀寫權限。
這里我們還是用到udev管理串口設備文件。
新建一個文件,注意不要重名:
$ sudo gedit /etc/udev/rules.d/50-usb-serial.rules
在打開的文件中寫入:
KERNEL=="ttyUSB*",OWNER="<yourusername>",GROUP="root",MODE="0666" KERNEL=="ttyACM*",OWNER="<yourusername>",GROUP="root",MODE="0666"
######替換<yourusername>為你的用戶
根據需要,通過這兩行我們將/dev下的所有以ttyUSB及ttyACM開頭的設備文件的所有者和屬組分別修改為用戶和root。
重啟。
查看效果
$ ls -l /dev/ttyUSB* /dev/ttyACM*
應該會有類似的輸出:
crw-rw-rw- 1 <yourusername> root 166, 0 4月 11 10:32 /dev/ttyACM0
crw-rw-rw- 1 <yourusername> root 188, 0 4月 11 10:32 /dev/ttyUSB0
現在可以愉快的使用串口設備了。
為什么我們的普通用戶能訪問/dev/MyArduino和/dev/MyPL0呢?他們的所有者和屬組都是root呀!這里我認為在使用超鏈接文件時權限是由超鏈接指向的具體設備文件權限決定。