今天大家都在抢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 要往哪里去?关于最后这个问题欢迎大家留言指导一下

NVIDIA Tegra-TK1串口驱动代码初探相关推荐

  1. 慢慢欣赏linux之串口驱动代码分析 - 基于powerpc 2.6.x版本

    串口驱动分两阶段初始化 第一阶段  串口驱动没有初始化前的准备工作,包括设备树的解析,platform设备注册 asmlinkage void __init start_kernel(void)    ...

  2. 8088单板机串口驱动代码测试

    1.8088单板机的串口接口电路如下 2.驱动代码 :------------------------------------------------------------------------- ...

  3. c++创建虚拟串口_linux虚拟串口控制器驱动实现——适用于无开发板学习串口驱动...

    在上一章我们已经说明了uart驱动的开发流程,本章我们就不再介绍uart相关的接口实现,仅通过实现一个虚拟的串口控制器程序,用以说明虚拟串口的开发流程. 本次开发的虚拟串口提供的功能如下: 提供两个串 ...

  4. xilinx linux 串口驱动

    串口是常用的外设,串口有很多电平标准,TTL.232.485 等等,它们的驱动程序都是一样的.在嵌入式Linux系统中,串口被看成终端设备(tty),包括 3 个结构体:uart_driver.uar ...

  5. Linux串口驱动阅读笔记

    sccv串口驱动学习–8250.c 本文主要记录学习CGEL3(基于Linux version 2.6.21.7) 中关于8250串口驱动代码的心得.代码位于Z:\CGEL3\archto\mips\ ...

  6. NVIDIA Jetson TX2 内核中添加 CP210x 串口驱动

    说明:本文的核心内容是围绕版本为 L4T 27.1 的内核编译过程.如果内核版本是 L4T 28.1 ,则可移步这篇文章: 编译 L4T 28.1,这两个版本的编译步骤几乎一样. 问题背景: 最近在 ...

  7. cubemx stm32 配置两个串口_STM32CubeMX的串口配置,以及驱动代码

    1.STM32CubeMX的配置没啥子好说的,使能然后改一下波特率和字长,然后在将中断勾选,把中断等级调到1(一定要比systick的优先级垃圾!!!) 2.驱动代码 在生成的it.c文件中,例如用的 ...

  8. 串口 (四) linux串口之驱动代码

    early console 之 early printk 需要平台侧实现 printch early console 之 earlycon //驱动需要实现的代码 static void pl011_ ...

  9. RoboMaster视觉教程(2)妙算(Nvidia Tegra K1)系统配置

    RoboMaster视觉教程(2)妙算(Nvidia Tegra K1)系统配置 概览 妙算资料链接汇总 妙算系统重置/克隆/恢复 妙算安装系统后要做的事 妙算通过网线直连电脑并共享电脑网络 妙算远程 ...

最新文章

  1. PHP安装parsekit扩展查看opcode
  2. 阿里某P5程序员求助:跟女票要结婚,她家要50万彩礼,女票爸爸说钱不够可以先欠着,这婚能结吗?欠条以后能赖吗?...
  3. JS数组方法(forEach()、every()、reduce())
  4. 论文阅读——《Robust Superpixel Tracking》
  5. TID大会学习心得之敏捷软件架构-微服务
  6. TransactionScope 分布式事务
  7. 视不可当:信息图与可视化传播
  8. MySql 清空、删除、截断表时1701错误
  9. python爱因斯坦的问题_爱因斯坦的思考题.py
  10. Integer中1000==1000为false而100==100为true
  11. python对象传递_Python参数传递对象的引用原理解析
  12. 基于Spark机器学习和实时流计算的智能推荐系统
  13. html5 橡皮擦效果,javascript – HTML5 Canvas:globalCompositeOperation(橡皮擦)
  14. html 链接长宽,CSS实现长宽比的几种方案【转载】
  15. 服务器nvida显卡驱动安装(亲测)
  16. Windows下安装VMware
  17. 模型量化论文阅读#2----BRECQ: PUSHING THE LIMIT OF POST-TRAINING QUANTIZATION BY BLOCK RECONSTRUCTION
  18. vue+echart图表数据切换
  19. Garrett Motion将在Auto Shanghai 2021上展示用于混合动力汽车和燃料电池汽车的下一代电动助力技术
  20. 如何选一款适合企业的进销存软件?这款软件推荐给你

热门文章

  1. java npl分词,使用OpenNLP进行分词及词性标注
  2. 一个简陋至极的员工考勤系统
  3. 个人网盘VS企业网盘 亿级市场的龙争虎斗
  4. 二十七、 影子价格(对偶问题)
  5. Linux中vi\vim命令详解
  6. 30 岁: 程序员心中永远的痛?
  7. bugku 神秘的文件(问题:在选定的档案中没有匹配的文件)
  8. 21岁大专学历,刚培训完前端,不造假简历,能找到工作吗?
  9. 法拉第未来预计明年年中开始量产其首款电动汽车FF 91
  10. 华中科技大计算机学院,华中科技大计算机学院.doc