tty驱动初始化

只分析tty层驱动,涉及到到console和uart相关的部分暂时只简略介绍。

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

1.tty_ldisc线路规程的初始化

只要是函数

driver/tty/tty_io.c    

console_init()

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线路规程的相关操作。

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线路规程的操作方法集

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

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函数源码如下:

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相关的初始化了。

	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定义如下:

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。

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函数如下:

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定义如下

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
};
然后函数通过

	retval = tty_register_driver(normal);

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

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

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函数如下

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如下

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函数如下:

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

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

	ourport = &s3c24xx_serial_ports[probe_index];
这个主要是从串口数组s3c24xx_serial_ports中选定初始化的串口。这里需要注意的uart_port中的ops,标红的那句。这里是具体操作s3c24xx串口的操作函数集。

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

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后,通过函数

	ret = s3c24xx_serial_init_port(ourport, info, dev);

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

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

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

	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
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值