博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
串口驱动(三)
阅读量:3897 次
发布时间:2019-05-23

本文共 9448 字,大约阅读时间需要 31 分钟。

1. 用户空间open的操作实现

        串口设备是被注册为字符设备的,在注册过程中填充了struct file_operations tty_fops结构体,该结构体中的成员open、read、write等就是驱动和应用交互的接口,应用程序打开串口设备时,经过系统调用,最后会调用到tty_fops.open函数,即tty_open。

tty_open()*************************************** 分析线路1 ***************************************************	-->tty = tty_open_current_tty(device, filp)  //获取当前设备对应的tty_struct	-->driver = tty_lookup_driver(device, filp, &noctty, &index);		-->driver = get_tty_driver(device, index);	  //从全局链表tty_drivers中找出匹配的tty_driver	-->tty = tty_driver_lookup_tty(driver, inode, index); 		-->return driver->ttys[idx];			//返回NULL,因为ttys[]数组的填充在后面*************************************** 分析线路2-1 ***************************************************	-->tty_init_dev(driver, index);		-->initialize_tty_struct(tty, driver, idx);			-->tty_ldisc_init(tty);				-->ld = tty_ldisc_get(N_TTY);	//获取串口的ldisc					-->ldops = get_ldops(disc);						-->ldops = tty_ldiscs[disc];					-->ld->ops = ldops;				-->tty->ldisc = ld;		//设置串口的ldisc,后面read/write操作都直接从tty->ldisc获取线路规程			-->tty->ops = driver->ops;          //这里的ops交接很关键键*************************************** 分析线路2-2 ***************************************************		-->tty_driver_install_tty(driver, tty);			-->tty_standard_install(driver, tty)				-->tty_init_termios(tty)				-->driver->ttys[tty->index] = tty;   //填充ttys[]数组*************************************** 分析线路2-3 ***************************************************		-->tty_ldisc_setup(tty, tty->link)			-->tty_ldisc_open(tty, ld);				-->ld->ops->open(tty)           //即n_tty_open					-->n_tty_open						-->n_tty_set_termios                            -->start_rx_dma  //开启串口接收DMA搬运    							-->dma_rx_callback                                    -->dma_rx_work										-->dma_rx_push_data*************************************** 分析线路3 ***************************************************	-->tty->ops->open(tty, filp);      //即uart_open		-->uart_open			-->uart_startup				-->uart_port_startup					-->uport->ops->startup(uport)    //即imx_startup						-->imx_startup									-->imx_uart_dma_init(sport);    //配置发送消息时DMA搬运的目标地址,接收消息时DMA搬运的源地址							-->INIT_DELAYED_WORK(&sport->tsk_dma_tx, dma_tx_work);   //DMA搬运线程

2. 具体代码分析

        先找到tty_fops 结构体的定义

//tty_io.cstatic const struct file_operations tty_fops = {	.read		= tty_read,	.write		= tty_write,	.open		= tty_open,	.release	= tty_release,     ······};

        然后分析tty_fops.open函数即tty_open()函数

static int tty_open(struct inode *inode, struct file *filp){	struct tty_struct *tty;   struct tty_driver *driver = NULL;   	tty = tty_open_current_tty(device, filp);  /* 得到的tty为NULL */	if (IS_ERR(tty)) {		retval = PTR_ERR(tty);		goto err_unlock;	} else if (!tty) {		driver = tty_lookup_driver(device, filp, &noctty, &index);	/*driver等于《串口驱动(一)》中注册的tty_driver(即normal),并将索引号放到index */          tty = tty_driver_lookup_tty(driver, inode, index);        //tty=NULL          if (tty) {								    		······    	} else			tty = tty_init_dev(driver, index);	/*tty->ops=driver->ops*/	} 	if (tty->ops->open)	/*即uart_open*/		retval = tty->ops->open(tty, filp);		return 0;}

        分析线路1 如下:

static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp){	if (device != MKDEV(TTYAUX_MAJOR, 0))  //串口的主设备号是SERIAL_IMX_MAJOR,这个在《串口驱动(一)》中有分析到         return NULL;         ······}    static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,		int *noctty, int *index){	struct tty_driver *driver;	switch (device) {	case MKDEV(TTYAUX_MAJOR, 1): {		······	}	default://上面的分支都不符合		driver = get_tty_driver(device, index);	/* ---> */		break;	}	return driver;} static struct tty_driver *get_tty_driver(dev_t device, int *index){	struct tty_driver *p;	list_for_each_entry(p, &tty_drivers, tty_drivers) {       //从全局链表tty_drivers中找出匹配的tty_driver,这里找到的应该是《串口驱动(一)》中注册的uart_ops操作集		dev_t base = MKDEV(p->major, p->minor_start);		if (device < base || device >= base + p->num)			continue;		*index = device - base;		return tty_driver_kref_get(p);	}	return NULL;}static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,		struct inode *inode, int idx){	if (driver->ops->lookup)	/* tty_operations里面没有lookup这个成员 */		return driver->ops->lookup(driver, inode, idx);	return driver->ttys[idx];		/* 返回具体的tty_struct(这个tty_struct在tty_init_dev函数中被放入ttys[]数组) */}	/* 因为tty_init_dev函数在这之前还未被调用,而且目前为止未发现其他位置初始化tty_struct并放入ttys[],所以这里返回NULL */

        分析线路2 如下:

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx){	struct tty_struct *tty;	int retval;	tty = alloc_tty_struct();	initialize_tty_struct(tty, driver, idx);   //设置串口的线路规程ldisc	retval = tty_driver_install_tty(driver, tty);	/* 将tty_struct放到driver->ttys[] */	retval = tty_ldisc_setup(tty, tty->link);	return tty;}

        分析线路2-1 如下:

void initialize_tty_struct(struct tty_struct *tty,		struct tty_driver *driver, int idx){	tty_ldisc_init(tty);   //取出串口的线路规程指针,即取出tty_ldiscs[N_TTY],N_TTY这一项在前面注册了的,可以回看串口驱动(二)	init_waitqueue_head(&tty->write_wait);	init_waitqueue_head(&tty->read_wait);	INIT_LIST_HEAD(&tty->tty_files);	INIT_WORK(&tty->SAK_work, do_SAK_work);    tty->driver = driver;    //把driver放到tty中 	tty->ops = driver->ops;            //ops的交接,这里很重要,后面会通过tty-ops->open、tty->ops->write来调用uart_open和uart_write}void tty_ldisc_init(struct tty_struct *tty){	struct tty_ldisc *ld = tty_ldisc_get(N_TTY);   //获取tty_ldiscs[N_TTY]	tty->ldisc = ld;      //将取出的线路规程指针赋值给tty->ldisc,后面read/write操作都直接从tty->ldisc获取线路规程}static struct tty_ldisc *tty_ldisc_get(int disc){	struct tty_ldisc *ld;	struct tty_ldisc_ops *ldops;	ldops = get_ldops(disc);   //获取tty_ldiscs[N_TTY]	ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);	ld->ops = ldops;          //  	return ld;}static struct tty_ldisc_ops *get_ldops(int disc){	unsigned long flags;	struct tty_ldisc_ops *ldops, *ret;	ldops = tty_ldiscs[disc];   //获取tty_ldiscs[N_TTY]	return ret;}

        分析线路2-2 如下:

static int tty_driver_install_tty(struct tty_driver *driver,						struct tty_struct *tty){	return driver->ops->install ? driver->ops->install(driver, tty) :		tty_standard_install(driver, tty);  /*driver->ops即uart_ops,uart->ops中并没有insrall成员*/}int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty){	int ret = tty_init_termios(tty);	tty_driver_kref_get(driver);     /* 从tty中提取driver */	tty->count++;	driver->ttys[tty->index] = tty;		/* 将tty填充到ttys[]数组 */	return 0;}

        分析线路2-3 如下:

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty){	struct tty_ldisc *ld = tty->ldisc;	int retval;	retval = tty_ldisc_open(tty, ld);}static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld){	if (ld->ops->open) {		ret = ld->ops->open(tty);		/* 即n_tty_open */	}}static int n_tty_open(struct tty_struct *tty){        struct n_tty_data *ldata;        ldata = kzalloc(sizeof(*ldata), GFP_KERNEL);        ldata->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);        ldata->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);        tty->disc_data = ldata;        reset_buffer_flags(tty->disc_data);        ldata->column = 0;        tty->minimum_to_wake = 1;        n_tty_set_termios(tty, NULL);   /* ---> */                return 0;}static void imx_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old){	struct imx_port *sport = (struct imx_port *)port;    /* 这里有超级多的寄存器配置操作,譬如波特率、时钟等等,全部删掉了 */	if (sport->dma_is_inited && !sport->dma_is_enabled) {		imx_enable_dma(sport);		start_rx_dma(sport);     /* 开启串口接收数据时的DMA搬运 */	}}static int start_rx_dma(struct imx_port *sport){	struct dma_chan	*chan = sport->dma_chan_rx;	struct dma_async_tx_descriptor *desc;	desc = dmaengine_prep_dma_cyclic(chan, sport->rx_buf.dmaaddr,		sport->rx_buf.buf_len, sport->rx_buf.period_len,		DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);	desc->callback = dma_rx_callback;    //DMA完成一次搬运后,会调用这个回调函数	dmaengine_submit(desc);         //将该描述符插入dmaengine驱动的传输队列	dma_async_issue_pending(chan);   //启动对应DMA通道上的传输	sport->dma_is_rxing = 1;	return 0;}static void dma_rx_callback(void *data){    dma_rx_work(sport);}static void dma_rx_work(struct imx_port *sport){	struct tty_struct *tty = sport->port.state->port.tty;	unsigned int cur_idx = sport->rx_buf.cur_idx;	dma_rx_push_data(sport, tty, 0, cur_idx);    /* 最终串口接收到的数据都被放到了tty ldisc的一个buffer里,用户空间读的时候会从这个buffer里取 */}

        分析线路3 如下:

static int uart_open(struct tty_struct *tty, struct file *filp){	struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;	int retval;	struct uart_state *state = drv->state + line;	retval = uart_startup(tty, state, 0);	/*--->*/}static int uart_startup(struct tty_struct *tty, struct uart_state *state,		int init_hw){	retval = uart_port_startup(tty, state, init_hw);	/*--->*/}static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,		int init_hw){	struct uart_port *uport = state->uart_port;	if (!state->xmit.buf) {		page = get_zeroed_page(GFP_KERNEL);		/*分配了一页内存*/		state->xmit.buf = (unsigned char *) page;	/*串口底层发送缓冲区*/		uart_circ_clear(&state->xmit);	}	retval = uport->ops->startup(uport);		/*即imx_startup,在串口驱动(二)中的serial_imx_probe函数中做的交接*/	return retval;}static int imx_startup(struct uart_port *port){	struct imx_port *sport = (struct imx_port *)port;	int retval, i;	unsigned long flags, temp;    /* 本函数中有很多的通过寄存器读写操作来进行的硬件配置动作,此处删去了很多 */	/* Can we enable the DMA support? */	if (is_imx6q_uart(sport) && !uart_console(port)		&& !sport->dma_is_inited)		imx_uart_dma_init(sport);  //配置发送消息时DMA搬运的目标地址,接收消息时DMA搬运的源地址	if (sport->dma_is_inited)		INIT_DELAYED_WORK(&sport->tsk_dma_tx, dma_tx_work);   //定义了一个延后工作任务,DMA发送搬运,在串口发送数据会唤醒调度	return 0;}

转载地址:http://mhuen.baihongyu.com/

你可能感兴趣的文章
重学C++之路_#1_基础用法
查看>>
重学C++之路_#1_异常处理
查看>>
C/C++指针回顾
查看>>
算法之排序--希尔排序
查看>>
javascript深入浅出图解作用域链和闭包
查看>>
WebKit之Http请求
查看>>
WebKit之FrameWork层介绍
查看>>
WebKit之Http响应
查看>>
Webkit之支持WML
查看>>
Webkit之HTMLInput分析
查看>>
WebKit之UserAgent分析
查看>>
Webkit之HTMLToken处理
查看>>
Webkit之HTMLTokenizer分析
查看>>
Webkit之平台相关层
查看>>
Webkit之UI层布局
查看>>
WebKit之InlineBox绘制阶段顺序
查看>>
WebKit之图像显示分析流程
查看>>
WebKit之addToJavaScriptWindowObject()分析
查看>>
资源之收集列表整理
查看>>
JS之kindeditor的用法简介
查看>>