本文共 9448 字,大约阅读时间需要 31 分钟。
串口设备是被注册为字符设备的,在注册过程中填充了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搬运线程
先找到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/