• 二十六【uart】Uart驱动架构分析


    一、前言

      在linux中,serial也对应着终端你,通常被称为串口终端。在shell上,我们看到得/dev/ttyS*就是串口终端所对应的设备节点。

      uart(Universal Asynchronous Receicer and transmitter)即为“通用异步收发器”。它是串口设备驱动的封装层。

    二、Uart驱动架构概貌

       从上图可以看到,uart设备是继tty_drivers的又一层封装。实际上uart_driver就是对应tty_driver.在它的操作函数中,将操作转入uart_port.

    • 在写操作的时候,先将数据放入一个叫做circ_buf的环形缓存区。然后uart_port从缓存区中取数据,将其写入到串口设备中。
    • 当uart_port从serial设备接收到数据时,会将设备放入对应line discipline的缓存区中。这样,用户在编写串口驱动的时候,只先要注册一个uart_driver.它的主要作用是定义设备节点号。然后将对设备的各项操作封装在uart_port.驱动工程师没必要关心上层的流程,只需按硬件规范将uart_port中的接口函数完成就可以了。

    三、uart驱动代码分析

    1、uart驱动注册函数

    drivers/tty/serial/serial_core.c

    /**
     *	uart_register_driver - register a driver with the uart core layer
     *	@drv: low level driver structure
     *
     *	Register a uart driver with the core driver.  We in turn register
     *	with the tty layer, and initialise the core driver per-port state.
     *
     *	We have a proc file in /proc/tty/driver which is named after the
     *	normal driver.
     *
     *	drv->port should be NULL, and the per-port structures should be
     *	registered using uart_add_one_port after this call has succeeded.
     */
    int uart_register_driver(struct uart_driver *drv)
    {
    	struct tty_driver *normal;
    	int i, retval = -ENOMEM;
    
    	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 = kcalloc(drv->nr, sizeof(struct uart_state), GFP_KERNEL);
    	if (!drv->state)
    		goto out;
    
    	normal = alloc_tty_driver(drv->nr);
    	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 = B4000000 | CS8 | CREAD | HUPCL | CLOCAL;
    	normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 4000000;   //默认的波特率
    	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;
    	}
               //驱动注册
    	retval = tty_register_driver(normal);
    	if (retval >= 0)
    		return retval;
    
    	for (i = 0; i < drv->nr; i++)
    		tty_port_destroy(&drv->state[i].port);
    	put_tty_driver(normal);
    out_kfree:
    	kfree(drv->state);
    out:
    	return retval;
    }
    

    2、添加端口函数uart_add_one_port()

    在uart_driver增加一个port

    /**
     *	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;
    	int num_groups;
    
    	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;
    	}
    
    	/* Link the port to the driver state table and vice versa */
    	atomic_set(&state->refcount, 1);
    	init_waitqueue_head(&state->remove_wait);
    	state->uart_port = uport;
    	uport->state = state;
    
    	state->pm_state = UART_PM_STATE_UNDEFINED;
    	uport->cons = drv->cons;
    	uport->minor = drv->tty_driver->minor_start + uport->line;
    	uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name,
    				drv->tty_driver->name_base + uport->line);
    	if (!uport->name) {
    		ret = -ENOMEM;
    		goto out;
    	}
    
    	/*
    	 * 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);
    	}
    	if (uport->cons && uport->dev)
    		of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
    
    	tty_port_link_device(port, drv->tty_driver, uport->line);
    	uart_configure_port(drv, state, uport);
    
    	port->console = uart_console(uport);
    
    	num_groups = 2;
    	if (uport->attr_group)
    		num_groups++;
    
    	uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
    				    GFP_KERNEL);
    	if (!uport->tty_groups) {
    		ret = -ENOMEM;
    		goto out;
    	}
    	uport->tty_groups[0] = &tty_dev_attr_group;
    	if (uport->attr_group)
    		uport->tty_groups[1] = uport->attr_group;
    
    	/*
    	 * Register the port whether it's detected or not.  This allows
    	 * setserial to be used to alter this port's parameters.
    	 */
    	tty_dev = tty_port_register_device_attr_serdev(port, drv->tty_driver,
    			uport->line, uport->dev, port, uport->tty_groups);
    	if (!IS_ERR(tty_dev)) {
    		device_set_wakeup_capable(tty_dev, 1);
    	} else {
    		dev_err(uport->dev, "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;
    }
    

    首先这个函数不能在中断环境中使用.Uart_port-> line就是对uart设备文件序号.它对应的也就是uart_driver-> state数组中的uart_port->line项.
    它主要初始化对应uart_driver-> state项.接着调用uart_configure_port()进行port的自动配置.然后注册 tty_device.如果用户空间运行了udev或者已经配置好了hotplug.就会在/ dev下自动生成设备文件了.

    3、串口的文件操作集合

    static const struct tty_operations uart_ops = {
    	.install	= uart_install,
    	.open		= uart_open,
    	.close		= uart_close,
    	.write		= uart_write,
    	.put_char	= uart_put_char,  //单字符写入
    	.flush_chars	= uart_flush_chars,
    	.write_room	= uart_write_room,     //检测缓冲区的剩余空间
    	.chars_in_buffer= uart_chars_in_buffer,   //检测包含数据缓冲区的数量
    	.flush_buffer	= uart_flush_buffer,   //刷新缓冲区并丢弃其中的数据
    	.ioctl		= uart_ioctl,
    	.throttle	= uart_throttle,
    	.unthrottle	= uart_unthrottle,
    	.send_xchar	= uart_send_xchar,
    	.set_termios	= uart_set_termios,
    	.set_ldisc	= uart_set_ldisc,   //设置线路规程的函数
    	.stop		= uart_stop,
    	.start		= uart_start,
    	.hangup		= uart_hangup,
    	.break_ctl	= uart_break_ctl,
    	.wait_until_sent= uart_wait_until_sent,  //用来向硬件发送数据
    #ifdef CONFIG_PROC_FS
    	.proc_show	= uart_proc_show,
    #endif
    	.tiocmget	= uart_tiocmget,    //获取特定tty设备当前的线路设置
    	.tiocmset	= uart_tiocmset,    //设置特定tty设备当前的线路
    	.set_serial	= uart_set_info_user,
    	.get_serial	= uart_get_info_user,
    	.get_icount	= uart_get_icount,
    #ifdef CONFIG_CONSOLE_POLL
    	.poll_init	= uart_poll_init,
    	.poll_get_char	= uart_poll_get_char,
    	.poll_put_char	= uart_poll_put_char,
    #endif
    };
    

      

     

      

      

  • 相关阅读:
    JMeter性能测试中控制业务比例
    软件版本命名规范
    软件测试方法——静态测试与动态测试
    安装BugFree 3.0.4时出现的问题
    Linux下给mysql创建用户分配权限
    LoadRunner 测试脚本
    linux dd命令详解
    Linux查看CPU和内存使用情况
    Error:java: 无效的源发行版: 10
    rf接口自动化之结果校验
  • 原文地址:https://www.cnblogs.com/yuanqiangfei/p/15850146.html
Copyright © 2020-2023  润新知