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.c
static 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等于《Linux串口驱动(1) - serial层》中注册的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,这个在《Linux串口驱动(1) - serial层》中有分析到
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,这里找到的应该是《Linux串口驱动(1) - serial层》中注册的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这一项在前面注册了的,可以回看Linux串口驱动(2) - 线路规程
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) {
//如果开启了DMA,则对dma进行初始化并开启RX的dma搬运
imx_enable_dma(sport);
start_rx_dma(sport); /* 开启串口接收数据时的DMA搬运 */
}
if (!sport->dma_is_enabled) {
//如果没有开启DMA,则设置串口的中断使能位
ucr2 = readl(sport->port.membase + UCR2);
writel(ucr2 | UCR2_ATEN, sport->port.membase + UCR2);
}
}
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,在《Linux串口驱动(2) - 线路规程》的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;
}