linux内核态访问串口,linux内核中串口驱动注册过程(tty驱动)[转]

本文详细分析了Linux内核如何访问串口,从`sw_uart_init`开始,讲解了串口驱动的注册过程,包括`uart_register_driver`和`platform_device_register`等关键步骤。在注册过程中,涉及`tty_driver`结构体的初始化、设备号分配、文件操作函数关联等。然后,文章阐述了`uart_add_one_port`如何将端口与驱动对应,并通过`tty_register_device`注册设备。最后,深入探讨了串口的读写操作,包括`uart_start`、`sw_uart_handle_tx`和`sw_uart_handle_rx`等函数,解释了数据如何从硬件层面传输到用户空间的过程。
摘要由CSDN通过智能技术生成

原文转自:http://m.blog.csdn.net/blog/lushengchu2003/9368031

最近闲来无事情做,想到以前项目中遇到串口硬件流控制的问题,蓝牙串口控制返回错误,上层读写串口buffer溢出的问题等,也折腾了一阵子,虽然 最终证明与串口驱动无关,但是排查问题时候毫无疑问会查看串口驱动的相关代码,所以把串口驱动的流程过了一遍,方便以后再用到时拿来用。分析的是全志代码 A20。直接开始代码分析吧。

串口驱动代码在linux-3.3/drivers/tty/serial目录下,全志把自己平台相关的代码集中到了一个文件中,叫sw_uart.c,那就从它的__init开始了:

static int __init sw_uart_init(void)

{

int ret;

u32 i;

struct sw_uart_pdata *pdata;

SERIAL_MSG("driver initializied\n");

ret = sw_uart_get_devinfo();

if (unlikely(ret))

return ret;

ret = uart_register_driver(&sw_uart_driver);

if (unlikely(ret)) {

SERIAL_MSG("driver initializied\n");

return ret;

}

for (i=0; i

pdata = &sw_uport_pdata[i];

if (pdata->used)

platform_device_register(&sw_uport_device[i]);

}

return platform_driver_register(&sw_uport_platform_driver);

}

sw_uart_get_devinfo是解析全志的sys配置脚本中的串口配置,一共有八个串口,要用到那个直接在sys脚本中设置1就行了,这个用过全志平台的都知道

接着sw_uart_driver结构体定义如下:

static struct uart_driver sw_uart_driver = {

.owner = THIS_MODULE,

.driver_name = "sw_serial",

.dev_name = "ttyS",

.nr = SW_UART_NR,

.cons = SW_CONSOLE,

};

这里SW_UART_NR为8,ttyS就是将要显示在/dev/目录下的名字了,从0~7

接着看uart注册函数,顾名思义是把全志自己平台的串口注册到串口核心serial_core中去:

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);

if (!normal)

goto out_kfree;

drv->tty_driver = normal;

normal->owner = drv->owner;

normal->driver_name = drv->driver_name;

normal->name = drv->dev_name;//名字为ttyS

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++) {

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);

if (retval >= 0)

return retval;

put_tty_driver(normal);

out_kfree:

kfree(drv->state);

out:

return -ENOMEM;

}

先列出来吧,以用到时候再回来看,这里先创建了NR个state, 并为每个state做一些初始化,但是这些state还没有和端口(uart_port)对应起来;初始化完port口后,调用tty_register_driver:

int tty_register_driver(struct tty_driver *driver)

{

int error;

int i;

dev_t dev;

void **p = NULL;

struct device *d;

if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {

p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);

if (!p)

return -ENOMEM;

}

if (!driver->major) {

error = alloc_chrdev_region(&dev, driver->minor_start,

driver->num, driver->name);

if (!error) {

driver->major = MAJOR(dev);

driver->minor_start = MINOR(dev);

}

} else {

dev = MKDEV(driver->major, driver->minor_start);

error = register_chrdev_region(dev, driver->num, driver->name);

}

if (error < 0) {

kfree(p);

return error;

}

if (p) {

driver->ttys = (struct tty_struct **)p;

driver->termios = (struct ktermios **)(p + driver->num);

} else {

driver->ttys = NULL;

driver->termios = NULL;

}

cdev_init(&driver->cdev, &tty_fops);

driver->cdev.owner = driver->owner;

error = cdev_add(&driver->cdev, dev, driver->num);

if (error) {

unregister_chrdev_region(dev, driver->num);

driver->ttys = NULL;

driver->termios = NULL;

kfree(p);

return error;

}

mutex_lock(&tty_mutex);

list_add(&driver->tty_drivers, &tty_drivers);

mutex_unlock(&tty_mutex);

if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {

for (i = 0; i < driver->num; i++) {

d = tty_register_device(driver, i, NULL);

if (IS_ERR(d)) {

error = PTR_ERR(d);

goto err;

}

}

}

proc_tty_register_driver(driver);

driver->flags |= TTY_DRIVER_INSTALLED;

return 0;

这里alloc_chrdev_region是动态分配了主从设备号,接着cdev_init,它file_operations结构提和它关联了起来,以后我们open /dev/ttyS节点时候会调用他的open函数,先看看这个结构体:

static const struct file_operations tty_fops = {

.llseek = no_llseek,

.read = tty_read,

.write = tty_write,

.poll = tty_poll,

.unlocked_ioctl = tty_ioctl,

.compat_ioctl = tty_compat_ioctl,

.open = tty_open,

.release = tty_release,

.fasync = tty_fasync,

};

接着cdev_add就把file_operations和设备号关联起来了,我们现在还没有创建设备节点,不过看到有为

driver->major,driver->minor_start赋值的,后面创建节点就是用这两个主从设备号。接着list_add把

这个tty_driver添加到链表中来,方便后续查找。

接着if语句判断TTY_DRIVER_DYNAMIC_DEV标志,我们前面有赋值:

normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;

所以这里的if条件不成立的,最后创建proc下的节点,就返回了。按我的理解,tty_register_driver是注册了一个tty的驱

动,这个驱动有了逻辑能力,但是这个时候这个驱动还没有对应任何设

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值