[Turn] Serial driver analysis

//串口驱动程序分析
(1),串口写数据
当用户层调用write()函数,就对应调用/driver/char/tty_io.c里面的tty_write()
	tty_write() --->
		do_tty_write() --->
				//driver/char/n_tty.c
				n_tty_write() --->
						add_wait_queue(&tty->write_wait, &wait);//加入等待队列
						c = tty->ops->write(tty, b, nr);--->//
								//driver/serial/serial_core.c
								uart_write();--->	
									//数据先保存在串口端口的缓冲区,然后起动发送
									uart_start(tty); --->
										__uart_start(tty); --->
											port->ops->start_tx(port); --->
													//串口驱动层 driver/serial/5250.c
													serial8250_start_tx(); --->
														up->ier |= UART_IER_THRI;//使能串口发送中断 
														serial_out(up, UART_IER, up->ier);//字符发送在中断函数中进行
						schedule();	//进程调度
总结:可见,即使是发送数据,也没有使用循环查询的方法,它只是把数据保存起来,然后开启发送中断,当串口
      芯片内部的发送缓冲区可以再次存入数据时,这个中断被触发;在中断处理函数中将数据一点点地发送给串口芯片。
	  
(2),串口中断发送函数
	//driver/serial/5250.c
	serial8250_interrupt(); --->
		serial8250_handle_port(up); --->
			transmit_chars(up); --->
				serial_out();
				uart_write_wakeup(&up->port);--->//如果已经发送完毕,唤醒进程 
					tasklet_schedule(&state->tlet); --->
						uart_tasklet_action(); --->
							tty_wakeup();--->
								//唤醒等待发送完毕的进程,以上面的加入等待队列相对应
								wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
	
				__stop_tx(up);//如果已经发送完毕,则禁止发送中断
	
	
	
(3)、串口读数据分析
当用户层调用read()函数,就对应调用/driver/char/tty_io.c里面的tty_read()
	tty_read(); --->
		//读操作的具体细节都在线路规程中实现的默认的线路规程的读操作时read_chan函数
		if (ld->ops->read)
			i = (ld->ops->read)(tty, file, buf, count);--->
				n_tty_read(); --->
					add_wait_queue(&tty->read_wait, &wait);//加入读等待队列
				
				c = tty->read_buf[tty->read_tail];
				spin_lock_irqsave(&tty->read_lock, flags);	   <----------------<--------<-----------					
				tty->read_tail = ((tty->read_tail+1) &(N_TTY_BUF_SIZE-1));							|
				tty_put_user()																		|
				.......																				|
																									|
		n_tty_receive_buf(); --->                 <----------<---------------<-------------			|
			n_tty_receive_char(); --->													  |			|	
				put_tty_queue() --->												      |			|
					put_tty_queue_nolock(); ---> //把数据读上来							  |			|
						if (tty->read_cnt < N_TTY_BUF_SIZE) {							  |			|
							tty->read_buf[tty->read_head] = c;							  |			|
							tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);	  |			|
							tty->read_cnt++;	//数据可读					  ----->--------->-------
						}                        										  |
																						  |
																						  |
		//driver/serial/5250.c														 	  |
		serial8250_interrupt(); --->													  |
			serial8250_handle_port();--->												  |
				receive_chars(up, &status); --->										  |
					uart_insert_char();--->												  | 
						tty_insert_flip_char(); --->//插入数据						      |
							struct tty_buffer *tb = tty->buf.tail;						  |
							if (tb && tb->used < tb->size) {							  |	
								tb->flag_buf_ptr[tb->used] = flag;						  |
								tb->char_buf_ptr[tb->used++] = ch;						  |	
								return 1;							-------->-------------|					
							}															  |
						tty_flip_buffer_push(tty);--->									  |	
							flush_to_ldisc(&tty->buf.work.work);--->					  |
								__tty_buffer_flush();---> /*刷新数据*/					  |
									struct tty_buffer *thead;							  |
									while ((thead = tty->buf.head) != NULL) {			  |
										tty->buf.head = thead->next;					  |
										tty_buffer_free(tty, thead);  --------->-----------
									}
									tty->buf.tail = NULL;
								wake_up(&tty->read_wait);	//唤醒读进程

Transfer from: Serial driver analysis

Published 91 original articles · praised 17 · 50,000+ views

Guess you like

Origin blog.csdn.net/qq_23327993/article/details/104840122