当前位置:   article > 正文

Android uart driver

android uart

我们都知道可以从手机的耳机口,通过USB-串口转换器链接到电脑USB接口,然后在电脑上使用putty或者cutecom,设置好波特率之类的参数,就可以读取到手机中kernel的log,甚至还能读到xbl,abl阶段的log.  那么,这些log到底是怎么来的呢? 我们所说的uart到底是啥?

1.UART是什么?

百度百科上是这么说的:通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器,是电脑硬件的一部分。它将要传输的资料在串行通信和并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片,UART通常被集成于其他通讯接口的连结上。

当然,在手机里面也可以有uart,并且我们的kernel中的printk log就是通过这个uart最终发送出来的.

2.UART驱动结构分析

2.1 驱动的init

先认识几个比较重要的结构.

console结构,这个里面的write函数其实就是后面printk会调用到的write.后面会具体分析.

  1. static struct console cons_ops = {
  2. .name = "ttyMSM",
  3. .write = msm_geni_serial_console_write,
  4. .device = uart_console_device,
  5. .setup = msm_geni_console_setup,
  6. .flags = CON_PRINTBUFFER,
  7. .index = -1,
  8. .data = &msm_geni_console_driver, //是下面的结构体
  9. };

uart_driver结构,显然,这个结构中有个cons的元素,就是上面的console结构:

  1. static struct uart_driver msm_geni_console_driver = {
  2. .owner = THIS_MODULE,
  3. .driver_name = "msm_geni_console",
  4. .dev_name = "ttyMSM",
  5. .nr = GENI_UART_NR_PORTS,
  6. .cons = &cons_ops, // ==>是上面的那个结构体.
  7. };

还有两个uart_ops的结构:

  1. static const struct uart_ops msm_geni_console_pops = {
  2. .tx_empty = msm_geni_serial_tx_empty,
  3. .stop_tx = msm_geni_serial_stop_tx,
  4. .start_tx = msm_geni_serial_start_tx,
  5. .stop_rx = msm_geni_serial_stop_rx,
  6. .set_termios = msm_geni_serial_set_termios,
  7. .startup = msm_geni_serial_startup,
  8. .config_port = msm_geni_serial_config_port,
  9. .shutdown = msm_geni_serial_shutdown,
  10. .type = msm_geni_serial_get_type,
  11. .set_mctrl = msm_geni_cons_set_mctrl,
  12. .get_mctrl = msm_geni_cons_get_mctrl,
  13. #ifdef CONFIG_CONSOLE_POLL
  14. .poll_get_char = msm_geni_serial_get_char,
  15. .poll_put_char = msm_geni_serial_poll_put_char,
  16. #endif
  17. .pm = msm_geni_serial_cons_pm,
  18. };
  19. serial_core.c中:
  20. static const struct tty_operations uart_ops = {
  21. .open = uart_open,
  22. .close = uart_close,
  23. .write = uart_write,
  24. .put_char = uart_put_char,
  25. .flush_chars = uart_flush_chars,
  26. .write_room = uart_write_room,
  27. .chars_in_buffer= uart_chars_in_buffer,
  28. .flush_buffer = uart_flush_buffer,
  29. .ioctl = uart_ioctl,
  30. .throttle = uart_throttle,
  31. .unthrottle = uart_unthrottle,
  32. .send_xchar = uart_send_xchar,
  33. .set_termios = uart_set_termios,
  34. .set_ldisc = uart_set_ldisc,
  35. .stop = uart_stop,
  36. .start = uart_start,
  37. .hangup = uart_hangup,
  38. .break_ctl = uart_break_ctl,
  39. .wait_until_sent= uart_wait_until_sent,
  40. #ifdef CONFIG_PROC_FS
  41. .proc_fops = &uart_proc_fops,
  42. #endif
  43. .tiocmget = uart_tiocmget,
  44. .tiocmset = uart_tiocmset,
  45. .get_icount = uart_get_icount,
  46. #ifdef CONFIG_CONSOLE_POLL
  47. .poll_init = uart_poll_init,
  48. .poll_get_char = uart_poll_get_char,
  49. .poll_put_char = uart_poll_put_char,
  50. #endif
  51. };

接下来,看看init函数是怎么实现的.

  1. static int __init msm_geni_serial_init(void)
  2. {
  3. int ret = 0;
  4. int i;
  5. for (i = 0; i < GENI_UART_NR_PORTS; i++) {
  6. msm_geni_serial_ports[i].uport.iotype = UPIO_MEM;
  7. msm_geni_serial_ports[i].uport.ops = &msm_geni_serial_pops;
  8. msm_geni_serial_ports[i].uport.flags = UPF_BOOT_AUTOCONF;
  9. msm_geni_serial_ports[i].uport.line = i;
  10. }
  11. for (i = 0; i < GENI_UART_CONS_PORTS; i++) {
  12. msm_geni_console_port.uport.iotype = UPIO_MEM;
  13. msm_geni_console_port.uport.ops = &msm_geni_console_pops; //这个操作函数结构体在上面
  14. msm_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF;
  15. msm_geni_console_port.uport.line = i;
  16. }
  17. ret = console_register(&msm_geni_console_driver); //看下面,这个函数命名有点坑,实际上就是调用了 uart_register_driver().
  18. if (ret)
  19. return ret;
  20. ret = uart_register_driver(&msm_geni_serial_hs_driver);//看上去是有register了两个 uart driver.
  21. if (ret) {
  22. uart_unregister_driver(&msm_geni_console_driver);
  23. return ret;
  24. }
  25. ret = platform_driver_register(&msm_geni_serial_platform_driver); //再注册一个platform driver
  26. if (ret) {
  27. console_unregister(&msm_geni_console_driver);
  28. uart_unregister_driver(&msm_geni_serial_hs_driver);
  29. return ret;
  30. }
  31. pr_info("%s: Driver initialized", __func__);
  32. return ret;
  33. }

嗯,其实init函数也没什么很特别的,关键就是调用了uart_register_driver. 后面要好好看一下这个注册函数的实现.

  1. /**
  2. * uart_register_driver - register a driver with the uart core layer
  3. * @drv: low level driver structure
  4. *
  5. * Register a uart driver with the core driver. We in turn register
  6. * with the tty layer, and initialise the core driver per-port state.
  7. *
  8. * We have a proc file in /proc/tty/driver which is named after the
  9. * normal driver.
  10. *
  11. * drv->port should be NULL, and the per-port structures should be
  12. * registered using uart_add_one_port after this call has succeeded.
  13. */
  14. int uart_register_driver(struct uart_driver *drv)
  15. {
  16. struct tty_driver *normal;
  17. int i, retval;
  18. BUG_ON(drv->state);
  19. /*
  20. * Maybe we should be using a slab cache for this, especially if
  21. * we have a large number of ports to handle.
  22. */
  23. drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); //nr是15.分配多个state
  24. if (!drv->state)
  25. goto out;
  26. normal = alloc_tty_driver(drv->nr); //normal是一个 struct tty_driver *
  27. if (!normal)
  28. goto out_kfree;
  29. drv->tty_driver = normal;
  30. normal->driver_name = drv->driver_name;
  31. normal->name = drv->dev_name;
  32. normal->major = drv->major;
  33. normal->minor_start = drv->minor;
  34. normal->type = TTY_DRIVER_TYPE_SERIAL;
  35. normal->subtype = SERIAL_TYPE_NORMAL;
  36. normal->init_termios = tty_std_termios;
  37. normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
  38. normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
  39. normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
  40. normal->driver_state = drv;
  41. tty_set_operations(normal, &uart_ops); //就是normal->ops=&uart_ops.
  42. /*
  43. * Initialise the UART state(s).
  44. */
  45. for (i = 0; i < drv->nr; i++) {
  46. struct uart_state *state = drv->state + i;
  47. struct tty_port *port = &state->port; //每一个state有一个tty_port
  48. tty_port_init(port); //这个init主要是设置这个port的一些参数
  49. port->ops = &uart_port_ops; //port的操作函数结构体是uart_port_ops
  50. }
  51. retval = tty_register_driver(normal); //这个比较重要
  52. if (retval >= 0)
  53. return retval;
  54. for (i = 0; i < drv->nr; i++)
  55. tty_port_destroy(&drv->state[i].port);
  56. put_tty_driver(normal);
  57. out_kfree:
  58. kfree(drv->state);
  59. out:
  60. return -ENOMEM;
  61. }

其实这个注册函数看上去也还好,就是让我们的uart_driver结构变得更加庞大了.
drv->tty_driver===>也就是上面函数中的normal,并且初始化了这个tty_driver结构体.
drv->state->port ====>这个state还会指向一个 struct tty_port结构

2.2 probe函数

看probe函数.省略掉不感兴趣的部分:

  1. static int msm_geni_serial_probe(struct platform_device *pdev)
  2. {
  3. int ret = 0;
  4. int line;
  5. struct msm_geni_serial_port *dev_port;
  6. struct uart_port *uport;
  7. struct resource *res;
  8. struct uart_driver *drv;
  9. const struct of_device_id *id;
  10. bool is_console = false;
  11. struct platform_device *wrapper_pdev;
  12. struct device_node *wrapper_ph_node;
  13. u32 wake_char = 0;
  14. id = of_match_device(msm_geni_device_tbl, &pdev->dev); //根据这个id,选出的是我们的msm_geni_console_driver
  15. if (id) {
  16. dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
  17. drv = (struct uart_driver *)id->data;
  18. } else {
  19. dev_err(&pdev->dev, "%s: No matching device found", __func__);
  20. return -ENODEV;
  21. }
  22. if (pdev->dev.of_node) {
  23. if (drv->cons)
  24. line = of_alias_get_id(pdev->dev.of_node, "serial"); //这个.
  25. else
  26. line = of_alias_get_id(pdev->dev.of_node, "hsuart");
  27. } else {
  28. line = pdev->id;
  29. }
  30. if (line < 0)
  31. line = atomic_inc_return(&uart_line_id) - 1;
  32. if ((line < 0) || (line >= GENI_UART_NR_PORTS))
  33. return -ENXIO;
  34. is_console = (drv->cons ? true : false); //我们的这个drv是有cons的呀.
  35. dev_port = get_port_from_line(line, is_console); //又出来一个新的结构,struct msm_geni_serial_port *dev_port;实际上这个结构在init函数里面有初始化的.
  36. if (IS_ERR_OR_NULL(dev_port)) {
  37. ret = PTR_ERR(dev_port);
  38. dev_err(&pdev->dev, "Invalid line %d(%d)\n",
  39. line, ret);
  40. goto exit_geni_serial_probe;
  41. }
  42. uport = &dev_port->uport; //重点,上面刚出现的这个结构指向的uport就是很重要的uport,也就是struct uart_port *uport;
  43. /* Don't allow 2 drivers to access the same port */
  44. if (uport->private_data) {
  45. ret = -ENODEV;
  46. goto exit_geni_serial_probe;
  47. }
  48. uport->dev = &pdev->dev;
  49. ......//中间做了不少对uport的设置.
  50. return uart_add_one_port(drv, uport); //将drv和uport绑定在一起.
  51. exit_geni_serial_probe:
  52. return ret;
  53. }

probe函数中,好像也没有做什么特别的事情,比较重要的是定义了一个struct msm_geni_serial_port *dev_port;而这个dev_port又指向一个uart_port的结构,并且对这个uart_port做了一些初始化.
当然,对系统结构分析最重要的是最后调用的函数uart_add_one_port. 接下来继续看看这个函数是怎么实现的.

 

  1. /**
  2. * uart_add_one_port - attach a driver-defined port structure
  3. * @drv: pointer to the uart low level driver structure for this port
  4. * @uport: uart port structure to use for this port.
  5. *
  6. * This allows the driver to register its own uart_port structure
  7. * with the core driver. The main purpose is to allow the low
  8. * level uart drivers to expand uart_port, rather than having yet
  9. * more levels of structures.
  10. */
  11. int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
  12. {
  13. struct uart_state *state;
  14. struct tty_port *port;
  15. int ret = 0;
  16. struct device *tty_dev;
  17. int num_groups;
  18. BUG_ON(in_interrupt());
  19. if (uport->line >= drv->nr)
  20. return -EINVAL;
  21. state = drv->state + uport->line; //uport->line=0.
  22. port = &state->port; //struct tty_port *port;
  23. mutex_lock(&port_mutex);
  24. mutex_lock(&port->mutex);
  25. if (state->uart_port) {
  26. ret = -EINVAL;
  27. goto out;
  28. }
  29. /* Link the port to the driver state table and vice versa */
  30. atomic_set(&state->refcount, 1);
  31. init_waitqueue_head(&state->remove_wait);
  32. state->uart_port = uport; //当当当!state指向的uart_port结构就是传参传进来的uport
  33. uport->state = state; //相应地,uport的state也就是传进来的drv->state.这两步就是将drv和uport绑定在一起的!
  34. state->pm_state = UART_PM_STATE_UNDEFINED;
  35. uport->cons = drv->cons; //uport的cons就是drv的cons
  36. uport->minor = drv->tty_driver->minor_start + uport->line;
  37. /*
  38. * If this port is a console, then the spinlock is already
  39. * initialised.
  40. */
  41. if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
  42. spin_lock_init(&uport->lock);
  43. lockdep_set_class(&uport->lock, &port_lock_key);
  44. }
  45. if (uport->cons && uport->dev)
  46. of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
  47. //这个uart_configure_port里面会根据条件判断是否需要调用一个register_console.
  48. //并且会加入到console_drivers的list中.这个如果注册成功,在printk最后会调到uport->cons->write函数.
  49. //也就是开篇的static struct console cons_ops中的write函数.
  50. uart_configure_port(drv, state, uport);
  51. port->console = uart_console(uport);
  52. num_groups = 2;
  53. if (uport->attr_group)
  54. num_groups++;
  55. uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
  56. GFP_KERNEL);
  57. if (!uport->tty_groups) {
  58. ret = -ENOMEM;
  59. goto out;
  60. }
  61. uport->tty_groups[0] = &tty_dev_attr_group;
  62. if (uport->attr_group)
  63. uport->tty_groups[1] = uport->attr_group;
  64. /*
  65. * Register the port whether it's detected or not. This allows
  66. * setserial to be used to alter this port's parameters.
  67. */
  68. tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
  69. uport->line, uport->dev, port, uport->tty_groups);
  70. if (likely(!IS_ERR(tty_dev))) {
  71. device_set_wakeup_capable(tty_dev, 1);
  72. } else {
  73. dev_err(uport->dev, "Cannot register tty device on line %d\n",
  74. uport->line);
  75. }
  76. /*
  77. * Ensure UPF_DEAD is not set.
  78. */
  79. uport->flags &= ~UPF_DEAD;
  80. out:
  81. mutex_unlock(&port->mutex);
  82. mutex_unlock(&port_mutex);
  83. return ret;
  84. }

到现在位置,驱动的初始化部分就结束了. 后面再看看是如何使用这个uart进行通信的.

3 printk分析

直接上code:

  1. /**
  2. * printk - print a kernel message
  3. * @fmt: format string
  4. *
  5. * This is printk(). It can be called from any context. We want it to work.
  6. *
  7. * We try to grab the console_lock. If we succeed, it's easy - we log the
  8. * output and call the console drivers. If we fail to get the semaphore, we
  9. * place the output into the log buffer and return. The current holder of
  10. * the console_sem will notice the new output in console_unlock(); and will
  11. * send it to the consoles before releasing the lock.
  12. *
  13. * One effect of this deferred printing is that code which calls printk() and
  14. * then changes console_loglevel may break. This is because console_loglevel
  15. * is inspected when the actual printing occurs.
  16. *
  17. * See also:
  18. * printf(3)
  19. *
  20. * See the vsnprintf() documentation for format string extensions over C99.
  21. */
  22. asmlinkage __visible int printk(const char *fmt, ...)
  23. {
  24. va_list args;
  25. int r;
  26. va_start(args, fmt);
  27. r = vprintk_func(fmt, args);
  28. va_end(args);
  29. return r;
  30. }
  31. EXPORT_SYMBOL(printk);

接着看:

  1. static inline __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
  2. {
  3. return vprintk_default(fmt, args);
  4. }

再看:

  1. int vprintk_default(const char *fmt, va_list args)
  2. {
  3. int r;
  4. #ifdef CONFIG_KGDB_KDB //这个是没有定义的...
  5. if (unlikely(kdb_trap_printk)) {
  6. r = vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
  7. return r;
  8. }
  9. #endif
  10. r = vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);
  11. return r;
  12. }
  13. EXPORT_SYMBOL_GPL(vprintk_default);

继续往后看:

  1. asmlinkage int vprintk_emit(int facility, int level,
  2. const char *dict, size_t dictlen,
  3. const char *fmt, va_list args)
  4. {
  5. static bool recursion_bug;
  6. static char textbuf[LOG_LINE_MAX];
  7. static char textbuf1[LOG_LINE_MAX];
  8. char *text = textbuf;
  9. char *text1 = textbuf1;
  10. size_t text_len = 0;
  11. enum log_flags lflags = 0;
  12. unsigned long flags;
  13. int this_cpu;
  14. int printed_len = 0;
  15. int nmi_message_lost;
  16. bool in_sched = false;
  17. /* cpu currently holding logbuf_lock in this function */
  18. static unsigned int logbuf_cpu = UINT_MAX;
  19. if (level == LOGLEVEL_SCHED) {
  20. level = LOGLEVEL_DEFAULT;
  21. in_sched = true; //如果传进来的level==LOGLEVEL_SCHED,那就设为true
  22. }
  23. boot_delay_msec(level);
  24. printk_delay();
  25. ......
  26. /*
  27. * The printf needs to come first; we need the syslog
  28. * prefix which might be passed-in as a parameter.
  29. */
  30. text_len = vscnprintf(text1, sizeof(textbuf1), fmt, args);
  31. ......
  32. #ifdef CONFIG_EARLY_PRINTK_DIRECT
  33. printascii(text1);
  34. #endif
  35. if(needPrintTime)
  36. {
  37. char buf[64];
  38. size_t buf_size = sprintf(buf, "(CPU:%d-pid:%d:%s) ", smp_processor_id(), current->pid, current->comm);
  39. strncpy(text, buf, buf_size);
  40. strncpy(text + buf_size, text1, text_len);
  41. text_len += buf_size;
  42. }
  43. else
  44. {
  45. strncpy(text, text1, text_len);
  46. }
  47. if (level == LOGLEVEL_DEFAULT)
  48. level = default_message_loglevel;
  49. if (dict)
  50. lflags |= LOG_PREFIX|LOG_NEWLINE;
  51. printed_len += log_output(facility, level, lflags, dict, dictlen, text, text_len);
  52. if(lflags & LOG_NEWLINE)
  53. {
  54. needPrintTime = true;
  55. }
  56. else
  57. {
  58. needPrintTime = false;
  59. }
  60. logbuf_cpu = UINT_MAX;
  61. raw_spin_unlock(&logbuf_lock);
  62. lockdep_on();
  63. local_irq_restore(flags);
  64. /* If called from the scheduler, we can not call up(). */
  65. if (!in_sched) {
  66. lockdep_off();
  67. /*
  68. * Try to acquire and then immediately release the console
  69. * semaphore. The release will print out buffers and wake up
  70. * /dev/kmsg and syslog() users.
  71. */
  72. if (console_trylock())
  73. console_unlock(); //这里...
  74. lockdep_on();
  75. }
  76. return printed_len;
  77. }
  78. EXPORT_SYMBOL(vprintk_emit);

再看console_unlock, 这个函数中调了   call_console_drivers(level, ext_text, ext_len, text, len);

那么再看call_console_drivers:

  1. /*
  2. * Call the console drivers, asking them to write out
  3. * log_buf[start] to log_buf[end - 1].
  4. * The console_lock must be held.
  5. */
  6. static void call_console_drivers(int level,
  7. const char *ext_text, size_t ext_len,
  8. const char *text, size_t len)
  9. {
  10. struct console *con;
  11. trace_console_rcuidle(text, len);
  12. if (!console_drivers)
  13. return;
  14. for_each_console(con) { //遍历console_drivers,实际上只有前面提到的一个地方有成功加到这个list中
  15. if(strncmp(con->name,"logk",4) != 0){
  16. if (level >= console_loglevel && !ignore_loglevel)
  17. continue;
  18. if (exclusive_console && con != exclusive_console)
  19. continue;
  20. }
  21. if (!(con->flags & CON_ENABLED))
  22. continue;
  23. if (!con->write)
  24. continue;
  25. if (!cpu_online(smp_processor_id()) &&
  26. !(con->flags & CON_ANYTIME))
  27. continue;
  28. if (con->flags & CON_EXTENDED)
  29. con->write(con, ext_text, ext_len);
  30. else
  31. con->write(con, text, len); //这个write函数就是最开始的console结构体中的write函数.
  32. }
  33. }

至于上面这个函数中遍历的console_drivers,这个list成员的注册,有一个要求是根据cmdline传过来的参数需要一致. 在机台中读到cmdline中含有console=ttyMSM0. 也就是为什么只有最开篇的这个console结构体中的write函数最终被调到,并且有log 通过uart吐出来.

可以看到,printk丢log还是单向的.也就是说是只是printk函数最终调用了uart_driver这个结构体指向的一个结构console的write函数.

很显然,这并不是uart的全部功能,uart应该是双向通信,这个是可以通过对节点进行open,write,read等操作来实现的. 

参考博文:

http://www.cnblogs.com/lidabo/p/5414007.html  驱动程序调试方法之printk——printk的原理与直接使用

非常感谢!

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号