串口tty驱动之初始化

只分析串口驱动,和console相关的部分暂时省去。

内核串口部分需要用到tty部分,tty包含了tty内核和tty线路规程。这些是在串口代码之前初始化。

1.tty_ldisc线路规程的初始化

只要是函数

driver/tty/tty_io.c    

console_init()

  1. void __init console_init(void)  
  2. {  
  3.     initcall_t *call;  
  4.   
  5.     /* Setup the default TTY line discipline. */  
  6.     tty_ldisc_begin();  
  7.   
  8.     /* 
  9.      * set up the console device so that later boot sequences can 
  10.      * inform about problems etc.. 
  11.      */  
  12.     call = __con_initcall_start;  
  13.     while (call < __con_initcall_end) {  
  14.         (*call)();  
  15.         call++;  
  16.     }  
  17. }  
void __init console_init(void)
{
	initcall_t *call;

	/* Setup the default TTY line discipline. */
	tty_ldisc_begin();

	/*
	 * set up the console device so that later boot sequences can
	 * inform about problems etc..
	 */
	call = __con_initcall_start;
	while (call < __con_initcall_end) {
		(*call)();
		call++;
	}
}

此处和tty相关的就是函数tty_ldisc_begin(),内核通过此函数来初始化tty线路规程的相关操作。

  1. void tty_ldisc_begin(void)  
  2. {  
  3.     /* Setup the default TTY line discipline. */  
  4.     (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);  
  5. }  
void tty_ldisc_begin(void)
{
	/* Setup the default TTY line discipline. */
	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
}

tty_ldisc_begin函数原来是对tty_register_ldisc的封装,从名字就可以看出来这个是对tty线路规程的初始化操作(内核线路规程的简称是ldisc)

此处先看函数调用的两个参数,其中第一个参数N_TTY是一个宏,第二个参数是内核tty线路规程的操作方法集

  1. #define N_TTY       0  
  2. #define N_SLIP      1  
  3. #define N_MOUSE     2  
#define N_TTY		0
#define N_SLIP		1
#define N_MOUSE		2

  1. struct tty_ldisc_ops tty_ldisc_N_TTY = {  
  2.     .magic           = TTY_LDISC_MAGIC,  
  3.     .name            = "n_tty",  
  4.     .open            = n_tty_open,  
  5.     .close           = n_tty_close,  
  6.     .flush_buffer    = n_tty_flush_buffer,  
  7.     .chars_in_buffer = n_tty_chars_in_buffer,  
  8.     .read            = n_tty_read,  
  9.     .write           = n_tty_write,  
  10.     .ioctl           = n_tty_ioctl,  
  11.     .set_termios     = n_tty_set_termios,  
  12.     .poll            = n_tty_poll,  
  13.     .receive_buf     = n_tty_receive_buf,  
  14.     .write_wakeup    = n_tty_write_wakeup  
  15. };  
struct tty_ldisc_ops tty_ldisc_N_TTY = {
	.magic           = TTY_LDISC_MAGIC,
	.name            = "n_tty",
	.open            = n_tty_open,
	.close           = n_tty_close,
	.flush_buffer    = n_tty_flush_buffer,
	.chars_in_buffer = n_tty_chars_in_buffer,
	.read            = n_tty_read,
	.write           = n_tty_write,
	.ioctl           = n_tty_ioctl,
	.set_termios     = n_tty_set_termios,
	.poll            = n_tty_poll,
	.receive_buf     = n_tty_receive_buf,
	.write_wakeup    = n_tty_write_wakeup
};

然后具体的tty_register_ldisc函数源码如下:
  1. int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)  
  2. {  
  3.     unsigned long flags;  
  4.     int ret = 0;  
  5.   
  6.     if (disc < N_TTY || disc >= NR_LDISCS)  
  7.         return -EINVAL;  
  8.   
  9.     spin_lock_irqsave(&tty_ldisc_lock, flags);  
  10.     tty_ldiscs[disc] = new_ldisc;  
  11.     new_ldisc->num = disc;  
  12.     new_ldisc->refcount = 0;  
  13.     spin_unlock_irqrestore(&tty_ldisc_lock, flags);  
  14.   
  15.     return ret;  
  16. }  
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
{
	unsigned long flags;
	int ret = 0;

	if (disc < N_TTY || disc >= NR_LDISCS)
		return -EINVAL;

	spin_lock_irqsave(&tty_ldisc_lock, flags);
	tty_ldiscs[disc] = new_ldisc;
	new_ldisc->num = disc;
	new_ldisc->refcount = 0;
	spin_unlock_irqrestore(&tty_ldisc_lock, flags);

	return ret;
}
可以发现tty线路规程的操作很好理解。内核定义一个tty_ldiscs数组,然后根据数组下标来存放对应的线路规程的操作集,而这里的数组下标表示的就是具体的协议,在头文件中已经通过宏定义好了。例如N_TTY 0。

所以可以发现

ldisc[0] 存放的是N_TTY对应的线路规程操作集

ldisc[1]存放的是N_SLIP对应的线路规程操作集

ldisc[2]存放的就是N_MOUSE对应的线路规程操作集

依次类推。

此处就是ldisc[N_TTY] = tty_ldisc_N_TTY。

然后函数退出,最后返回到console_init()函数。

假如串口作为console,则console_init()会通过剩余的代码调用s3c_serial_console_init()执行console相关的初始化了。

  1. while (call < __con_initcall_end) {  
  2.     (*call)();  
  3.     call++;  
  4. }  
	while (call < __con_initcall_end) {
		(*call)();
		call++;
	}
由于本文只分析不做console的串口驱动,所以这部分代码和串口无关。console_init()函数和串口相关的操作就是

之前调用的tty_ldisc_begin函数了。这个tty_ldisc初始化到这一步就暂时放在一边,待到后面通过open函数中再来调用

此处已注册的的tty_disc线路规程

2.tty_driver初始化 uart_driver中uart_state的初始化

2.1tty_driver相关部分的初始化

初始化线路规程后,系统开始初始化tty_driver。

而tty_driver需要初始化很多字段例如

tty_driver.owner

tty_driver.driver_name
tty_driver.name
tty_driver.major
tty_driver.minor_start  
tty_driver.type    
这些字段根据不同的tty_driver需要初始化成不同的参数。

根据LDD3的解释,内核的tty_driver有三种:控制台、串口、pty。这三种对应着三种不同的tty_driver,而这三种tty_driver对应的上述字段是不同的。

那么这些参数来源哪里呢?来源于程序员的定义,编译之初确定好这些参数后,内核最后会将其赋值给tty_driver。

那这些参数定义在哪里呢?定义在uart_driver中,这个就是驱动需要修改的一个数据结构。

这就相当于在tty_driver外面就加了一层uart_driver。而uart_driver中的部分字段和tty_driver中的部分字段数值是一样的。

此处的uart_driver定义如下:

  1. static struct uart_driver s3c24xx_uart_drv = {  
  2.     .owner      = THIS_MODULE,  
  3.     .dev_name   = "s3c2410_serial",  
  4.     .nr     = CONFIG_SERIAL_SAMSUNG_UARTS,  
  5.     .cons       = S3C24XX_SERIAL_CONSOLE,  
  6.     .driver_name    = S3C24XX_SERIAL_NAME,  
  7.     .major      = S3C24XX_SERIAL_MAJOR,  
  8.     .minor      = S3C24XX_SERIAL_MINOR,  
  9. };  
static struct uart_driver s3c24xx_uart_drv = {
	.owner		= THIS_MODULE,
	.dev_name	= "s3c2410_serial",
	.nr		= CONFIG_SERIAL_SAMSUNG_UARTS,
	.cons		= S3C24XX_SERIAL_CONSOLE,
	.driver_name	= S3C24XX_SERIAL_NAME,
	.major		= S3C24XX_SERIAL_MAJOR,
	.minor		= S3C24XX_SERIAL_MINOR,
};

内核通过

driver/serial/samsung.c文件中的

s3c24xx_modinit()函数来进行tty_driver的初始化,参数就是韦uart_driver结构的s3c24xx_uart_drv。

  1. static int __init s3c24xx_serial_modinit(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     ret = uart_register_driver(&s3c24xx_uart_drv);  
  6.     if (ret < 0) {  
  7.         printk(KERN_ERR "failed to register UART driver\n");  
  8.         return -1;  
  9.     }  
  10.   
  11.     return 0;  
  12. }  
static int __init s3c24xx_serial_modinit(void)
{
	int ret;

	ret = uart_register_driver(&s3c24xx_uart_drv);
	if (ret < 0) {
		printk(KERN_ERR "failed to register UART driver\n");
		return -1;
	}

	return 0;
}
uart_register_driver函数如下:
  1. int uart_register_driver(struct uart_driver *drv)  
  2. {  
  3.     struct tty_driver *normal;  
  4.     int i, retval;  
  5.   
  6.     BUG_ON(drv->state);  
  7.   
  8.     /* 
  9.      * Maybe we should be using a slab cache for this, especially if 
  10.      * we have a large number of ports to handle. 
  11.      */  
  12.     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);  
  13.     if (!drv->state)  
  14.         goto out;  
  15.   
  16.     normal = alloc_tty_driver(drv->nr);  
  17.     if (!normal)  
  18.         goto out_kfree;  
  19.   
  20.     drv->tty_driver = normal;  
  21.     normal->owner        = drv->owner;  
  22.     normal->driver_name  = drv->driver_name;  
  23.     normal->name     = drv->dev_name;  
  24.     normal->major        = drv->major;  
  25.     normal->minor_start  = drv->minor;  
  26.     normal->type     = TTY_DRIVER_TYPE_SERIAL;  
  27.     normal->subtype      = SERIAL_TYPE_NORMAL;  
  28.     normal->init_termios = tty_std_termios;  
  29.     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;  
  30.     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;  
  31.     normal->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;  
  32.     normal->driver_state    = drv;  
  33.     <span style="color: rgb(255, 0, 0);">tty_set_operations(normal, &uart_ops);</span>  
  34.   
  35.     /* 
  36.      * Initialise the UART state(s). 
  37.      */  
  38.     for (i = 0; i < drv->nr; i++) {  
  39.         struct uart_state *state = drv->state + i;  
  40.         struct tty_port *port = &state->port;  
  41.   
  42.         tty_port_init(port);  
  43.         port->ops = &uart_port_ops;  
  44.         port->close_delay     = 500; /* .5 seconds */  
  45.         port->closing_wait    = 30000;   /* 30 seconds */  
  46.         tasklet_init(&state->tlet, uart_tasklet_action,  
  47.                  (unsigned long)state);  
  48.     }  
  49.   
  50.     <span style="color: rgb(255, 0, 0);">retval = tty_register_driver(normal);</span>  
  51.     if (retval >= 0)  
  52.         return retval;  
  53.   
  54.     put_tty_driver(normal);  
  55. out_kfree:  
  56.     kfree(drv->state);  
  57. out:  
  58.     return -ENOMEM;  
  59. }  
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->owner		= drv->owner;
	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     = 500;	/* .5 seconds */
		port->closing_wait    = 30000;	/* 30 seconds */
		tasklet_init(&state->tlet, uart_tasklet_action,
			     (unsigned long)state);
	}

	retval = tty_register_driver(normal);
	if (retval >= 0)
		return retval;

	put_tty_driver(normal);
out_kfree:
	kfree(drv->state);
out:
	return -ENOMEM;
}

这个函数就是用来初始化tty_drive的,并且可以看见tty_driver中的字段初始化的数值就是函数传递进来的uart_driver中的对应字段。

此外此函数还需要注意的地方都已经标红。

其中,通过    tty_set_operations(normal, &uart_ops);

指定了tty_driver的操作集tty_operation为uart_ops,假如是其他的tty_driver相信这个ops也是需要修改的。

uart_ops定义如下

  1. static const struct tty_operations uart_ops = {  
  2.     .open       = uart_open,  
  3.     .close      = uart_close,  
  4.     .write      = uart_write,  
  5.     .put_char   = uart_put_char,  
  6.     .flush_chars    = uart_flush_chars,  
  7.     .write_room = uart_write_room,  
  8.     .chars_in_buffer= uart_chars_in_buffer,  
  9.     .flush_buffer   = uart_flush_buffer,  
  10.     .ioctl      = uart_ioctl,  
  11.     .throttle   = uart_throttle,  
  12.     .unthrottle = uart_unthrottle,  
  13.     .send_xchar = uart_send_xchar,  
  14.     .set_termios    = uart_set_termios,  
  15.     .set_ldisc  = uart_set_ldisc,  
  16.     .stop       = uart_stop,  
  17.     .start      = uart_start,  
  18.     .hangup     = uart_hangup,  
  19.     .break_ctl  = uart_break_ctl,  
  20.     .wait_until_sent= uart_wait_until_sent,  
  21. #ifdef CONFIG_PROC_FS  
  22.     .proc_fops  = &uart_proc_fops,  
  23. #endif  
  24.     .tiocmget   = uart_tiocmget,  
  25.     .tiocmset   = uart_tiocmset,  
  26.     .get_icount = uart_get_icount,  
  27. #ifdef CONFIG_CONSOLE_POLL  
  28.     .poll_init  = uart_poll_init,  
  29.     .poll_get_char  = uart_poll_get_char,  
  30.     .poll_put_char  = uart_poll_put_char,  
  31. #endif  
  32. };  
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
};
然后函数通过
  1. retval = tty_register_driver(normal);  
	retval = tty_register_driver(normal);

实现tty_driver的"注册"。具体的注册函数如下:

  1. int tty_register_driver(struct tty_driver *driver)  
  2. {  
  3.     int error;  
  4.     int i;  
  5.     dev_t dev;  
  6.     void **p = NULL;  
  7.     struct device *d;  
  8.   
  9.     if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {  
  10.         p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);  //注意这一句,为tty_driver分配了一个tty_struct *指针数组,并且使用的kzalloc方式也就是说。初始化的时候该数组内容是零!这点在tty_open判断的时候会用到!  
  11.         if (!p)   
  12.             return -ENOMEM;  
  13.     }  
  14.   
  15.     if (!driver->major) {  
  16.         error = alloc_chrdev_region(&dev, driver->minor_start,  
  17.                         driver->num, driver->name);  
  18.         if (!error) {  
  19.             driver->major = MAJOR(dev);  
  20.             driver->minor_start = MINOR(dev);  
  21.         }  
  22.     } else {  
  23.         dev = MKDEV(driver->major, driver->minor_start);  
  24.         error = register_chrdev_region(dev, driver->num, driver->name);  
  25.     }  
  26.     if (error < 0) {  
  27.         kfree(p);  
  28.         return error;  
  29.     }  
  30.   
  31.     if (p) {  
  32.         driver->ttys = (struct tty_struct **)p;  
  33.         driver->termios = (struct ktermios **)(p + driver->num);  
  34.     } else {  
  35.         driver->ttys = NULL;  
  36.         driver->termios = NULL;  
  37.     }  
  38.   
  39.     <span style="color: rgb(255, 0, 0);">cdev_init(&driver->cdev, &tty_fops);</span>  
  40.     driver->cdev.owner = driver->owner;  
  41.     error = cdev_add(&driver->cdev, dev, driver->num);  
  42.     if (error) {  
  43.         unregister_chrdev_region(dev, driver->num);  
  44.         driver->ttys = NULL;  
  45.         driver->termios = NULL;  
  46.         kfree(p);  
  47.         return error;  
  48.     }  
  49.   
  50.     mutex_lock(&tty_mutex);  
  51.     <span style="color: rgb(255, 0, 0);">list_add(&driver->tty_drivers, &tty_drivers);</span>  
  52.     mutex_unlock(&tty_mutex);  
  53.   
  54.     if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {  
  55.         for (i = 0; i < driver->num; i++) {  
  56.             d = tty_register_device(driver, i, NULL);  
  57.             if (IS_ERR(d)) {  
  58.                 error = PTR_ERR(d);  
  59.                 goto err;  
  60.             }  
  61.         }  
  62.     }  
  63.     proc_tty_register_driver(driver);  
  64.     driver->flags |= TTY_DRIVER_INSTALLED;  
  65.     return 0;  
  66.   
  67. err:  
  68.     for (i--; i >= 0; i--)  
  69.         tty_unregister_device(driver, i);  
  70.   
  71.     mutex_lock(&tty_mutex);  
  72.     list_del(&driver->tty_drivers);  
  73.     mutex_unlock(&tty_mutex);  
  74.   
  75.     unregister_chrdev_region(dev, driver->num);  
  76.     driver->ttys = NULL;  
  77.     driver->termios = NULL;  
  78.     kfree(p);  
  79.     return error;  
  80. }  
int tty_register_driver(struct tty_driver *driver)
{
	int error;
	int i;
	dev_t dev;
	void **p = NULL;
	struct device *d;

	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);  //注意这一句,为tty_driver分配了一个tty_struct *指针数组,并且使用的kzalloc方式也就是说。初始化的时候该数组内容是零!这点在tty_open判断的时候会用到!
		if (!p) 
			return -ENOMEM;
	}

	if (!driver->major) {
		error = alloc_chrdev_region(&dev, driver->minor_start,
						driver->num, driver->name);
		if (!error) {
			driver->major = MAJOR(dev);
			driver->minor_start = MINOR(dev);
		}
	} else {
		dev = MKDEV(driver->major, driver->minor_start);
		error = register_chrdev_region(dev, driver->num, driver->name);
	}
	if (error < 0) {
		kfree(p);
		return error;
	}

	if (p) {
		driver->ttys = (struct tty_struct **)p;
		driver->termios = (struct ktermios **)(p + driver->num);
	} else {
		driver->ttys = NULL;
		driver->termios = NULL;
	}

	cdev_init(&driver->cdev, &tty_fops);
	driver->cdev.owner = driver->owner;
	error = cdev_add(&driver->cdev, dev, driver->num);
	if (error) {
		unregister_chrdev_region(dev, driver->num);
		driver->ttys = NULL;
		driver->termios = NULL;
		kfree(p);
		return error;
	}

	mutex_lock(&tty_mutex);
	list_add(&driver->tty_drivers, &tty_drivers);
	mutex_unlock(&tty_mutex);

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
		for (i = 0; i < driver->num; i++) {
			d = tty_register_device(driver, i, NULL);
			if (IS_ERR(d)) {
				error = PTR_ERR(d);
				goto err;
			}
		}
	}
	proc_tty_register_driver(driver);
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;

err:
	for (i--; i >= 0; i--)
		tty_unregister_device(driver, i);

	mutex_lock(&tty_mutex);
	list_del(&driver->tty_drivers);
	mutex_unlock(&tty_mutex);

	unregister_chrdev_region(dev, driver->num);
	driver->ttys = NULL;
	driver->termios = NULL;
	kfree(p);
	return error;
}
该“注册”主要包含了如下工作:

1.根据主设备号、次设备号,注册一个字符驱动,其操作集为tty_fops

  1. static const struct file_operations tty_fops = {  
  2.     .llseek     = no_llseek,  
  3.     .read       = tty_read,  
  4.     .write      = tty_write,  
  5.     .poll       = tty_poll,  
  6.     .unlocked_ioctl = tty_ioctl,  
  7.     .compat_ioctl   = tty_compat_ioctl,  
  8.     .open       = tty_open,  
  9.     .release    = tty_release,  
  10.     .fasync     = tty_fasync,  
  11. };  
static const struct file_operations tty_fops = {
	.llseek		= no_llseek,
	.read		= tty_read,
	.write		= tty_write,
	.poll		= tty_poll,
	.unlocked_ioctl	= tty_ioctl,
	.compat_ioctl	= tty_compat_ioctl,
	.open		= tty_open,
	.release	= tty_release,
	.fasync		= tty_fasync,
};

并在/dev文件夹下生成对应的设备文件节点。

2.将tty_driver添加到tty_driver链表tty_drivers中,内核通过维护tty_drivers链表来维护tty_driver。

前面提到了内核的tty_driver主要包含三大类console、串口、pty。

完成之后tty_driver初始化也结束,然后进去s3c24xx的uart相关的初始化。

2.uart_driver的部分初始化

此函数关于uart_driver部分的初始化主要是初始化了uart_driver中的uart_state这个成员。

uart_state也是一个结构体,其中包含uart_pot、tty_port两个成员。而系统中对具体硬件的操作就是通过uart_port.ops

来操作的,这个uart_port需要重点留意下,但是在此函数中就只初始化了tty_port这个成员,uart_port的初始化

在后面的platform_driver.probe函数中初始化,这也正好说明了uart_port是和具体硬件精密相关的。

3.uart_port初始化

uart_port的初始化是通过platform_driver中的probe函数来完成的。如何调用到probe函数的就不说了。

probe函数如下
  1. static int s3c2440_serial_probe(struct platform_device *dev)  
  2. {  
  3.     dbg("s3c2440_serial_probe: dev=%p\n", dev);  
  4.     return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);  
  5. }  
static int s3c2440_serial_probe(struct platform_device *dev)
{
	dbg("s3c2440_serial_probe: dev=%p\n", dev);
	return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
}
其第二个参数s3c2440_uart_inf如下
  1. static struct s3c24xx_uart_info s3c2440_uart_inf = {  
  2.     .name       = "Samsung S3C2440 UART",  
  3.     .type       = PORT_S3C2440,  
  4.     .fifosize   = 64,  
  5.     .rx_fifomask    = S3C2440_UFSTAT_RXMASK,  
  6.     .rx_fifoshift   = S3C2440_UFSTAT_RXSHIFT,  
  7.     .rx_fifofull    = S3C2440_UFSTAT_RXFULL,  
  8.     .tx_fifofull    = S3C2440_UFSTAT_TXFULL,  
  9.     .tx_fifomask    = S3C2440_UFSTAT_TXMASK,  
  10.     .tx_fifoshift   = S3C2440_UFSTAT_TXSHIFT,  
  11.     .get_clksrc = s3c2440_serial_getsource,  
  12.     .set_clksrc = s3c2440_serial_setsource,  
  13.     .reset_port = s3c2440_serial_resetport,  
  14. };  
static struct s3c24xx_uart_info s3c2440_uart_inf = {
	.name		= "Samsung S3C2440 UART",
	.type		= PORT_S3C2440,
	.fifosize	= 64,
	.rx_fifomask	= S3C2440_UFSTAT_RXMASK,
	.rx_fifoshift	= S3C2440_UFSTAT_RXSHIFT,
	.rx_fifofull	= S3C2440_UFSTAT_RXFULL,
	.tx_fifofull	= S3C2440_UFSTAT_TXFULL,
	.tx_fifomask	= S3C2440_UFSTAT_TXMASK,
	.tx_fifoshift	= S3C2440_UFSTAT_TXSHIFT,
	.get_clksrc	= s3c2440_serial_getsource,
	.set_clksrc	= s3c2440_serial_setsource,
	.reset_port	= s3c2440_serial_resetport,
};

先简单介绍下uart_port结构的初始化。uart_port和系统中具体的硬件对应,每个uart都有自己对应的uart_port。而这个结构体的初始化由点类似于

前面说的tty_driver,并不是直接初始化tty_driver的,而是在tty_driver外面包了一层uart_driver。此处也是的在uart_port外面包了一层struct s3c24xx_uart_port 。

这样s3c24xx_uart_port除了包含uart_port这个重要结构外还可以存放s3c24xx系列串口的特有信息。

uart_port的初始化信息主要来源于struct s3c24xx_uart_info,如上所示。这些数值会在probe函数中赋值给uart_port用于初始化。

初始化uart_port之后就是调用函数将这个初始化好的uart_port和前面的uart_driver关联上。因为uart_driver中即有tty相关的tty_driver又有好友uart_port信息的uart_state

,这样tty和uart就有可能关联上了。下面从probe函数开始分析具体代码。


s3c24xx_serial_probe函数如下:

  1. int s3c24xx_serial_probe(struct platform_device *dev,  
  2.              struct s3c24xx_uart_info *info)  
  3. {  
  4.     struct s3c24xx_uart_port *ourport;  
  5.     int ret;  
  6.   
  7.     dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);  
  8.   
  9.     <span style="color: rgb(255, 0, 0);">ourport = &s3c24xx_serial_ports[probe_index];</span>  
  10.     probe_index++;  
  11.   
  12.     dbg("%s: initialising port %p...\n", __func__, ourport);  
  13.   
  14.     <span style="color: rgb(255, 0, 0);">ret = s3c24xx_serial_init_port(ourport, info, dev);</span>  
  15.     if (ret < 0)  
  16.         goto probe_err;  
  17.   
  18.     dbg("%s: adding port\n", __func__);  
  19.     <span style="color: rgb(255, 0, 0);">uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);</span>  
  20.     platform_set_drvdata(dev, &ourport->port);  
  21.   
  22.     ret = device_create_file(&dev->dev, &dev_attr_clock_source);  
  23.     if (ret < 0)  
  24.         printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);  
  25.   
  26.     ret = s3c24xx_serial_cpufreq_register(ourport);  
  27.     if (ret < 0)  
  28.         dev_err(&dev->dev, "failed to add cpufreq notifier\n");  
  29.   
  30.     return 0;  
  31.   
  32.  probe_err:  
  33.     return ret;  
  34. }  
int s3c24xx_serial_probe(struct platform_device *dev,
			 struct s3c24xx_uart_info *info)
{
	struct s3c24xx_uart_port *ourport;
	int ret;

	dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);

	ourport = &s3c24xx_serial_ports[probe_index];
	probe_index++;

	dbg("%s: initialising port %p...\n", __func__, ourport);

	ret = s3c24xx_serial_init_port(ourport, info, dev);
	if (ret < 0)
		goto probe_err;

	dbg("%s: adding port\n", __func__);
	uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
	platform_set_drvdata(dev, &ourport->port);

	ret = device_create_file(&dev->dev, &dev_attr_clock_source);
	if (ret < 0)
		printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);

	ret = s3c24xx_serial_cpufreq_register(ourport);
	if (ret < 0)
		dev_err(&dev->dev, "failed to add cpufreq notifier\n");

	return 0;

 probe_err:
	return ret;
}

函数总重要的语句全部标红了。先分析第一句

  1. <span style="color: rgb(255, 0, 0);">ourport = &s3c24xx_serial_ports[probe_index];</span>  
	ourport = &s3c24xx_serial_ports[probe_index];
这个主要是从串口数组s3c24xx_serial_ports中选定初始化的串口。这里需要注意的uart_port中的ops,标红的那句。这里是具体操作s3c24xx串口的操作函数集。

后面具体的write  read等函数最终调用的是这里操作集,都是最底层的硬件操作。

  1. static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {  
  2.     [0] = {  
  3.         .port = {  
  4.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),  
  5.             .iotype     = UPIO_MEM,  
  6.             .irq        = IRQ_S3CUART_RX0,  
  7.             .uartclk    = 0,  
  8.             .fifosize   = 16,  
  9.             <span style="color: rgb(255, 0, 0);">.ops       = &s3c24xx_serial_ops,</span>  
  10.             .flags      = UPF_BOOT_AUTOCONF,  
  11.             .line       = 0,  
  12.         }  
  13.     },  
  14.     [1] = {  
  15.         .port = {  
  16.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),  
  17.             .iotype     = UPIO_MEM,  
  18.             .irq        = IRQ_S3CUART_RX1,  
  19.             .uartclk    = 0,  
  20.             .fifosize   = 16,  
  21.             .ops        = &s3c24xx_serial_ops,  
  22.             .flags      = UPF_BOOT_AUTOCONF,  
  23.             .line       = 1,  
  24.         }  
  25.     },  
  26. #if CONFIG_SERIAL_SAMSUNG_UARTS > 2  
  27.   
  28.     [2] = {  
  29.         .port = {  
  30.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),  
  31.             .iotype     = UPIO_MEM,  
  32.             .irq        = IRQ_S3CUART_RX2,  
  33.             .uartclk    = 0,  
  34.             .fifosize   = 16,  
  35.             .ops        = &s3c24xx_serial_ops,  
  36.             .flags      = UPF_BOOT_AUTOCONF,  
  37.             .line       = 2,  
  38.         }  
  39.     },  
  40. #endif  
  41. #if CONFIG_SERIAL_SAMSUNG_UARTS > 3  
  42.     [3] = {  
  43.         .port = {  
  44.             .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),  
  45.             .iotype     = UPIO_MEM,  
  46.             .irq        = IRQ_S3CUART_RX3,  
  47.             .uartclk    = 0,  
  48.             .fifosize   = 16,  
  49.             .ops        = &s3c24xx_serial_ops,  
  50.             .flags      = UPF_BOOT_AUTOCONF,  
  51.             .line       = 3,  
  52.         }  
  53.     }  
  54. #endif  
  55. };  
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
	[0] = {
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
			.iotype		= UPIO_MEM,
			.irq		= IRQ_S3CUART_RX0,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 0,
		}
	},
	[1] = {
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
			.iotype		= UPIO_MEM,
			.irq		= IRQ_S3CUART_RX1,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 1,
		}
	},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

	[2] = {
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
			.iotype		= UPIO_MEM,
			.irq		= IRQ_S3CUART_RX2,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 2,
		}
	},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
	[3] = {
		.port = {
			.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
			.iotype		= UPIO_MEM,
			.irq		= IRQ_S3CUART_RX3,
			.uartclk	= 0,
			.fifosize	= 16,
			.ops		= &s3c24xx_serial_ops,
			.flags		= UPF_BOOT_AUTOCONF,
			.line		= 3,
		}
	}
#endif
};
确定将要初始化的uart后,通过函数
  1. ret = s3c24xx_serial_init_port(ourport, info, dev);  
	ret = s3c24xx_serial_init_port(ourport, info, dev);

进行初始化了。根据前面介绍的,他的初始化的信息主要来源于那个s3c2440_uart_inf 结构体中的信息。

uart_port函数具体的初始化就不展开分析了,里面都是些赋值的操作。

接着分析uart_port和uart_driver关联的操作。这一步是通过函数

  1. uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);  
	uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
实现的。可以发现这个函数的第一个参数uart_driver是s3c24xx_uart_drv,和前面tty_driver初始化中提到的通过uart_register_driver注册的uart_driver是一样的!

这样uart_driver中的uart_state也就初始化好了,其中uart_state包含了uart_port的信息。这个uart_port是和具体的串口硬件相关的。

总结

到这一步所有的tty和uart初始化的部分算是完成了。但是目前还不能进行read、write操作。

因为还有一个重要的函数tty_open没有分析,作为tty设备的uart,这个tty_open函数也是非常重要的,下篇分析。

初始化的工作主要是

1.初始化uart_driver结构体,包括初始化uart_driver结构体中的tty_driver uart_state。

2.uart_state部分的初始化主要是初始化其中的uart_port,这部分的初始化在probe函数总完成

修改驱动需要设计的数据结构

1.uart_driver

uart_driver中的数据用于初始化tty_driver

2.s3c24xx_uart_info

用于初始化uart_port

3.s3c24xx_uart_port或者说uart_port。

uart_port的初始化,在s3c24xx的uart驱动中uart_port是内嵌在s3c24xx_uart_port中的

4.uart_ops

最底层的硬件操作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值