转自:http://www.wowotech.net/linux_kenrel/183.html
目录:
1 首先分析设备驱动的注册
1.1 uart_register_driver分析
1.2 tty_register_driver分析
1.3 serial8250_register_ports()函数分析
1.4 serial8250_probe()函数分析
2 然后,我们来看设备的打开过程
3 TTY设备的读
3.1 read_chan()
4 TTY设备的写
5 总结
1 首先分析设备驱动的注册
对于8250.c来说,主要涉及:
-
serial8250_init()--->uart_register_driver(&serial8250_reg)
-
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev)
-
serial8250_probe(struct platform_device *dev)
struct uart_driver serial8250_reg的定义如下:
1 static static struct uart_driver serial8250_reg = { 2 .owner = THIS_MODULE, 3 .driver_name = "serial", 4 .dev_name = "ttyS", 5 .major = TTY_MAJOR, 6 .minor = 64, 7 .nr = UART_NR, 8 .cons = SERIAL8250_CONSOLE, 9 };
1.1 uart_register_driver分析
主要完成了一下功能:
-
分配数个uart_state结构体内存: (在uart_add_one_port()里会用到它来关联uart_port)
-
分配tty_driver。normal = alloc_tty_driver(drv->nr)
-
关联struct uart_driver和tty_driver:
uart_driver-> tty_driver= tty_driver; tty_driver ->driver_state = uart_driver; -
设置tty_driver的操作函数为uart_ops(tty_operations类型)中的操作函数:
-
调用tty_register_driver():根据tty_driver里的数据来注册字符设备( 来自于 uart_driver);并添加到tty_drivers链表;调用tty_register_device()产生设备文件。
1 int uart_register_driver(struct uart_driver *drv) 2 { 3 struct tty_driver *normal = NULL; 4 int i, retval; BUG_ON(drv->state); /* 5 * Maybe we should be using a slab cache for this, especially if 6 * we have a large number of ports to handle. 7 */ 8 drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); 9 retval = -ENOMEM; 10 if (!drv->state) 11 goto out; normal = alloc_tty_driver(drv->nr); 12 if (!normal) 13 goto out; drv->tty_driver = normal; normal->owner = drv->owner; 14 normal->driver_name = drv->driver_name; 15 normal->name = drv->dev_name; 16 normal->major = drv->major; 17 normal->minor_start = drv->minor; 18 normal->type = TTY_DRIVER_TYPE_SERIAL; 19 normal->subtype = SERIAL_TYPE_NORMAL; 20 normal->init_termios = tty_std_termios; 21 normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; 22 normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600; 23 normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 24 normal->driver_state = drv; 25 tty_set_operations(normal, &uart_ops); /* 26 * Initialise the UART state(s). 27 */ 28 for (i = 0; i < drv->nr; i++) { 29 struct uart_state *state = drv->state + i; state->close_delay = 500; /* .5 seconds */ 30 state->closing_wait = 30000; /* 30 seconds */ mutex_init(&state->mutex); 31 } retval = tty_register_driver(normal); 32 out: 33 if (retval < 0) { 34 put_tty_driver(normal); 35 kfree(drv->state); 36 } 37 return retval; 38 }
1.2 tty_register_driver分析
与传统的字符设备驱动程序完全一致,主要做了一下工作:
-
创建字符设备
-
注册字符设备
-
设置udev,创建/dev节点,名称为"%s%d", driver->name, index + driver->name_base,
normal->name = uart_driver->dev_name; //来自于uart_driver= "ttyS", //见struct uart_driver serial8250_reg的定义。
driver->name_base =0;
driver->num=(0--- driver->num); // driver->num = uart_driver->nr = UART_NR = 8
因此创建的节点名为:/dev/ttySx x=(0…7) -
Proc文件系统操作;
1 int tty_register_driver(struct tty_driver *driver) 2 { 3 int error; 4 int i; 5 dev_t dev; 6 void **p = NULL; if (driver->flags & TTY_DRIVER_INSTALLED) 7 return 0; if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { 8 p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL); 9 if (!p) 10 return -ENOMEM; 11 } if (!driver->major) { 12 error = alloc_chrdev_region(&dev, driver->minor_start, driver->num, 13 driver->name); 14 if (!error) { 15 driver->major = MAJOR(dev); 16 driver->minor_start = MINOR(dev); 17 } 18 } else { 19 dev = MKDEV(driver->major, driver->minor_start); 20 error = register_chrdev_region(dev, driver->num, driver->name); 21 } 22 if (error < 0) { 23 kfree(p); 24 return error; 25 } if (p) { 26 driver->ttys = (struct tty_struct **)p; 27 driver->termios = (struct ktermios **)(p + driver->num); 28 driver->termios_locked = (struct ktermios **)(p + driver->num * 2); 29 } else { 30 driver->ttys = NULL; 31 driver->termios = NULL; 32 driver->termios_locked = NULL; 33 } cdev_init(&driver->cdev, &tty_fops); 34 driver->cdev.owner = driver->owner; 35 error = cdev_add(&driver->cdev, dev, driver->num); if (error) { 36 unregister_chrdev_region(dev, driver->num); 37 driver->ttys = NULL; 38 driver->termios = driver->termios_locked = NULL; 39 kfree(p); 40 return error; 41 } if (!driver->put_char) 42 driver->put_char = tty_default_put_char; mutex_lock(&tty_mutex); 43 list_add(&driver->tty_drivers, &tty_drivers); 44 mutex_unlock(&tty_mutex); if ( !(driver->flags & TTY_DRIVER_DYNAMIC_DEV) ) { 45 for(i = 0; i < driver->num; i++) 46 tty_register_device(driver, i, NULL); 47 } 48 proc_tty_register_driver(driver); 49 return 0; 50 }
此时,内核已经注册了tty_drivers到全局链表tty_drivers。
1.3 serial8250_register_ports()函数分析
主要完成以下任务:
-
为端口号line赋值
-
初始化定时器
-
为uart_8250_port->uart_port.ops赋值= &serial8250_pops
-
为uart_8250_port[].uart_port->device赋值
-
将uart_8250_port[].uart_port挂入uart_driver->state[]->port
1 static void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev) 2 { 3 int i; serial8250_isa_init_ports(); for (i = 0; i < nr_uarts; i++) { 4 struct uart_8250_port *up = &serial8250_ports[i]; up->port.dev = dev; 5 uart_add_one_port(drv, &up->port); 6 } 7 }
1.4 serial8250_probe()函数分析
通过struct plat_serial8250_port *p = dev->dev.platform_data获取platform_device的设备私有数据(里面一般包括mapbase、irq、iotype等),将这些数据赋给uart_port,然后调用:
serial8250_register_port()--->uart_add_one_port(&serial8250_reg, &uart->port)
将uart_port注册到uart_driver->state[]->port里面。
1 static int __devinit serial8250_probe(struct platform_device *dev) 2 { 3 struct plat_serial8250_port *p = dev->dev.platform_data; 4 struct uart_port port; 5 int ret, i; 6 7 memset(&port, 0, sizeof(struct uart_port)); 8 9 for (i = 0; p && p->flags != 0; p++, i++) { 10 port.iobase = p->iobase; 11 port.membase = p->membase; 12 port.irq = p->irq; 13 port.uartclk = p->uartclk; 14 port.regshift = p->regshift; 15 port.iotype = p->iotype; 16 port.flags = p->flags; 17 port.mapbase = p->mapbase; 18 port.hub6 = p->hub6; 19 port.dev = &dev->dev; 20 if (share_irqs) 21 port.flags |= UPF_SHARE_IRQ; 22 ret = serial8250_register_port(&port); 23 if (ret < 0) { 24 dev_err(&dev->dev, "unable to register port at index %d " 25 "(IO%lx MEM%llx IRQ%d): %d\n", i, 26 p->iobase, (unsigned long long)p->mapbase, 27 p->irq, ret); 28 } 29 } 30 return 0; 31 }
2 然后,我们来看设备的打开过程
以/dev/ttyS0为例。
根据系统在前面在此字符设备注册的fops,在open()后,系统应该是进入tty_fops的tty_open()函数。
可以明确:
tty_struct结构是在tty_open()时构建;
tty_struct保存在file->private_data;
以后的操作通过filp就可以找到tty_struct
然后通过tty_struct->tty_driver->open(tty_struct*, filp)调用的是tty_operations uart_ops.open =uart_open(serile_core.c);通过 uart_register_driver()->tty_set_operations(normal, &uart_ops)注册。
tty_operations里的函数都是以(tty_struct, file* filp) 为参数。
而在uart_open(tty_struct*, filp)里,进行一些初始化后,调用了uart_startup(state, 0),此函数主要做了两件事:
1)分配并初始化transmit 和 temporary缓冲区circ_buf
2)调用port->ops->startup(port
port=state.port |state = uart_driver->state[] |uart_driver=tty_struct->tty_driver->driver_state
1 static int tty_open(int input, int output, int primary, void *d, 2 char **dev_out) 3 { 4 struct tty_chan *data = d; 5 int fd, err; fd = os_open_file(data->dev, of_set_rw(OPENFLAGS(), input, output), 0); 6 if(fd < 0) 7 return fd; if(data->raw){ 8 CATCH_EINTR(err = tcgetattr(fd, &data->tt)); 9 if(err) 10 return err; err = raw(fd); 11 if(err) 12 return err; 13 } *dev_out = data->dev; 14 return fd; 15 }
总结:
tty_open()后,创建了tty_struct,并保存在filp中;再调用uart层的tty_operations->uart_ops.open(),在里面创建了发送的circ_buf;然后调用了uart_port->uart_ops->open(tty, filp)。
tty_struct对应一个已经打开的具体tty设备。
3 TTY设备的读
TTY设备的读分为两部分:首先是进程读取tty_struct对应的缓冲区并阻塞当前进程;然后设备中断里,接收数据,唤醒进程的读操作。
程序首先进入tty_read():
-
首先通过file->private_data获取tty_struct,然后再获取tty_ldisc;
-
最后调用 tty_ldisc->read。对于N_TTY即tty_ldisc_N_TTY.read()=read_chan()
1 static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 2 loff_t *ppos) 3 { 4 int i; 5 struct tty_struct * tty; 6 struct inode *inode; 7 struct tty_ldisc *ld; 8 tty = (struct tty_struct *)file->private_data; 9 inode = file->f_path.dentry->d_inode; 10 if (tty_paranoia_check(tty, inode, "tty_read")) 11 return -EIO; 12 if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) 13 return -EIO; 14 /* We want to wait for the line discipline to sort out in this 15 situation */ 16 ld = tty_ldisc_ref_wait(tty); 17 lock_kernel(); 18 if (ld->read) 19 i = (ld->read)(tty,file,buf,count); 20 else 21 i = -EIO; 22 tty_ldisc_deref(ld); 23 unlock_kernel(); 24 if (i > 0) 25 inode->i_atime = current_fs_time(inode->i_sb); 26 return i;
3.1 read_chan()
-
初始化延迟工作队列:init_dev()==>initialize_tty_struct()==>INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc)
-
tty->read_wait只被n_tty_receive_buf()函数(或里面的分支)调用;
-
n_tty_receive_buf()只被flush_to_ldisc()调用
-
而tty_flip_buffer_push()有两种方式来调用flush_to_ldisc():
1)tty->low_latency===> flush_to_ldisc(&tty->buf.work.work);
2)schedule_delayed_work(&tty->buf.work, 1);
两者都是调用flush_to_ldisc(),不同点在于后者是延迟执行flush_to_ldisc()。延迟工作队列是在initialize_tty_struct()===>INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);中进行初始化的。
对于驱动层,调用轨迹如下:
在open()操作里申请中断;在中断里唤醒进程。
tty_open()==>………==>serial8250_startup()==>serial_link_irq_chain()==>request_irq()--------申请中断
serial8250_interrupt()--------------------------------------------------------------------------------------------处理中断
->serial8250_handle_port()
->receive_chars()
-> uart_insert_char() //接收字符,存入tty_buffer,tty_struct包含tty_bufhead
->tty_insert_flip_char() //而tty_bufhead包含三个tty_buffer成员:head、tail、free
->tty_flip_buffer_push()-> flush_to_ldisc()
-> n_tty_receive_buf()
-> memcpy(tty->read_buf + tty->read_head, cp, i); //拷贝数据至tty->read_buf
->tty->read_cnt += i //指示接收buff的字符数。
//与read_chan()-->input_available_p()
里对tty->read_cnt的判断对应->wake_up(&tty->read_wait) //唤醒进程
大致是下图的流程:
调用tty_insert_flip_char或者tty_insert_flip_string将数据放入tty的缓存tty->tty_buffer;然后调用tty_flip_buffer_push(),将数据从tty缓存拷贝至ldisc缓存。
- tty_insert_flip_string:将hardware driver中的数据缓冲到tty_buffer中,而这个tty_buffer指针则是在tty_port->buf->tail.
- tty_flip_buffer_push:将tty_buffer也即tty驱动层缓冲区数据推到tty线路规程层缓冲区,否则tty核心层无法读取到数据,这样用户也就无法从tty核心层取到数据,可以理解为userspace->tty核心->line discipline->tty驱动.
- 源码中tty_flip_buffer_push会启动flush_to_ldisc的work, 在work进程中会把tty_buffer中的数据推到ldisc的缓冲区。
- userspace->read->tty_read->n_tty_read(tty_ldisc_ops)读取ldisc缓冲区数据
1 static ssize_t read_chan(struct tty_struct *tty, struct file *file, 2 unsigned char __user *buf, size_t nr) 3 { 4 unsigned char __user *b = buf; 5 DECLARE_WAITQUEUE(wait, current); 6 int c; 7 int minimum, time; 8 ssize_t retval = 0; 9 ssize_t size; 10 long timeout; 11 unsigned long flags; 12 13 do_it_again: 14 15 if (!tty->read_buf) { 16 printk("n_tty_read_chan: called with read_buf == NULL?!?\n"); 17 return -EIO; 18 } 19 20 c = job_control(tty, file); 21 if(c < 0) 22 return c; 23 24 minimum = time = 0; 25 timeout = MAX_SCHEDULE_TIMEOUT; 26 if (!tty->icanon) { 27 time = (HZ / 10) * TIME_CHAR(tty); 28 minimum = MIN_CHAR(tty); 29 if (minimum) { 30 if (time) 31 tty->minimum_to_wake = 1; 32 else if (!waitqueue_active(&tty->read_wait) || 33 (tty->minimum_to_wake > minimum)) 34 tty->minimum_to_wake = minimum; 35 } else { 36 timeout = 0; 37 if (time) { 38 timeout = time; 39 time = 0; 40 } 41 tty->minimum_to_wake = minimum = 1; 42 } 43 } 44 45 /* 46 * Internal serialization of reads. 47 */ 48 if (file->f_flags & O_NONBLOCK) { 49 if (!mutex_trylock(&tty->atomic_read_lock)) 50 return -EAGAIN; 51 } 52 else { 53 if (mutex_lock_interruptible(&tty->atomic_read_lock)) 54 return -ERESTARTSYS; 55 } 56 57 add_wait_queue(&tty->read_wait, &wait); 58 while (nr) { 59 /* First test for status change. */ 60 if (tty->packet && tty->link->ctrl_status) { 61 unsigned char cs; 62 if (b != buf) 63 break; 64 cs = tty->link->ctrl_status; 65 tty->link->ctrl_status = 0; 66 if (tty_put_user(tty, cs, b++)) { 67 retval = -EFAULT; 68 b--; 69 break; 70 } 71 nr--; 72 break; 73 } 74 /* This statement must be first before checking for input 75 so that any interrupt will set the state back to 76 TASK_RUNNING. */ 77 set_current_state(TASK_INTERRUPTIBLE); 78 79 if (((minimum - (b - buf)) < tty->minimum_to_wake) && 80 ((minimum - (b - buf)) >= 1)) 81 tty->minimum_to_wake = (minimum - (b - buf)); 82 83 if (!input_available_p(tty, 0)) { 84 if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { 85 retval = -EIO; 86 break; 87 } 88 if (tty_hung_up_p(file)) 89 break; 90 if (!timeout) 91 break; 92 if (file->f_flags & O_NONBLOCK) { 93 retval = -EAGAIN; 94 break; 95 } 96 if (signal_pending(current)) { 97 retval = -ERESTARTSYS; 98 break; 99 } 100 n_tty_set_room(tty); 101 timeout = schedule_timeout(timeout); 102 continue; 103 } 104 __set_current_state(TASK_RUNNING); 105 106 /* Deal with packet mode. */ 107 if (tty->packet && b == buf) { 108 if (tty_put_user(tty, TIOCPKT_DATA, b++)) { 109 retval = -EFAULT; 110 b--; 111 break; 112 } 113 nr--; 114 } 115 116 if (tty->icanon) { 117 /* N.B. avoid overrun if nr == 0 */ 118 while (nr && tty->read_cnt) { 119 int eol; 120 121 eol = test_and_clear_bit(tty->read_tail, 122 tty->read_flags); 123 c = tty->read_buf[tty->read_tail]; 124 spin_lock_irqsave(&tty->read_lock, flags); 125 tty->read_tail = ((tty->read_tail+1) & 126 (N_TTY_BUF_SIZE-1)); 127 tty->read_cnt--; 128 if (eol) { 129 /* this test should be redundant: 130 * we shouldn't be reading data if 131 * canon_data is 0 132 */ 133 if (--tty->canon_data < 0) 134 tty->canon_data = 0; 135 } 136 spin_unlock_irqrestore(&tty->read_lock, flags); 137 138 if (!eol || (c != __DISABLED_CHAR)) { 139 if (tty_put_user(tty, c, b++)) { 140 retval = -EFAULT; 141 b--; 142 break; 143 } 144 nr--; 145 } 146 if (eol) { 147 tty_audit_push(tty); 148 break; 149 } 150 } 151 if (retval) 152 break; 153 } else { 154 int uncopied; 155 uncopied = copy_from_read_buf(tty, &b, &nr); 156 uncopied += copy_from_read_buf(tty, &b, &nr); 157 if (uncopied) { 158 retval = -EFAULT; 159 break; 160 } 161 } 162 163 /* If there is enough space in the read buffer now, let the 164 * low-level driver know. We use n_tty_chars_in_buffer() to 165 * check the buffer, as it now knows about canonical mode. 166 * Otherwise, if the driver is throttled and the line is 167 * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode, 168 * we won't get any more characters. 169 */ 170 if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) { 171 n_tty_set_room(tty); 172 check_unthrottle(tty); 173 } 174 175 if (b - buf >= minimum) 176 break; 177 if (time) 178 timeout = time; 179 } 180 mutex_unlock(&tty->atomic_read_lock); 181 remove_wait_queue(&tty->read_wait, &wait); 182 183 if (!waitqueue_active(&tty->read_wait)) 184 tty->minimum_to_wake = minimum; 185 186 __set_current_state(TASK_RUNNING); 187 size = b - buf; 188 if (size) { 189 retval = size; 190 if (nr) 191 clear_bit(TTY_PUSH, &tty->flags); 192 } else if (test_and_clear_bit(TTY_PUSH, &tty->flags)) 193 goto do_it_again; 194 195 n_tty_set_room(tty); 196 197 return retval; 198 }
4 TTY设备的写
首先调用tty_write.
1 static ssize_t tty_write(struct file * file, const char __user * buf, size_t count, 2 loff_t *ppos) 3 { 4 struct tty_struct * tty; 5 struct inode *inode = file->f_path.dentry->d_inode; 6 ssize_t ret; 7 struct tty_ldisc *ld; 8 9 tty = (struct tty_struct *)file->private_data; 10 if (tty_paranoia_check(tty, inode, "tty_write")) 11 return -EIO; 12 if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags))) 13 return -EIO; 14 15 ld = tty_ldisc_ref_wait(tty); 16 if (!ld->write) 17 ret = -EIO; 18 else 19 ret = do_tty_write(ld->write, tty, file, buf, count); 20 tty_ldisc_deref(ld); 21 return ret; 22 }
TTY设备写涉及写进程、中断ISR、即tasklet_action三部分的配合:
应用APP函数调用:
tty_write()---------------------------------------------------------------------------------------------------tty_io.c
do_tty_write()
copy_from_user() //将用户空间数据copy至tty->write_buf
write () //对于N_TTY,即tty_ldisc_N_TTY.write()=write_chan()
add_wait_queue()-------------------------------------------------------------------------n_tty.c
//添加等待队列
tty->driver->flush_chars(tty); //struct tty_operations uart_ops. flush_chars=uart_flush_chars()
uart_start()---------------------------------------------------------------------------serial_core.c
__uart_start()
uart_port->ops->start_tx() //uart_ops serial8250_pops.start_tx = serial8250_start_tx
transmit_chars() //启动真正发送------------------------------8250.c
serial_out(up, UART_TX, xmit->buf[xmit->tail]) //copy至芯片发送buffer
uart_write_wakeup(struct uart_port *port) //调度tasklet_schedule()
tasklet_schedule(&info->tlet);
schedule(); //进程睡眠
中断函数调用:
serial8250_interrupt()-----------------------------------------------------------------------------------处理中断
serial8250_handle_port()------------------------------------------------------------------------8250.c
if(Transmit-hold-register empty)
transmit_chars()
serial_out(up, UART_TX, xmit->buf[xmit->tail]) //copy至芯片发送缓冲
uart_write_wakeup()---------------------------------------------------serial_core.c
tasklet_schedule() //调度tasklet_schedule()
tasklet在tty_open()-->……-->uart_open()-->uart_get()
-->tasklet_init(&state->info->tlet, uart_tasklet_action,state)中进行初始化。
【tty_open()-->tty_struct.tty_driver.open()=uart_open()-->uart_get()-->tasklet_init()】tasklet_action()调用:
经过tasklet_schedule ()后执行uart_tasklet_action。
uart_tasklet_action ()
tty_wakeup()
ld->write_wakeup(tty)//即n_tty_write_wakeup():发送信号SIGIO给fasync_struct所描述的PID
wake_up_interruptible(&tty->write_wait); //唤醒写进程
5 总结
在理清了数据走向和函数调用关系后,我们可以清晰的知道开发TTY驱动,需要我们做什么:
-
定义uart_driver数据结构;
-
定义uart_port数据结构;
-
完成uart_ops操作函数集合。
最后放一张从进程、vfs、tty_core、serial_core到uart驱动各个数据结构之间的相互关系图:
博客推荐:
tty驱动分析:http://blog.csdn.net/lizuobin2/article/details/51773305 ,对数据流和读写说的很好
问题:
1. 怎样理解tty核心层、tty驱动层、线路规程层、串口核心层、串口驱动层?
核心层其实就是operation的实现方法部分,所以
- tty核心层为tty_read/tty_write等,包括n_tty.c(line discipline)和tty_io.c中
- serial核心层为uart_read/uart_write等,包括serial_core.c和imx.c(freescale chip).
2. TTY这层存在的作用是什么?
TTY用来抽象串行接口的设备,抽象了串行接口设备所需要的特性、功能,抽象后,一个tty设备即可表示一个串行输入、输出接口(比如控制台、keypad、串口、pty设备)等