linux内核串口控制器注册以及收发

来源:互联网 发布:我的祖国钢琴谱 淘宝 编辑:程序博客网 时间:2024/05/21 08:54

我们看到Linux dev目录下面ttyS系列的串口设备的终端,现在用一个uart 控制器的程序来分析下怎么实现的


首先是定义了uart driver

static struct uart_driver XX_uart_driver = {
.owner = THIS_MODULE,
.driver_name = XX_UART_DEV_NAME, //名字,实际是uart
.dev_name = "ttyS", 
.nr = XX_UART_NUM,
.cons = XX_CONSOLE,
};

通过uart 核心层提供的接口先上层注册

uart_register_driver(&XX_uart_driver);

它原型是这样的

int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;


BUG_ON(drv->state);


/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;


normal = alloc_tty_driver(drv->nr); //由此可以看出来是通过tty设备来抽象的
if (!normal)
goto out_kfree;


drv->tty_driver = normal;


normal->driver_name= drv->driver_name;
normal->name = drv->dev_name;
normal->major = drv->major;
normal->minor_start= drv->minor;
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->subtype= SERIAL_TYPE_NORMAL;
normal->init_termios= tty_std_termios;
normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
normal->driver_state    = drv;
tty_set_operations(normal, &uart_ops);


/*
* Initialise the UART state(s).
*/
for (i = 0; i < drv->nr; i++) { //cpu的串口多少组串口
struct uart_state *state = drv->state + i;
struct tty_port *port = &state->port;


tty_port_init(port);
port->ops = &uart_port_ops;
port->close_delay     = HZ / 2;/* .5 seconds */
port->closing_wait    = 30 * HZ;/* 30 seconds */
}


retval = tty_register_driver(normal); //注册tty设备驱动
if (retval >= 0)
return retval;


put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}

注册相应的devices,由于一个CPU有多组串口

for (i=0; i<XX_UART_NUM; i++) {
pdata = &XX_uport_pdata[i];
if (!pdata->used)
continue;
platform_device_register(&XX_uport_device[i]);
}

XX_uport_device里面包含了很多resource

struct XX_uart_pdata {
unsigned int used;
unsigned int base;
unsigned int irq; //中断号
unsigned int max_ios;
unsigned int io_num;//io内存
};

流程走到了注册

static struct platform_driver XX_uport_platform_driver = {
.probe  = XX_uart_probe,
.remove = XX_uart_remove,
.driver = {
.name  = XX_UART_DEV_NAME,
.pm    = SERIAL_XX_PM_OPS,
.owner = THIS_MODULE,
},
};

static int __devinit XX_uart_probe(struct platform_device *pdev)
{
struct uart_port *port;
struct XX_uart_port *XX_uport;
int ret = -1;


port = &XX_uart_ports[pdev->id].port;
port->dev = &pdev->dev;
sw_uport = UART_TO_SPORT(port);
sw_uport->id = pdev->id;
sw_uport->ier = 0;
sw_uport->lcr = 0;
sw_uport->mcr = 0;
sw_uport->fcr = 0;
sw_uport->dll = 0;
sw_uport->dlh = 0;
snprintf(XX_uport->name, 16, XX_UART_DEV_NAME"%d", pdev->id);
pdev->dev.init_name = XX_uport->name;


SERIAL_DBG("uart.%d probe ... \n", pdev->id);


/* request system resource and init them */
ret = XX_uart_request_resource(XX_uport);
if (unlikely(ret)) {
SERIAL_MSG("uart%d error to get resource\n", pdev->id);
return -ENXIO;
}


port->uartclk = clk_get_rate(sw_uport->mclk);


port->type = PORT_XX;
port->flags = UPF_BOOT_AUTOCONF;
port->mapbase = XX_uport->pdata->base;
port->irq = XX_uport->pdata->irq;
platform_set_drvdata(pdev, port);



SERIAL_DBG("add uart%d port, port_type %d, uartclk %d\n",
pdev->id, port->type, port->uartclk);
return uart_add_one_port(&XX_uart_driver, port);
}

 
我们来看一下linux内核是如何相应收数据,和发送数据的。
首先看驱动向内核注册的串口操作集合
static struct uart_ops _uart_ops = {
.tx_empty = _uart_tx_empty,
.set_mctrl = _uart_set_mctrl,
.get_mctrl = _uart_get_mctrl,
.stop_tx = _uart_stop_tx,//停止发送
.start_tx = _uart_start_tx,//应用层最终通过tty的驱动发送数据会掉到这个接口
.stop_rx = _uart_stop_rx,//停止接受
.enable_ms = _uart_enable_ms,
.break_ctl = _uart_break_ctl,
.startup = _uart_startup, //这个接口通常会做一些初始化一个串口 比如 注册串口中断,打开中断。
.shutdown = _uart_shutdown,
.set_termios = _uart_set_termios,//设置串口的属性
.type = _uart_type,//串口类型
.release_port = _uart_release_port,
.request_port = _uart_request_port,
.config_port = _uart_config_port,
.verify_port = _uart_verify_port,
.pm = _uart_pm,
};

首先我们看一下发送:
发送时cpu主动发起的数据,相对比较简单,由应用程序直接调用到_uart_start_tx,把相应的数据写到相应的寄存器,通常数据发送完以后会产生中断,这个中断对应的了startup函数里面注册中断。

接收数据过程:
接收数据cpu是被动接收的,只能听过中断来检测数据的到来,当中断到来时,产生中断。void uart_insert_char(struct uart_port *port, unsigned int status,unsigned int overrun, unsigned int ch, unsigned int flag)接口把接收到数据推送到上层去。

1 0
原创粉丝点击