今天大家都在抢iphoen X,我也来强行装一波X,讲讲最近读的TK1串口驱动serial-tegra.c
照旧从驱动的init函数看起。(千万别从头看起)
static int __init tegra_uart_init(void)
{int ret;ret = uart_register_driver(&tegra_uart_driver);if (ret < 0) {pr_err("Could not register %s driver\n",tegra_uart_driver.driver_name);return ret;}ret = platform_driver_register(&tegra_uart_platform_driver);if (ret < 0) {pr_err("Uart platfrom driver register failed, e = %d\n", ret);uart_unregister_driver(&tegra_uart_driver);return ret;}return 0;
}
uart_register_driver注册tegra_uart_driver,tegra_uart_driver结构如下:
static struct uart_driver tegra_uart_driver = {.owner = THIS_MODULE,.driver_name = "serial-hs-tegra",.dev_name = "ttyTHS",.cons = NULL,.nr = TEGRA_UART_MAXIMUM,
};
uart_register_driver 函数定义在 kernel/drivers/tty/serial/serial_core.c 文件中,
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.*/drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, 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 = 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;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;port->close_delay = HZ / 2; /* .5 seconds *///port->closing_wait = 30 * HZ;/* 30 seconds */port->closing_wait = 10 * HZ;}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 -ENOMEM;
}
1.drv->tty_driver = normal;将tegra_usrt_driver中的tty_driver 与tty_driver normal同步
2.normal->driver_state = drv;
这样一来只要将serial8250_ops结构体成员的值赋给我们uart_dirver就可以了,那么这个过程在哪呢?就是在uart_add_one_port()函数中,这个函数是从serial8250_init->serial8250_register_ports()->uart_add_one_port()逐步调用过来的,这一步就将port和uart_driver联系起来了。(uart_ops<=>uart_driver:8250.c)
摘自:http://developer.51cto.com/art/201209/357501_all.htm
这一段描述我也不太能理解,希望有大神指导指导。我认为这一句作用与1差不多,都是将tegra_usrt_driver中的tty_driver 与tty_driver normal同步,这样子tty_drivers调用ops操作函数的时候自然会调用到tegra_uart_ops
uart_add_one_port()代码如下:
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;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;}
/* 重要一句 */ driver->state->uart_port与 uport划上等号state->uart_port = uport;state->pm_state = -1;uport->cons = drv->cons;uport->state = state;/** 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);}
/* 实际调用 port->ops->config_port(port, flags) 稍后再看 */uart_configure_port(drv, state, uport);/** 上一篇文章中,我们提到tty注册了一个字符设备 “ttySAC ”* 那么,我们平时看到的 “ttySAC0”“ttySAC1”等就是在这里注册的*/tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);if (likely(!IS_ERR(tty_dev))) {device_init_wakeup(tty_dev, 1);device_set_wakeup_enable(tty_dev, 0);} elseprintk(KERN_ERR "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_state来绑定的,有必要上uart_state的定义:
struct uart_state {struct tty_port port;enum uart_pm_state pm_state;struct circ_buf xmit;struct uart_port *uart_port;
};
struct uart_port的定义如下(节选):
..
unsigned int read_status_mask; /* driver specific */unsigned int ignore_status_mask; /* driver specific */struct uart_state *state; /* pointer to parent state */struct uart_icount icount; /* statistics */
..
3.tty_set_operations(normal, &uart_ops);
此句之所以值得关注是因为.在这里将tty_driver的操作集统一设为了uart_ops.这样就使得从用户空间下来的操作可以找到正确的serial_core的操作函数,uart_ops是在serial_core.c中的:所以结合
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, .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 .read_proc = uart_read_proc,
#endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL .poll_init = uart_poll_init, .poll_get_char = uart_poll_get_char, .poll_put_char = uart_poll_put_char,
#endif
};
未完待续,感觉对前面的分析有点重复造轮子的嫌疑,毕竟前面的驱动代码都是差不多的,当是自己的阅读总结吧。
直接分析tegra_uart_ops部分代码:
static struct uart_ops tegra_uart_ops = {.tx_empty = tegra_uart_tx_empty,.set_mctrl = tegra_uart_set_mctrl,.get_mctrl = tegra_uart_get_mctrl,.stop_tx = tegra_uart_stop_tx,.start_tx = tegra_uart_start_tx,.stop_rx = tegra_uart_stop_rx,.flush_buffer = tegra_uart_flush_buffer,.enable_ms = tegra_uart_enable_ms,.break_ctl = tegra_uart_break_ctl,.startup = tegra_uart_startup,.shutdown = tegra_uart_shutdown,.set_termios = tegra_uart_set_termios,.type = tegra_uart_type,.request_port = tegra_uart_request_port,.release_port = tegra_uart_release_port,
};
先看start_up操作,
static int tegra_uart_startup(struct uart_port *u)
{struct tegra_uart_port *tup = to_tegra_uport(u);int ret;ret = tegra_uart_dma_channel_allocate(tup, false);if (ret < 0) {dev_err(u->dev, "Tx Dma allocation failed, err = %d\n", ret);return ret;}if (!tup->use_rx_pio) {ret = tegra_uart_dma_channel_allocate(tup, true);if (ret < 0) {dev_err(u->dev, "Rx Dma allocation failed, err = %d\n",ret);goto fail_rx_dma;}}ret = tegra_uart_hw_init(tup);if (ret < 0) {dev_err(u->dev, "Uart HW init failed, err = %d\n", ret);goto fail_hw_init;}ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED,dev_name(u->dev), tup);//fast interrupt IQRF_DISABLEDif (ret < 0) {dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq);goto fail_hw_init;}return 0;fail_hw_init:if (!tup->use_rx_pio)tegra_uart_dma_channel_free(tup, true);
fail_rx_dma:tegra_uart_dma_channel_free(tup, false);return ret;
}
最重要的看到注册中断,
request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED,dev_name(u->dev), tup);
进入中断处理函数:
static irqreturn_t tegra_uart_isr(int irq, void *data)
{struct tegra_uart_port *tup = data;struct uart_port *u = &tup->uport;unsigned long iir;unsigned long ier;bool is_rx_int = false;unsigned long flags;int ret;//struct timeval tstart,tend;spin_lock_irqsave(&u->lock, flags);while (1) {iir = tegra_uart_read(tup, UART_IIR);if (iir & UART_IIR_NO_INT) { if (!tup->use_rx_pio && is_rx_int) {ret = tegra_uart_handle_rx_dma(tup);if (ret) {spin_unlock_irqrestore(&u->lock, flags);return IRQ_HANDLED;}if (tup->rx_in_progress) {ier = tup->ier_shadow;ier |= (UART_IER_RLSI | UART_IER_RTOIE |TEGRA_UART_IER_EORD);tup->ier_shadow = ier;tegra_uart_write(tup, ier, UART_IER);}}spin_unlock_irqrestore(&u->lock, flags);return IRQ_HANDLED;}switch ((iir >> 1) & 0x7) {case 0: /* Modem signal change interrupt */tegra_uart_handle_modem_signal_change(u);break;case 1: /* Transmit interrupt only triggered when using PIO */tup->ier_shadow &= ~UART_IER_THRI;tegra_uart_write(tup, tup->ier_shadow, UART_IER);tegra_uart_handle_tx_pio(tup);break;case 4: /* End of data */case 6: /* Rx timeout *///printk(KERN_ALERT"rx timeout \n");case 2: /* Receive */if (!tup->use_rx_pio && !is_rx_int) {is_rx_int = true;/* Disable Rx interrupts */ier = tup->ier_shadow;ier |= UART_IER_RDI;tegra_uart_write(tup, ier, UART_IER);ier &= ~(UART_IER_RDI | UART_IER_RLSI |UART_IER_RTOIE |TEGRA_UART_IER_EORD);tup->ier_shadow = ier;tegra_uart_write(tup, ier, UART_IER);} elsedo_handle_rx_pio(tup);break;case 3: /* Receive error */tegra_uart_decode_rx_error(tup,tegra_uart_read(tup, UART_LSR));break;case 5: /* break nothing to handle */case 7: /* break nothing to handle */break;}}
}
这里也很好理解,中断触发进来,进入while循环,再进入switch函数,这里我们看到有好几种case .case 4, 6, 2都会进入case2的receive处理函数中
is_rx_int = true;
is_rx_int置1,while loop 进入
ret = tegra_uart_handle_rx_dma(tup);
static int tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
{struct timeval tstart,tend;struct dma_tx_state state;struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);struct tty_port *port = &tup->uport.state->port;int count;int rx_level = 0;struct dma_async_tx_descriptor *prev_rx_dma_desc;int ret;/* Deactivate flow control to stop sender */if (tup->rts_active)set_rts(tup, false);dmaengine_terminate_all(tup->rx_dma_chan);dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);prev_rx_dma_desc = tup->rx_dma_desc;count = tup->rx_bytes_requested - state.residue;/* If we are here, DMA is stopped */if (count) {do_gettimeofday(&tstart);ret = tegra_uart_copy_rx_to_tty(tup, port, count);//copy rx_dma_data to tty_bufferdo_gettimeofday(&tend);printk("tegra_uart_copy_rx_to_tty taken: %ld us\n",1000000* (tend.tv_sec - tstart.tv_sec) +(tend.tv_usec - tstart.tv_usec) );if (ret)goto skip_pio;}do_gettimeofday(&tstart);ret = tegra_uart_handle_rx_pio(tup, port);do_gettimeofday(&tend);printk("tegra_uart_handle_rx_pio taken: %ld us\n",1000000* (tend.tv_sec - tstart.tv_sec) +(tend.tv_usec - tstart.tv_usec) );
skip_pio:if (tup->enable_rx_buffer_throttle) {rx_level = tty_buffer_get_level(port);if (rx_level > 70)mod_timer(&tup->timer,jiffies + tup->timer_timeout_jiffies);}if (tty) {do_gettimeofday(&tstart);tty_flip_buffer_push(port);//flip data of tty_buffer to read_buffer(user space and operated by read())do_gettimeofday(&tend);printk("tty_flip_buffer_push taken: %ld us\n",1000000* (tend.tv_sec - tstart.tv_sec) +(tend.tv_usec - tstart.tv_usec) );tty_kref_put(tty);}tegra_uart_start_rx_dma(tup);async_tx_ack(prev_rx_dma_desc);if (tup->enable_rx_buffer_throttle) {if ((rx_level <= 70) && tup->rts_active)set_rts(tup, true);} else if (tup->rts_active)set_rts(tup, true);return ret;
}
然后触发中断的条件是什么呢?这是关键点
查看TegraK1_TRM_DP06905001_public_v03p.pdf(Technical Reference Manual),关于uart_fifo 寄存器的说明
与上边代码中所有的case对应,case 6:rx_timeout_intr表示fifo 中有数据当没有达到trigger level,经过4 bytes的传输时间仍未有数据到达,触发rx_timeout
case 4:eord_timeout_intr(eord即 end of data),表示当fifo中无数据,经过4bytes的传输时间仍未有数据到达。触发eord中断。两种情况加以区分,如有纰漏敬请指出。
接下来进入tegra_uart_handle_rx_dma,代码如下:
static int tegra_uart_handle_rx_dma(struct tegra_uart_port *tup)
{struct dma_tx_state state;struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port);struct tty_port *port = &tup->uport.state->port;int count;int rx_level = 0;struct dma_async_tx_descriptor *prev_rx_dma_desc;int ret;/* Deactivate flow control to stop sender */if (tup->rts_active)set_rts(tup, false);dmaengine_terminate_all(tup->rx_dma_chan);//停止dma传输,这一句花费较多时间,实机测试可能花费30us左右dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state);//获得dma状态prev_rx_dma_desc = tup->rx_dma_desc;count = tup->rx_bytes_requested - state.residue;//获得dma中数据大小/* If we are here, DMA is stopped */if (count) {ret = tegra_uart_copy_rx_to_tty(tup, port, count);//将数据从dma的buffer拷贝到tty_buffer,实机测试可能花费16usif (ret) //ret只有两种状态,0或者负数(即出现错误),所以正常情况下会继续执行tegra_uart_handle_rx_piogoto skip_pio;}ret = tegra_uart_handle_rx_pio(tup, port);skip_pio:if (tup->enable_rx_buffer_throttle) {rx_level = tty_buffer_get_level(port);if (rx_level > 70)mod_timer(&tup->timer,jiffies + tup->timer_timeout_jiffies);}if (tty) {tty_flip_buffer_push(port);//将tty_buffer的数据push 到更上层的read_buftty_kref_put(tty);}tegra_uart_start_rx_dma(tup);//启动dmaasync_tx_ack(prev_rx_dma_desc);if (tup->enable_rx_buffer_throttle) {if ((rx_level <= 70) && tup->rts_active)set_rts(tup, true);} else if (tup->rts_active)set_rts(tup, true);return ret;
}
tegra_uart_handle_rx_pio的作用:检查fifo中是否有残余数据或是新数据,并用pio mode读取数据。这样做虽然可以防止数据丢失,但是如果有新数据在此时到来,相比dma模式读取数据的效率大大降低。
如果把这一句注释掉 ,那么如果有新数据在此时到来,dma又已经关闭,fifo中数据触发了trigger level 要往哪里去?关于最后这个问题欢迎大家留言指导一下