uart适配
最后更新于:2022-04-01 23:27:15
# UART适配
## linux 下适配
使用默认编译的安卓系统,ls /dev可以看到 ttyS0~3
尝试echo 'test' > /dev/ttyS0 可以在串口终端看到test字符,说明uart1就是ttyS0
荔枝派上除了默认的uart1是系统log输出口外,uart0与sdc0复用,不方便使用,就剩uart3可以使用。
linux下使用stty设置串口参数:
`stty -F /dev/ttyS1 ispeed 115200 ospeed 115200`
但是发现对ttyS0之外的串口操作会返回:`stty: /dev/ttyS1: Input/output error`
在linux的驱动目录下找到相关文件:tty/serial/8250_sunxi.c
~~~
struct platform_device sw_uart_dev[] = {
[0] = {.name = "sunxi-uart", .id = 0, .num_resources = ARRAY_SIZE(sw_uart_res[0]), .resource = &sw_uart_res[0][0], .dev = {}},
......
};
static int __init sw_serial_init(void)
{
int ret;
int i;
int used = 0;
char uart_para[16];
memset(sw_serial, 0, sizeof(sw_serial));
uart_used = 0;
for (i=0; iport_no = dev->id;
sport->pdev = dev;
ret = sw_serial_get_config(sport, dev->id);
if (ret) {
UART_MSG(KERN_ERR "Failed to get config information\n");
goto free_dev;
}
ret = sw_serial_get_resource(sport);
if (ret) {
UART_MSG(KERN_ERR "Failed to get resource\n");
goto free_dev;
}
platform_set_drvdata(dev, sport);
sport->port.irq = sport->irq;
sport->port.fifosize= 64;
sport->port.regshift= 2;
sport->port.iotype = UPIO_DWAPB32;
sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
sport->port.uartclk = sport->sclk;
sport->port.pm = sw_serial_pm;
sport->port.dev = &dev->dev;
sport->port.mapbase = sport->mmres->start;
sw_serial[sport->port_no] = serial8250_register_port(&sport->port); //注册端口
UART_MSG("serial probe %d, membase %p irq %d mapbase 0x%08x\n",
dev->id, sport->port.membase, sport->port.irq, sport->port.mapbase);
UART_MSG("sport->pdev is %x \n &sport->pdev is %x",sport->pdev,&sport->pdev);
UART_MSG("pdev.dev is %x \n &pdev.dev is %x",sport->pdev->dev,&sport->pdev->dev);
UART_MSG("dev.dev is %x \n &dev.dev is %x",dev->dev,&dev->dev);
return 0;
free_dev:
kfree(sport);
sport = NULL;
return ret;
}
~~~
.config
~~~
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_SUNXI=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
~~~
8250.c
~~~
#define UART_NR CONFIG_SERIAL_8250_NR_UA RTS
static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
int serial8250_register_port(struct uart_port *port)
{
struct uart_8250_port *uart;
int ret = -ENOSPC;
if (port->uartclk == 0)
return -EINVAL;
mutex_lock(&serial_mutex);
uart = serial8250_find_match_or_unused(port);
if (uart) {
uart_remove_one_port(&serial8250_reg, &uart->port);
uart->port.iobase = port->iobase;
uart->port.membase = port->membase;
uart->port.irq = port->irq;
uart->port.irqflags = port->irqflags;
uart->port.uartclk = port->uartclk;
uart->port.fifosize = port->fifosize;
uart->port.regshift = port->regshift;
uart->port.iotype = port->iotype;
uart->port.flags = port->flags | UPF_BOOT_AUTOCONF;
uart->port.mapbase = port->mapbase;
uart->port.private_data = port->private_data;
if (port->dev)
uart->port.dev = port->dev;
if (port->flags & UPF_FIXED_TYPE)
serial8250_init_fixed_type_port(uart, port->type);
set_io_from_upio(&uart->port);
/* Possibly override default I/O functions. */
uart->port.flags = port->flags | UPF_BOOT_AUTOCONF;
uart->port.mapbase = port->mapbase;
uart->port.private_data = port->private_data;
if (port->dev)
uart->port.dev = port->dev;
if (port->flags & UPF_FIXED_TYPE)
serial8250_init_fixed_type_port(uart, port->type);
set_io_from_upio(&uart->port);
/* Possibly override default I/O functions. */
if (port->serial_in)
uart->port.serial_in = port->serial_in;
if (port->serial_out)
uart->port.serial_out = port->serial_out;
/* Possibly override set_termios call */
if (port->set_termios)
uart->port.set_termios = port->set_termios;
if (port->pm)
uart->port.pm = port->pm;
if (serial8250_isa_config != NULL)
serial8250_isa_config(0, &uart->port,
&uart->capabilities);
ret = uart_add_one_port(&serial8250_reg, &uart->port);
if (ret == 0)
ret = uart->port.line;
}
mutex_unlock(&serial_mutex);
return ret;
}
~~~
serial_core.c
~~~
/**
* uart_add_one_port - attach a driver-defined port structure
* @drv: pointer to the uart low level driver structure for this port
* @uport: uart port structure to use for this port.
*
* This allows the driver to register its own uart_port structure
* with the core driver. The main purpose is to allow the low
* level uart drivers to expand uart_port, rather than having yet
* more levels of structures.
*/
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
struct uart_state *state;
struct tty_port *port;
int ret = 0;
struct device *tty_dev;
BUG_ON(in_interrupt());
if (uport->line >= drv->nr)
return -EINVAL;
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
goto out;
}
state->uart_port = uport;
state->pm_state = -1;
uport->cons = drv->cons;
uport->state = state;
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
uart_configure_port(drv, state, uport);
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this ports parameters.
*/
tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); //最终注册设备
if (likely(!IS_ERR(tty_dev))) {
device_init_wakeup(tty_dev, 1);
device_set_wakeup_enable(tty_dev, 0);
} else
printk(KERN_ERR "Cannot register tty device on line %d\n",
uport->line);
/*
* Ensure UPF_DEAD is not set.
*/
uport->flags &= ~UPF_DEAD;
out:
mutex_unlock(&port->mutex);
mutex_unlock(&port_mutex);
return ret;
}
~~~
tty_io.c
~~~
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
struct device *device)
{
char name[64];
dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
if (index >= driver->num) {
printk(KERN_ERR "Attempt to register invalid tty line number "
" (%d).\n", index);
return ERR_PTR(-EINVAL);
}
if (driver->type == TTY_DRIVER_TYPE_PTY)
pty_line_name(driver, index, name);
else
tty_line_name(driver, index, name);
return device_create(tty_class, device, dev, NULL, name);
}
EXPORT_SYMBOL(tty_register_device);
static void tty_line_name(struct tty_driver *driver, int index, char *p)
{
sprintf(p, "%s%d", driver->name, index + driver->name_base); //最终的ttySx
}
~~~
大致的驱动加载过程就是上面这样
下面看下log信息:
~~~
[ 0.158533] [uart]: used uart info.: 0x06
[ 0.158563] [uart]: this coming sw_serial_probe
[ 0.288179] [uart]: serial probe 1, membase (null) irq 2 mapbase 0x01c28400
[ 0.295378] [uart]: sport->pdev is c08c4a08
[ 0.295383] &sport->pdev is df04c2c8[uart]: pdev.dev is c08c4ad0
[ 0.303155] &pdev.dev is c08c5680[uart]: dev.dev is c08c4ad0
[ 0.309079] &dev.dev is c08c5680[uart]: this coming sw_serial_probe
[ 0.318097] [uart]: <3>Failed to get config information
[ 0.323344] sunxi-uart: probe of sunxi-uart.2 failed with error -1
~~~
可见uart1和uart3在获取fex时都被识别出来了,uart1成功probe,但是uart3“Failed to get config information”
uart3对应sw_uart_dev[2],即
~~~
ret = sw_serial_get_config(sport, dev->id);
if (ret) {
UART_MSG(KERN_ERR "Failed to get config information\n");
goto free_dev;
}
static int sw_serial_get_config(struct sw_serial_port *sport, u32 uart_id)
{
char uart_para[16] = {0};
int ret;
sprintf(uart_para, "uart_para%d", uart_id);
ret = script_parser_fetch(uart_para, "uart_port", &sport->port_no, sizeof(int));
if (ret)
return -1;
if (sport->port_no != uart_id)
return -1;
ret = script_parser_fetch(uart_para, "uart_type", &sport->pin_num, sizeof(int));
if (ret)
return -1;
return 0;
}
~~~
查看可知fex里的uart_port和uart_parax必须一一对应,所以就是fex里之前写错了,改正回来
这时查看ttyS1参数就有了:
~~~
busybox stty -F /dev/ttyS1
speed 9600 baud;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ;
eol2 = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W;
lnext = ^V; flush = ^O; min = 1; time = 0;
-brkint -imaxbel
~~~
设置下波特率,并测试
~~~
busybox stty -F /dev/ttyS1 ispeed 115200 ospeed 115200
echo 'test' > /dev/ttyS1
~~~
成功在uart3上收到数据~
';