Linux串口驱动程序(6)-串口驱动实现

在Linux驱动开发中,一般都不会从0开始写,我们做的更多的是移植和修改,有时候还需要对驱动程序进行优化。当然具备移植、修改驱动能力的基础是能够读懂驱动程序,同时需要对这个驱动程序的核心功能非常了解。接下来开始编写发送中断处理程序和接收中断处理程序的代码。

1.发送中断处理程序的设计

  • 下面我们就开始对s3c24xx_serial_tx_chars进行重新的编写,编写的依据就是我们之前分析出来的发送流程。最终的目的是,编写的代码,在编译进内核之后可以正确的通过串口输出和输入信息。
// 发送中断处理程序
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
	struct s3c24xx_uart_port *ourport = id;
	struct uart_port *port = &ourport->port;
	struct circ_buf *xmit = &port->state->xmit;  // 循环缓冲
	int count = 256;

	// 1、判断x_char里面是否有数据,如果有数据把它发送然后退出。
	if (port->x_char) 
	{
		wr_regb(port, S3C2410_UTXH, port->x_char);
		goto out;
	}
	
	// 2、判断循环缓冲是否为空,或者串口不允许发送,则把中断关闭
	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) 
	{
		s3c24xx_serial_stop_tx(port);
		goto out;
	}
	
	// 3、利用while循环发送数据
	while (!uart_circ_empty(xmit) && count-- > 0)   // 循环的条件是:1.循环缓冲不为空,2.发送的数据量最多为256字节
	{
		// 3.1 判断发送的fifo是否满了(UFSTAT寄存器14位),如果满了要退出
		if (rd_regl(port, S3C2410_UFSTAT) & (1 << 14))
			break;
	
		// 3.2 没有满就从循环缓冲的尾部去除数据,写入S3C2410_UTXH。从循环缓冲中取出数据,用tail
		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
		
		// 3.3 调整循环缓冲的位置
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
		port->icount.tx++;   // 发送的数据量加1
	}
	
    // 4、如果循环缓冲里面的数据小于WAKEUP_CHARS(256),则唤醒之前阻塞的发送进程
	if (uart_circ_chars_pending(xmit) < 256)
		uart_write_wakeup(port);
		
	// 5、同时如果循环缓冲为空了,把发送中断关闭。
	if (uart_circ_empty(xmit))
		s3c24xx_serial_stop_tx(port);
	
 out:
	return IRQ_HANDLED;   // 驱动被执行
}	
  • 当然在编写过程中,应该尽量不参考原来代码的实现,而是参考之前分析的流程,碰到不会的函数、结构、可以参考内核里面的其他代码。

2.接收中断处理程序的设计

  • 接收中断处理函数是s3c24xx_serial_rx_chars,编写过程方法和上面的也类似。
// 接收中断处理程序
s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
	struct s3c24xx_uart_port *ourport = dev_id;
	struct uart_port *port = &ourport->port;
	struct tty_struct *tty = port->state->port.tty;
	unsigned int ufcon, ch, flag, ufstat, uerstat;
	int max_count = 64;

	while (max_count-- > 0)
	{	
		// 1.判断接收fifo是否为空,如果为空,退出
		ufstat = rd_regl(port, S3C2410_UFSTAT);  // 读取UFSTAT寄存器状态,
		if ((ufstat & 0x3f) == 0)  // UFSTAT寄存器0到5位保存接收fifo的数据量
			break;
	
		// 2.读取错误状态寄存器
		uerstat = rd_regl(port, S3C2410_UERSTAT);
		
	    // 3.取出接收到的字符 
	    ch = rd_regb(port, S3C2410_URXH);
		
		// 6.如果接收到的是sysrq这个特殊字符,则进行特殊处理
		uart_handle_sysrq_char(port, ch);
		
		flag = TTY_NORMAL;
		
		// 7.把接收到的字符送到串口驱动的buf中
	    uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
	}
	
	// 8.把串口驱动中的数据送到read_buf中
	tty_flip_buffer_push(tty);
	
	return IRQ_HANDLED;
}


 

你可能感兴趣的:(Linux底层驱动)