Android uart driver

我们都知道可以从手机的耳机口,通过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.后面会具体分析.

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

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

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

还有两个uart_ops的结构:

static const struct uart_ops msm_geni_console_pops = {
	.tx_empty = msm_geni_serial_tx_empty,
	.stop_tx = msm_geni_serial_stop_tx,
	.start_tx = msm_geni_serial_start_tx,
	.stop_rx = msm_geni_serial_stop_rx,
	.set_termios = msm_geni_serial_set_termios,
	.startup = msm_geni_serial_startup,
	.config_port = msm_geni_serial_config_port,
	.shutdown = msm_geni_serial_shutdown,
	.type = msm_geni_serial_get_type,
	.set_mctrl = msm_geni_cons_set_mctrl,
	.get_mctrl = msm_geni_cons_get_mctrl,
#ifdef CONFIG_CONSOLE_POLL
	.poll_get_char	= msm_geni_serial_get_char,
	.poll_put_char	= msm_geni_serial_poll_put_char,
#endif
	.pm = msm_geni_serial_cons_pm,
};


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
	.proc_fops	= &uart_proc_fops,
#endif
	.tiocmget	= uart_tiocmget,
	.tiocmset	= uart_tiocmset,
	.get_icount	= uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
	.poll_init	= uart_poll_init,
	.poll_get_char	= uart_poll_get_char,
	.poll_put_char	= uart_poll_put_char,
#endif
};

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

static int __init msm_geni_serial_init(void)
{
	int ret = 0;
	int i;

	for (i = 0; i < GENI_UART_NR_PORTS; i++) { 
		msm_geni_serial_ports[i].uport.iotype = UPIO_MEM;
		msm_geni_serial_ports[i].uport.ops = &msm_geni_serial_pops;
		msm_geni_serial_ports[i].uport.flags = UPF_BOOT_AUTOCONF;
		msm_geni_serial_ports[i].uport.line = i;
	}

	for (i = 0; i < GENI_UART_CONS_PORTS; i++) { 
		msm_geni_console_port.uport.iotype = UPIO_MEM;
		msm_geni_console_port.uport.ops = &msm_geni_console_pops; //这个操作函数结构体在上面
		msm_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF; 
		msm_geni_console_port.uport.line = i;
	}

	ret = console_register(&msm_geni_console_driver);  //看下面,这个函数命名有点坑,实际上就是调用了 uart_register_driver().
	if (ret)
		return ret;

	ret = uart_register_driver(&msm_geni_serial_hs_driver);//看上去是有register了两个 uart driver.
	if (ret) {
		uart_unregister_driver(&msm_geni_console_driver);  
		return ret;
	}

	ret = platform_driver_register(&msm_geni_serial_platform_driver); //再注册一个platform driver
	if (ret) {
		console_unregister(&msm_geni_console_driver);
		uart_unregister_driver(&msm_geni_serial_hs_driver);
		return ret;
	}

	pr_info("%s: Driver initialized", __func__);
	return ret;
}

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

/**
 *	uart_register_driver - register a driver with the uart core layer
 *	@drv: low level driver structure
 *
 *	Register a uart driver with the core driver.  We in turn register
 *	with the tty layer, and initialise the core driver per-port state.
 *
 *	We have a proc file in /proc/tty/driver which is named after the
 *	normal driver.
 *
 *	drv->port should be NULL, and the per-port structures should be
 *	registered using uart_add_one_port after this call has succeeded.
 */
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);  //nr是15.分配多个state
	if (!drv->state)
		goto out;

	normal = alloc_tty_driver(drv->nr); //normal是一个 struct tty_driver *
	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); //就是normal->ops=&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;  //每一个state有一个tty_port

		tty_port_init(port); //这个init主要是设置这个port的一些参数
		port->ops = &uart_port_ops; //port的操作函数结构体是uart_port_ops
	}

	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;
}

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

2.2 probe函数

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

static int msm_geni_serial_probe(struct platform_device *pdev)
{
	int ret = 0;
	int line;
	struct msm_geni_serial_port *dev_port;
	struct uart_port *uport;
	struct resource *res;
	struct uart_driver *drv;
	const struct of_device_id *id;
	bool is_console = false;
	struct platform_device *wrapper_pdev;
	struct device_node *wrapper_ph_node;
	u32 wake_char = 0;

	id = of_match_device(msm_geni_device_tbl, &pdev->dev);  //根据这个id,选出的是我们的msm_geni_console_driver
	if (id) {
		dev_dbg(&pdev->dev, "%s: %s\n", __func__, id->compatible);
		drv = (struct uart_driver *)id->data;
	} else {
		dev_err(&pdev->dev, "%s: No matching device found", __func__);
		return -ENODEV;
	}

	if (pdev->dev.of_node) {
		if (drv->cons)
			line = of_alias_get_id(pdev->dev.of_node, "serial"); //这个.
		else
			line = of_alias_get_id(pdev->dev.of_node, "hsuart");
	} else {
		line = pdev->id;
	}

	if (line < 0)
		line = atomic_inc_return(&uart_line_id) - 1;

	if ((line < 0) || (line >= GENI_UART_NR_PORTS))
		return -ENXIO;
	is_console = (drv->cons ? true : false);  //我们的这个drv是有cons的呀.
	dev_port = get_port_from_line(line, is_console); //又出来一个新的结构,struct msm_geni_serial_port *dev_port;实际上这个结构在init函数里面有初始化的.
	if (IS_ERR_OR_NULL(dev_port)) {
		ret = PTR_ERR(dev_port);
		dev_err(&pdev->dev, "Invalid line %d(%d)\n",
					line, ret);
		goto exit_geni_serial_probe;
	}

	uport = &dev_port->uport;  //重点,上面刚出现的这个结构指向的uport就是很重要的uport,也就是struct uart_port *uport;

	/* Don't allow 2 drivers to access the same port */
	if (uport->private_data) {
		ret = -ENODEV;
		goto exit_geni_serial_probe;
	}

	uport->dev = &pdev->dev;

	......//中间做了不少对uport的设置.
	

	return uart_add_one_port(drv, uport); //将drv和uport绑定在一起.

exit_geni_serial_probe:
	return ret;
}

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

 

/**
 *	uart_add_one_port - attach a driver-defined port structure
 *	@drv: pointer to the uart low level driver structure for this port
 *	@uport: uart port structure to use for this port.
 *
 *	This allows the driver to register its own uart_port structure
 *	with the core driver.  The main purpose is to allow the low
 *	level uart drivers to expand uart_port, rather than having yet
 *	more levels of structures.
 */
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;
	int num_groups;

	BUG_ON(in_interrupt());

	if (uport->line >= drv->nr)
		return -EINVAL;

	state = drv->state + uport->line;  //uport->line=0.
	port = &state->port; //struct tty_port *port;

	mutex_lock(&port_mutex);
	mutex_lock(&port->mutex);
	if (state->uart_port) {
		ret = -EINVAL;
		goto out;
	}

	/* Link the port to the driver state table and vice versa */
	atomic_set(&state->refcount, 1);
	init_waitqueue_head(&state->remove_wait);
	state->uart_port = uport;   //当当当!state指向的uart_port结构就是传参传进来的uport
	uport->state = state; //相应地,uport的state也就是传进来的drv->state.这两步就是将drv和uport绑定在一起的!

	state->pm_state = UART_PM_STATE_UNDEFINED;
	uport->cons = drv->cons;  //uport的cons就是drv的cons
	uport->minor = drv->tty_driver->minor_start + uport->line;

	/*
	 * 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);
	}
	if (uport->cons && uport->dev)
		of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
	
	//这个uart_configure_port里面会根据条件判断是否需要调用一个register_console.
//并且会加入到console_drivers的list中.这个如果注册成功,在printk最后会调到uport->cons->write函数.
	//也就是开篇的static struct console cons_ops中的write函数.
	uart_configure_port(drv, state, uport);  

	port->console = uart_console(uport);

	num_groups = 2;
	if (uport->attr_group)
		num_groups++;

	uport->tty_groups = kcalloc(num_groups, sizeof(*uport->tty_groups),
				    GFP_KERNEL);
	if (!uport->tty_groups) {
		ret = -ENOMEM;
		goto out;
	}
	uport->tty_groups[0] = &tty_dev_attr_group;
	if (uport->attr_group)
		uport->tty_groups[1] = uport->attr_group;

	/*
	 * Register the port whether it's detected or not.  This allows
	 * setserial to be used to alter this port's parameters.
	 */
	tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
			uport->line, uport->dev, port, uport->tty_groups);
	if (likely(!IS_ERR(tty_dev))) {
		device_set_wakeup_capable(tty_dev, 1);
	} else {
		dev_err(uport->dev, "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进行通信的.

3 printk分析

直接上code:

/**
 * printk - print a kernel message
 * @fmt: format string
 *
 * This is printk(). It can be called from any context. We want it to work.
 *
 * We try to grab the console_lock. If we succeed, it's easy - we log the
 * output and call the console drivers.  If we fail to get the semaphore, we
 * place the output into the log buffer and return. The current holder of
 * the console_sem will notice the new output in console_unlock(); and will
 * send it to the consoles before releasing the lock.
 *
 * One effect of this deferred printing is that code which calls printk() and
 * then changes console_loglevel may break. This is because console_loglevel
 * is inspected when the actual printing occurs.
 *
 * See also:
 * printf(3)
 *
 * See the vsnprintf() documentation for format string extensions over C99.
 */
asmlinkage __visible int printk(const char *fmt, ...)
{
	va_list args;
	int r;

	va_start(args, fmt);
	r = vprintk_func(fmt, args);
	va_end(args);

	return r;
}
EXPORT_SYMBOL(printk);

接着看:

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

再看:

int vprintk_default(const char *fmt, va_list args)
{
	int r;

#ifdef CONFIG_KGDB_KDB  //这个是没有定义的...
	if (unlikely(kdb_trap_printk)) {
		r = vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
		return r;
	}
#endif
	r = vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);

	return r;
}
EXPORT_SYMBOL_GPL(vprintk_default);

继续往后看:

asmlinkage int vprintk_emit(int facility, int level,
			    const char *dict, size_t dictlen,
			    const char *fmt, va_list args)
{
	static bool recursion_bug;
	static char textbuf[LOG_LINE_MAX];
	static char textbuf1[LOG_LINE_MAX];
	char *text = textbuf;
	char *text1 = textbuf1;
	size_t text_len = 0;
	enum log_flags lflags = 0;
	unsigned long flags;
	int this_cpu;
	int printed_len = 0;
	int nmi_message_lost;
	bool in_sched = false;
	/* cpu currently holding logbuf_lock in this function */
	static unsigned int logbuf_cpu = UINT_MAX;
	

	if (level == LOGLEVEL_SCHED) {
		level = LOGLEVEL_DEFAULT;
		in_sched = true;   //如果传进来的level==LOGLEVEL_SCHED,那就设为true
	}

	boot_delay_msec(level);
	printk_delay();

	......
	/*
	 * The printf needs to come first; we need the syslog
	 * prefix which might be passed-in as a parameter.
	 */
	text_len = vscnprintf(text1, sizeof(textbuf1), fmt, args);

	......

#ifdef CONFIG_EARLY_PRINTK_DIRECT
	printascii(text1);
#endif

	if(needPrintTime)
	{
		char buf[64];
		size_t buf_size = sprintf(buf, "(CPU:%d-pid:%d:%s) ", smp_processor_id(), current->pid, current->comm);
		strncpy(text, buf, buf_size);
		strncpy(text + buf_size, text1, text_len);
		text_len += buf_size;
	}
	else
	{
		strncpy(text, text1, text_len);
	}

	if (level == LOGLEVEL_DEFAULT)
		level = default_message_loglevel;

	if (dict)
		lflags |= LOG_PREFIX|LOG_NEWLINE;

	printed_len += log_output(facility, level, lflags, dict, dictlen, text, text_len);
	
	if(lflags & LOG_NEWLINE)
	{
		needPrintTime = true;
	}
	else
	{
		needPrintTime = false;
	}

	logbuf_cpu = UINT_MAX;
	raw_spin_unlock(&logbuf_lock);
	lockdep_on();
	local_irq_restore(flags);

	/* If called from the scheduler, we can not call up(). */
	if (!in_sched) {
		lockdep_off();
		/*
		 * Try to acquire and then immediately release the console
		 * semaphore.  The release will print out buffers and wake up
		 * /dev/kmsg and syslog() users.
		 */
		if (console_trylock())
			console_unlock();  //这里...
		lockdep_on();
	}

	return printed_len;
}
EXPORT_SYMBOL(vprintk_emit);

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

那么再看call_console_drivers:

/*
 * Call the console drivers, asking them to write out
 * log_buf[start] to log_buf[end - 1].
 * The console_lock must be held.
 */
static void call_console_drivers(int level,
				 const char *ext_text, size_t ext_len,
				 const char *text, size_t len)
{
	struct console *con;

	trace_console_rcuidle(text, len);

	if (!console_drivers)
		return;

	for_each_console(con) {  //遍历console_drivers,实际上只有前面提到的一个地方有成功加到这个list中
		if(strncmp(con->name,"logk",4) != 0){
			if (level >= console_loglevel && !ignore_loglevel)
				continue;
			if (exclusive_console && con != exclusive_console)
				continue;
	}
		if (!(con->flags & CON_ENABLED))
			continue;
		if (!con->write)
			continue;
		if (!cpu_online(smp_processor_id()) &&
		    !(con->flags & CON_ANYTIME))
			continue;
		if (con->flags & CON_EXTENDED)
			con->write(con, ext_text, ext_len);
		else
			con->write(con, text, len);  //这个write函数就是最开始的console结构体中的write函数.
	}
}

至于上面这个函数中遍历的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的原理与直接使用

非常感谢!

 

你可能感兴趣的:(Android系统)