只分析串口驱动,和console相关的部分暂时省去。
内核串口部分需要用到tty部分,tty包含了tty内核和tty线路规程。这些是在串口代码之前初始化。
1.tty_ldisc线路规程的初始化
只要是函数
driver/tty/tty_io.c
console_init()
[cpp] view plain copy print ?
- void __init console_init(void)
- {
- initcall_t *call;
-
-
- tty_ldisc_begin();
-
-
-
-
-
- call = __con_initcall_start;
- while (call < __con_initcall_end) {
- (*call)();
- call++;
- }
- }
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线路规程的相关操作。
[cpp] view plain copy print ?
- void tty_ldisc_begin(void)
- {
-
- (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_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线路规程的操作方法集
[cpp] view plain copy print ?
- #define N_TTY 0
- #define N_SLIP 1
- #define N_MOUSE 2
#define N_TTY 0
#define N_SLIP 1
#define N_MOUSE 2
[cpp] view plain copy print ?
- 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
- };
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函数源码如下:
[cpp] view plain copy print ?
- 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;
- }
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相关的初始化了。
[cpp] view plain copy print ?
- while (call < __con_initcall_end) {
- (*call)();
- call++;
- }
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定义如下:
[cpp] view plain copy print ?
- 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,
- };
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。
[cpp] view plain copy print ?
- 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;
- }
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函数如下:
[cpp] view plain copy print ?
- int uart_register_driver(struct uart_driver *drv)
- {
- struct tty_driver *normal;
- int i, retval;
-
- BUG_ON(drv->state);
-
-
-
-
-
- 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;
- <span style="color: rgb(255, 0, 0);">tty_set_operations(normal, &uart_ops);</span>
-
-
-
-
- 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;
- port->closing_wait = 30000;
- tasklet_init(&state->tlet, uart_tasklet_action,
- (unsigned long)state);
- }
-
- <span style="color: rgb(255, 0, 0);">retval = tty_register_driver(normal);</span>
- if (retval >= 0)
- return retval;
-
- put_tty_driver(normal);
- out_kfree:
- kfree(drv->state);
- out:
- return -ENOMEM;
- }
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定义如下
[cpp] view plain copy print ?
- 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
- };
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
};
然后函数通过
[cpp] view plain copy print ?
- retval = tty_register_driver(normal);
retval = tty_register_driver(normal);
实现tty_driver的"注册"。具体的注册函数如下:
[cpp] view plain copy print ?
- 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);
- 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;
- }
-
- <span style="color: rgb(255, 0, 0);">cdev_init(&driver->cdev, &tty_fops);</span>
- 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);
- <span style="color: rgb(255, 0, 0);">list_add(&driver->tty_drivers, &tty_drivers);</span>
- 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;
- }
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
[cpp] view plain copy print ?
- 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,
- };
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函数如下
[cpp] view plain copy print ?
- 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);
- }
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如下
[cpp] view plain copy print ?
- 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,
- };
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函数如下:
[cpp] view plain copy print ?
- 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);
-
- <span style="color: rgb(255, 0, 0);">ourport = &s3c24xx_serial_ports[probe_index];</span>
- probe_index++;
-
- dbg("%s: initialising port %p...\n", __func__, ourport);
-
- <span style="color: rgb(255, 0, 0);">ret = s3c24xx_serial_init_port(ourport, info, dev);</span>
- if (ret < 0)
- goto probe_err;
-
- dbg("%s: adding port\n", __func__);
- <span style="color: rgb(255, 0, 0);">uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);</span>
- 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;
- }
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;
}
函数总重要的语句全部标红了。先分析第一句
[cpp] view plain copy print ?
- <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等函数最终调用的是这里操作集,都是最底层的硬件操作。
[cpp] view plain copy print ?
- 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,
- <span style="color: rgb(255, 0, 0);">.ops = &s3c24xx_serial_ops,</span>
- .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
- };
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后,通过函数
[cpp] view plain copy print ?
- 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关联的操作。这一步是通过函数
[cpp] view plain copy print ?
- 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
最底层的硬件操作