这些系统中的串口设备的地址和中断资源与硬件设备不匹配。可以通过两种方式来解决这个问题:
- 修改BIOS中的串口设置来匹配系统的设备属性
- 修改系统中的设备属性来匹配BIOS中的串口设置
第一种方式的好处是,只要修改一次,以后只要不更改系统,不主板电池耗尽,一劳永逸。但是并非所有的主机都提供这个功能,比如,我手上的这台机器的BIOS只能修改有限的选项,不是你想把中断改成什么都行。第二种方式的坏处是,每次系统启动都需要再次修改,但好处是可以自动化运行(就是执行一个命令),而且大部分主机都可以进入BIOS来查看硬件设置。比如这台工控主机:
根据目前了解的信息,我们可以使用任何Linux下的串口工具来配置系统中串口的地址和中断资源,如setserial。为了增加工作中的娱乐性,我使用python实现了一个根据串口硬件配置更改系统串口设备属性的程序。开始为了偷懒用了python,后来发现如果操作c的结构体,python真不在行。
程序的配置文件为Json格式,默认为:
{
"coms":
[
{"dev": "/dev/ttyS0", "type": "16550A", "addr": "0x03f8", "irq": 4 },
{"dev": "/dev/ttyS1", "type": "16550A", "addr": "0x02f8", "irq": 3 },
{"dev": "/dev/ttyS2", "type": "16550A", "addr": "0x0210", "irq": 11},
{"dev": "/dev/ttyS3", "type": "16550A", "addr": "0x0218", "irq": 11},
{"dev": "/dev/ttyS4", "type": "16550A", "addr": "0x0220", "irq": 11},
{"dev": "/dev/ttyS5", "type": "16550A", "addr": "0x0228", "irq": 11},
{"dev": "/dev/ttyS6", "type": "16550A", "addr": "0x0300", "irq": 10},
{"dev": "/dev/ttyS7", "type": "16550A", "addr": "0x0308", "irq": 10},
{"dev": "/dev/ttyS8", "type": "16550A", "addr": "0x0310", "irq": 10},
{"dev": "/dev/ttyS9", "type": "16550A", "addr": "0x0318", "irq": 10}
]
}
在32位系统下的程序实现如下(如果使用64位的系统,可能得修改其中对c结构体的pack和unpack):
#!/usr/bin/python3
import termios
import os
import fcntl
import json
import struct
import sys
import subprocess
import argparse
import datetime
uart_table = { "8250": 1, "16450":2, "16550":3, "16550A":4 }
coms_json = ' {"coms": [\
{"dev": "/dev/ttyS0", "type": "16550A", "addr": "0x03f8", "irq": 4 },\
{"dev": "/dev/ttyS1", "type": "16550A", "addr": "0x02f8", "irq": 3 },\
{"dev": "/dev/ttyS2", "type": "16550A", "addr": "0x0210", "irq": 11},\
{"dev": "/dev/ttyS3", "type": "16550A", "addr": "0x0218", "irq": 11},\
{"dev": "/dev/ttyS4", "type": "16550A", "addr": "0x0220", "irq": 11},\
{"dev": "/dev/ttyS5", "type": "16550A", "addr": "0x0228", "irq": 11},\
{"dev": "/dev/ttyS6", "type": "16550A", "addr": "0x0300", "irq": 10},\
{"dev": "/dev/ttyS7", "type": "16550A", "addr": "0x0308", "irq": 10},\
{"dev": "/dev/ttyS8", "type": "16550A", "addr": "0x0310", "irq": 10},\
{"dev": "/dev/ttyS9", "type": "16550A", "addr": "0x0318", "irq": 10}\
] }'
def find_com_dev(port, irq):
result = subprocess.run(['ls /dev/ttyS*'],shell=True, stdout=subprocess.PIPE)
devs = result.stdout.decode("utf-8").split('\n')
for dev in devs:
try:
fd = os.open(dev, os.O_RDWR | os.O_NONBLOCK)
termios_attr = bytearray([0]*56)
fcntl.ioctl(fd, termios.TIOCGSERIAL, termios_attr)
termios_attr_unpacked = struct.unpack('iiIiiiiiHbbiHHBHIL', termios_attr)
os.close(fd)
except FileNotFoundError:
continue
if termios_attr_unpacked[2] == port and termios_attr_unpacked[3] == irq:
return dev
return ""
def rename_com_dev(old_name, new_name):
os.rename(new_name, new_name+"11")
os.rename(old_name, new_name)
os.rename(new_name+"11", old_name)
def set_serial(opt):
try:
port_new = int(opt["addr"], 16)
type_new = uart_table[opt["type"]]
irq_new = opt["irq"]
fd = os.open(opt["dev"], os.O_RDWR | os.O_NONBLOCK)
termios_attr = bytearray([0]*56)
fcntl.ioctl(fd, termios.TIOCGSERIAL, termios_attr)
type_old = struct.unpack("i", termios_attr[0:4])
if type_new != type_old:
termios_attr[0:4] = type_new.to_bytes(4, sys.byteorder)
port_old = struct.unpack("I", termios_attr[8:12])
if port_new != port_old:
termios_attr[8:12] = port_new.to_bytes(4, sys.byteorder)
irq_old = struct.unpack("i", termios_attr[12:16])
if(irq_new != irq_old):
termios_attr[12:16] = irq_new.to_bytes(4, sys.byteorder)
fcntl.ioctl(fd, termios.TIOCSSERIAL, termios_attr)
os.close(fd)
return True
except OSError as e:
print(e.strerror)
except Exception as e:
print(str(e))
return False
def print_com_config(config):
print("dev:{3} type:{0} port:{1} irq:{2}"
.format(config["type"], config["addr"], config["irq"], config["dev"]))
if __name__ == "__main__":
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument("-c", "--config" , help="config file path")
arg_parser.add_argument("-v", "--version", action='store_true')
args = arg_parser.parse_args()
if args.version:
print("0.90")
sys.exit(0)
if args.config:
try:
config_file = os.open(sys.argv[1], os.O_RDONLY)
coms_json = os.read(config_file, 1024).decode("utf-8")
os.close(config_file)
except OSError as e:
print("{0} open failed because :\n{1}!".format(sys.argv[1], e.strerror))
print("setserial will use the default settings.")
try:
jstr = json.loads(coms_json)
print("you will config com ports with below configs:")
for com_config in jstr["coms"]:
print_com_config(com_config)
chose = input("enter Y/y to continue, others to exit:")
if chose != 'Y' and chose != 'y':
sys.exit(0)
for com_config in jstr["coms"]:
dev = find_com_dev(int(com_config["addr"], 16), int(com_config["irq"]))
if dev == com_config["dev"]:
print("com dev: {0} has correct settings!".format(dev))
continue
if not dev:
if set_serial(com_config):
print("success set: {}".format(com_config["dev"]))
else:
print("failed set: {}".format(com_config["dev"]))
else:
rename_com_dev(dev, com_config["dev"])
print("success rename:{} to {}".format(dev, com_config["dev"]))
except json.JSONDecodeError as e:
print(e.strerror)
except Exception as e:
print(str(e))