• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

tty驱动初步学习

武飞扬头像
zzsddre
帮助1

一、linux tty驱动框架

本人是linux驱动初学者,最近在初步学习uart驱动,在这记录下来自己的理解
linux3.10
soc:君正x1000e

四位大佬写的很好
https://blog.csdn.net/cosmoslhf/article/details/16945009
https://blog.csdn.net/lizuobin2/article/details/51801183
https://blog.csdn.net/Luckiers/article/details/123577836
https://blog.csdn.net/mike8825/article/details/107598833

在Linux kernel中,tty驱动不像于spi,iic等那么架构简单,它是一个庞大的系统,它的框架大体如下图一。我们作为普通的驱动开发移植人员,不会从零写tty驱动,一般都是厂家根据现有的tty驱动和自家芯片修改,拿到板子按照厂家的配置,串口应该使能直接使用的。但是开发的过程中也有可能需要用到串口,一般会修改serial驱动,这样我们不会动tty_core层。

Linux tty子系统包含:tty核心(tty_core),tty线路规程(tty_line_discipine)和tty驱动(tty_driver)。tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱动。
图一学新通

二、uart驱动数据架构

1、主要用到的数据架构图二

图二
学新通
uart_driver描述uart设备,其他下面有2个重要的结构体uart_state(用于描述底层硬件信息),tty_drvier(tty层驱动)。
uart_driver结构体

struct uart_driver {
	struct module		*owner;
	const char		*driver_name;//串口驱动名字
	const char		*dev_name;//设备名字
	int			 major;
	int			 minor;
	int			 nr;
	struct console		*cons;

	/*
		有两个成员未被赋值,对于tty_driver 代表的是上层,它会在 register_uart_driver 中的过程中赋值,
		而uart_state 则代表下层,uart_state 也会在register_uart_driver 的过程中分配空间,
		但是它里面真正设置硬件相关的东西是 uart_state->uart_port ,
		这个uart_port 是需要我们从其它地方调用 uart_add_one_port 来添加的。

	 */
	struct uart_state	*state;//底层指定硬件参数
	struct tty_driver	*tty_driver;//tty层
};
学新通

一般来说一个芯片具有多个串口,比如君正x1000e具有3个串口,那uart_state应该有多个,每个保存一个串口信息。
tty_prot是TTY device的一种抽象

circ_buf是一个发送缓存,在写数据时,当tty层调用驱动提供的写函数时,数据会首先进入circ_buf的环形缓存区,然后由uart_port从缓存区中取数据,将其写入到串口设备中。
当uart_port从串口设备接收到数据时,它会直接将数据放入tty的缓存中(tty缓存属于tty_port),进而放入对应的line discipline的缓存区中。
学新通
uart_state结构体
uart_state中最重要的结构体成员uart_port
uart_state->uart_port(在这保存硬件串口信息)

truct uart_port {
	spinlock_t		lock;			/* port lock */
	unsigned long		iobase;			/* io基地址 */
	unsigned char __iomem	*membase;		/*内存基地址 */
	unsigned int		(*serial_in)(struct uart_port *, int);//串口接受函数指针
	void			(*serial_out)(struct uart_port *, int, int);//串口发送函数指针
	void			(*set_termios)(struct uart_port *,
				               struct ktermios *new,
				               struct ktermios *old);
	int			(*handle_irq)(struct uart_port *);
	void			(*pm)(struct uart_port *, unsigned int state,
				      unsigned int old);
	void			(*handle_break)(struct uart_port *);
	unsigned int		irq;			/* irq number */
	unsigned long		irqflags;		/* irq flags  */
	unsigned int		uartclk;		/* base uart clock */
	unsigned int		fifosize;		/* tx fifo size */
	unsigned char		x_char;			/* xon/xoff char */
	unsigned char		regshift;		/* reg offset shift */
	unsigned char		iotype;			/* io访问方式 */
	unsigned char		unused1;
....
	unsigned int		mctrl;			/* 当前控制模式*/
	unsigned int		timeout;		/* character-based timeout */
	unsigned int		type;			/* port type */
	const struct uart_ops	*ops;
	unsigned int		custom_divisor;
	unsigned int		line;			/* port index */
	resource_size_t		mapbase;		/* for ioremap */
	struct device		*dev;			/* parent device */
	unsigned char		hub6;			/* this should be in the 8250 driver */
	unsigned char		suspended;
	unsigned char		irq_wake;
	unsigned char		unused[2];
	void			*private_data;		/* generic platform data pointer */
};
学新通

tty_port结构体
保存着tty层驱动信息


struct tty_driver {
	int	magic;		/* magic number for this structure */
	struct kref kref;	/* Reference management */
	struct cdev *cdevs;
	struct module	*owner;
	const char	*driver_name;
	const char	*name;
	int	name_base;	/* offset of printed name */
	int	major;		/* major device number */
	int	minor_start;	/* start of minor device number */
	unsigned int	num;	/* number of devices allocated */
	short	type;		/* type of tty driver */
	short	subtype;	/* subtype of tty driver */
	struct ktermios init_termios; /* Initial termios */
	unsigned long	flags;		/* tty driver flags */
	struct proc_dir_entry *proc_entry; /* /proc fs entry */
	struct tty_driver *other; /* only used for the PTY driver */

	/*
	 * Pointer to the tty data structures
	 */
	struct tty_struct **ttys;
	struct tty_port **ports;
	struct ktermios **termios;
	void *driver_state;

	/*
	 * Driver methods
	 */

	const struct tty_operations *ops;
	struct list_head tty_drivers;
};

学新通

三、uart驱动编写步骤

uart它是linux在tty的基础上又做了一层封装,得益于该封装层,可以比较容易的编写新的串口驱动程序。
只需要2大步
(1)uart_register_driver函数注册一个串口设备
(2)platform_driver_register注册一个平台设备(最后执行uart_add_one_port)

驱动大概框架


static int serial_jz47xx_startup(struct uart_port *port)
{
	/*request_irq注册驱动*/
	retval = request_irq(up->port.irq, serial_jz47xx_irq, 0, up->name, up);//request irq
	if (retval)
		return retval;

	/*清理寄存器*/

	/*初始化串口*/

	/*使能中断*/
}
/*操作集合*/
struct uart_ops serial_jz47xx_pops = {
	/*回调函数*/
	.startup	= serial_jz47xx_startup,//start endport
	....
};

static struct uart_driver serial_jz47xx_reg = {
	.owner		= THIS_MODULE,		
	.driver_name	= "JZ47XX serial",  //串口驱动名字
	.dev_name	= "ttyS",     //串口设备名字
	.major		= TTY_MAJOR,    //主设备号
	.minor		= 64,
	.nr		= PORT_NR,/* 该uart_driver支持的串口个数(最大) */
	.cons		= JZ47XX_CONSOLE,	/* 其对应的console.若该uart_driver支持serial console,否则为NULL */
};


static int serial_jz47xx_probe(struct platform_device *pdev)
{
	/*获取平台资源*/
	/*申请内存空间*/
	/*开启时钟*/
	/*初始化port结构体(和硬件有关的)*/
	/*开启DMA*/
	/*初始化栈*/
	/*uart_add_one_port*/
}

static struct platform_driver serial_jz47xx_driver = {
	.probe          = serial_jz47xx_probe,
	.remove         = serial_jz47xx_remove,
	.driver		= {
		.name	= "jz-uart",
		.owner	= THIS_MODULE,
	},
};

/*注册uart设备和平台设备*/
int __init serial_jz47xx_init(void)
{
	int ret;
	ret = uart_register_driver(&serial_jz47xx_reg);
	ret = platform_driver_register(&serial_jz47xx_driver)
	return ret;
}

void __exit serial_jz47xx_exit(void)
{
	platform_driver_unregister(&serial_jz47xx_driver);
	uart_unregister_driver(&serial_jz47xx_reg);
}

module_init(serial_jz47xx_init);
module_exit(serial_jz47xx_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:jz47xx-uart");

学新通

驱动分析

uart设备依托在platform平台设备上实现,主要在platform设备的probe函数中初始化uart_prot结构体

  1. 调用uart_register_driver注册一个uart驱动
 uart_register_driver(&serial_jz47xx_reg);
  1. platform_driver_register平台设备注册
platform_driver_register(&serial_jz47xx_driver);
  1. 在platform设备的probe函数中主要做了4件事
    获取平台资源
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取io资源
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获取中断资源
dma_filter = platform_get_resource(pdev, IORESOURCE_DMA, 0);//获取DMA资源
初始化uart_port结构体
up->port.type = PORT_JZ47XX;
	up->port.iotype = UPIO_MEM;
	up->port.mapbase = mmres->start;
	up->port.irq = irqres->start;
	up->port.fifosize = 64;
	up->port.ops = &serial_jz47xx_pops;
	up->port.line = pdev->id;
	up->port.dev = &pdev->dev;
	up->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
	up->port.uartclk = clk_get_rate(up->clk);
	up->port.membase = ioremap(mmres->start, mmres->end - mmres->start   1);
	if (!up->port.membase) {
		ret = -ENOMEM;
		goto err_clk;
	}
开时钟和DMA
clk_enable(up->clk);//开启时钟
serial_jz47xx_dma_init(up);//dma初始化
最后uart_add_one_port
uart_add_one_port(&serial_jz47xx_reg, &up->port);//根据uart_port的信息配置串口,并将其注册到系统中

再看ops操作集合

struct uart_ops serial_jz47xx_pops = {
	.tx_empty	= serial_jz47xx_tx_empty,//TX Buffer empty
	.set_mctrl	= serial_jz47xx_set_mctrl,//set Modem control
	.get_mctrl	= serial_jz47xx_get_mctrl,//get Modem control
	.stop_tx	= serial_jz47xx_stop_tx,//stop TX
	.start_tx	= serial_jz47xx_start_tx,//start tx
	.stop_rx	= serial_jz47xx_stop_rx,//stop rx
	.enable_ms	= serial_jz47xx_enable_ms,//modem status enable
	.break_ctl	= serial_jz47xx_break_ctl,//
	.startup	= serial_jz47xx_startup,//start endport
	.shutdown	= serial_jz47xx_shutdown,//shutdown endport
	.set_termios	= serial_jz47xx_set_termios,//change para of endport
	.type		= serial_jz47xx_type,//
	.release_port	= serial_jz47xx_release_port,//release port I/O
	.request_port	= serial_jz47xx_request_port,//
	.config_port	= serial_jz47xx_config_port,//
	.verify_port	= serial_jz47xx_verify_port,//
};

学新通

这些回调函数一般是厂家写的???
看startup回调函数(主要是申请中断)

static int serial_jz47xx_startup(struct uart_port *port)
{
	struct uart_jz47xx_port *up = (struct uart_jz47xx_port *)port;
	unsigned long flags;
	int retval;
	up->port.uartclk = clk_get_rate(up->clk);//get clk
	//1、先判断当前dma是否在用,如果在用就添加到软中断等待处理
	if(up->use_dma)
		tasklet_schedule(&up->tasklet_dma_rx);
	/*
	 * Allocate the IRQ
	 */
	 //2、申请中断,串口接收和发送都是以中断为入口的吧?
	retval = request_irq(up->port.irq, serial_jz47xx_irq, 0, up->name, up);//request irq
	if (retval)
		return retval;

	/*
	 * Clear the FIFO buffers and disable them.
	 * (they will be reenabled in set_termios())
	 */
	 //3、清理FIFO
	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO
		   | UART_FCR_CLEAR_RCVR
		   | UART_FCR_CLEAR_XMIT
		   | UART_FCR_UME);
	serial_out(up, UART_FCR, 0);

	/*
	 * Clear the interrupt registers.
	 */
	 //4、清理寄存器
	(void) serial_in(up, UART_LSR);
	(void) serial_in(up, UART_RX);
	(void) serial_in(up, UART_IIR);
	(void) serial_in(up, UART_MSR);

	/*
	 * Now, initialize the UART
	 */
	 //5、初始化串口
	serial_out(up, UART_LCR, UART_LCR_WLEN8);
	serial_out(up, UART_ISR, 0);

	spin_lock_irqsave(&up->port.lock, flags);
	up->port.mctrl |= TIOCM_OUT2;
	//6、设置模式?
	serial_jz47xx_set_mctrl(&up->port, up->port.mctrl);
	spin_unlock_irqrestore(&up->port.lock, flags);

	/*
	 * Finally, enable interrupts.  Note: Modem status interrupts
	 * are set via set_termos(), which will be occurring imminently
	 * anyway, so we don't enable them here.
	 */
	 //7、开DMA,开中断
	if(up->use_dma)
		up->ier = UART_IER_RLSI | UART_IER_RTOIE;
	else
		up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE;
	serial_out(up, UART_IER, up->ier);

	/*
	 * And clear the interrupt registers again for luck.
	 */
	(void) serial_in(up, UART_LSR);
	(void) serial_in(up, UART_RX);
	(void) serial_in(up, UART_IIR);
	(void) serial_in(up, UART_MSR);

	return 0;
}

学新通

1、先判断当前dma是否在用,如果在用就添加到软中断等待处理
2、申请中断,串口接收和发送都是以中断为入口的吧?
3、清理FIFO
4、清理寄存器
5、初始化串口
6、设置模式?
7、开DMA,开中断

进一步了解uart_register_driver和uart_add_one_port的实现过程

uart_register_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.
	 */
	//1、为多个串口分配缓存内存空间,drv->nr是串口数量
	drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
	if (!drv->state)
		goto out;
	//2、申请tty设备
	normal = alloc_tty_driver(drv->nr);
	if (!normal)
		goto out_kfree;

	drv->tty_driver = normal;

	//3、tty设备初始化
	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;
	//4、注册tty函数操作集合
	tty_set_operations(normal, &uart_ops);

	/*
	 * //5、初始化所有UART和TTY
	 */
	for (i = 0; i < drv->nr; i  ) {
		struct uart_state *state = drv->state   i;
		struct tty_port *port = &state->port;

		//6、tty_port保存着各个tty_drvice的共性属性,tty_port_init初始化缓冲区,互彻锁
		tty_port_init(port);
		port->ops = &uart_port_ops;
		port->close_delay     = HZ / 2;	/* .5 seconds */
		port->closing_wait    = 30 * HZ;/* 30 seconds */
	}

	//7、注册tty设备
	retval = tty_register_driver(normal);
	if (retval >= 0)
		return retval;
	//如果到了这步,那么说明上面tty_register_driver失败,那么需要释放资源
	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 -ENOMEM;
}

学新通

tty层的操作集


static const struct tty_operations uart_ops = {
	.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,	// 当termios设置被改变时又tty核心调用
	.set_ldisc	= uart_set_ldisc,		// 设置线路规程函数
	.stop		= uart_stop,	
	.start		= uart_start,
	.hangup		= uart_hangup,		// 挂起函数,当驱动挂起tty设备时调用
	.break_ctl	= uart_break_ctl,	// 线路中断控制函数
	.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
	.proc_fops	= &uart_proc_fops,
#endif
	.tiocmget	= uart_tiocmget,	// 获得当前tty的线路规程的设置
	.tiocmset	= uart_tiocmset,	// 设置当前tty线路规程的设置
#ifdef CONFIG_CONSOLE_POLL
	.poll_init	= uart_poll_init,
	.poll_get_char	= uart_poll_get_char,
	.poll_put_char	= uart_poll_put_char,
#endif
};

学新通

tty_register_driver驱动注册流程(也就是字符设备注册)

int tty_register_driver(struct tty_driver *driver)
{
	int error;
	int i;
	dev_t dev;
	void **p = NULL;
 
	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
	}
	
	/* 如果没有主设备号则申请 */
	if (!driver->major) {
		error = alloc_chrdev_region(&dev, driver->minor_start,
						driver->num, driver->name);
	} else {
		dev = MKDEV(driver->major, driver->minor_start);
		error = register_chrdev_region(dev, driver->num, driver->name);
	}
 
	if (p) { /* 为线路规程和termios分配空间 */
		driver->ttys = (struct tty_struct **)p;
		driver->termios = (struct ktermios **)(p   driver->num);
	} else {
		driver->ttys = NULL;
		driver->termios = NULL;
	}
 
	/* 创建字符设备,使用 tty_fops */
	cdev_init(&driver->cdev, &tty_fops);
	driver->cdev.owner = driver->owner;
	error = cdev_add(&driver->cdev, dev, driver->num);
 
	mutex_lock(&tty_mutex);
	
	/* 将该 driver->tty_drivers 添加到全局链表 tty_drivers */
	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  )
		    tty_register_device(driver, i, NULL);
	}
	
	/* 向proc文件系统注册driver */
	proc_tty_register_driver(driver);
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;
}

学新通

四、读写操作调用流程


 
static const struct file_operations tty_fops = {
	.open		= tty_open,
	.read		= tty_read,
	.write		= tty_write,
};
 
static struct tty_ldisc_ops n_tty_ops = {
        .open            = n_tty_open,
        .read            = n_tty_read,
        .write           = n_tty_write,
};
 
static const struct tty_operations uart_ops = {
	.open		= uart_open,
	.write		= uart_write,
};
 
static const struct uart_ops serial_sprd_ops = {
	.stop_tx = sprd_stop_tx,
	.start_tx = sprd_start_tx,
	.stop_rx = sprd_stop_rx,
	.set_termios = sprd_set_termios,
 
};
 
//open
static int tty_open(struct inode *inode, struct file *filp)
{
	struct tty_struct *tty;
	tty = tty_open_by_driver(device, inode, filp);
	retval = tty->ops->open(tty, filp);----->
}
 
//寻找driver,比如有usb转串口driver,cpu的uart driver
static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,struct file *filp)
{
	driver = tty_lookup_driver(device, filp, &index);
			-->tty = tty_init_dev(driver, index);
				tty = alloc_tty_struct(driver, idx);---->tty_ldisc_init(tty);
									tty->ops = driver->ops;
				retval = tty_driver_install_tty(driver, tty);
				retval = tty_ldisc_setup(tty, tty->link);
 
}
 
static struct uart_driver sprd_uart_driver = {
	.owner = THIS_MODULE,
	.driver_name = "sprd_serial",
	.dev_name = SPRD_TTY_NAME,
	.major = 0,
	.minor = 0,
	.nr = UART_NR_MAX,
	.cons = SPRD_CONSOLE,
};
 
static int uart_open(struct tty_struct *tty, struct file *filp)
{
 
	retval = uart_startup(tty, state, 0);--->retval = uport->ops->startup(uport);
		uart_change_speed(tty, state, NULL);--->uport->ops->set_termios(uport, termios, old_termios);
 
}
 
static const struct tty_operations uart_ops = {
	.open		= uart_open,
	.write		= uart_write,
 
};
int uart_register_driver(struct uart_driver *drv)
{
	tty_set_operations(normal, &uart_ops);--->driver->ops = op;
}
串口发送过程

static ssize_t tty_write(struct file *file, const char __user *buf,size_t count, loff_t *ppos)
{
		ret = do_tty_write(ld->ops->write, tty, file, buf, count);
}
 
static inline ssize_t do_tty_write(ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),struct tty_struct *tty,struct file *file,const char __user *buf,size_t count)
{
	ret = write(tty, file, tty->write_buf, size);--->n_tty_write
}
 
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr)
{
		while (nr > 0) {
			c = tty->ops->write(tty, b, nr);
 
		}
}
 
static int uart_write(struct tty_struct *tty,const unsigned char *buf, int count)
{
	while (1) {
		c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
		if (count < c)
			c = count;
		if (c <= 0)
			break;
		memcpy(circ->buf   circ->head, buf, c);
		circ->head = (circ->head   c) & (UART_XMIT_SIZE - 1);
		buf  = c;
		count -= c;
		ret  = c;
	}
 
	__uart_start(tty);
 
}
 
static void __uart_start(struct tty_struct *tty)
{
		port->ops->start_tx(port);
}
 
static void sprd_start_tx(struct uart_port *port)
{
	unsigned int ien;
 
	ien = serial_in(port, SPRD_IEN);
	if (!(ien & SPRD_IEN_TX_EMPTY)) {
		ien |= SPRD_IEN_TX_EMPTY;
		serial_out(port, SPRD_IEN, ien);
	}
}
//准备好了,把数据发过去
static irqreturn_t sprd_handle_irq(int irq, void *dev_id)
{
	if (ims & SPRD_IMSR_TX_FIFO_EMPTY)
		sprd_tx(port);
}
 
static inline void sprd_tx(struct uart_port *port)
{
	do {
		serial_out(port, SPRD_TXD, xmit->buf[xmit->tail]);
		xmit->tail = (xmit->tail   1) & (UART_XMIT_SIZE - 1);
		port->icount.tx  ;
		if (uart_circ_empty(xmit))
			break;
	} while (--count > 0);
}
串口接收过程
//中断来了,开始读数据,并存储 

static irqreturn_t sprd_handle_irq(int irq, void *dev_id){
	ims = serial_in(port, SPRD_IMSR);
	if (ims & (SPRD_IMSR_RX_FIFO_FULL |
		SPRD_IMSR_BREAK_DETECT | SPRD_IMSR_TIMEOUT))
		sprd_rx(port);	
}
 
static inline void sprd_rx(struct uart_port *port)
{
	struct tty_port *tty = &port->state->port;
	unsigned int ch, flag, lsr, max_count = SPRD_TIMEOUT;
 
	while ((serial_in(port, SPRD_STS1) & 0x00ff) && max_count--) {
		lsr = serial_in(port, SPRD_LSR);
		ch = serial_in(port, SPRD_RXD);//把数据一个接一个读处理,然后放缓冲区里
		uart_insert_char(port, lsr, SPRD_LSR_OE, ch, flag);--->uart_insert_char--->
	}
 
	tty_flip_buffer_push(tty);
}
 
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{
		i = ld->ops->read(tty, file, buf, count);--->n_tty_read
 
}
 
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr)
{
	uncopied = copy_from_read_buf(tty, &b, &nr);
}

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgjakea
系列文章
更多 icon
同类精品
更多 icon
继续加载