ftdi_sio驱动学习笔记 5 - 读写操作

目录

1. ftdi_prepare_write_buffer

2. ftdi_process_read_urb


读写相关操作对应的函数:

.process_read_urb =	ftdi_process_read_urb,
.prepare_write_buffer =	ftdi_prepare_write_buffer,

实际读写并不是ftdi_sio操作的,是通过内核标准的usb serial实现。

1. ftdi_prepare_write_buffer

static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
						void *dest, size_t size)

参数dest是目标缓冲区的地址,用于存放准备好的数据。size是该数据的长度。这个函数用于准备一个USB数据包以便通过FTDI芯片发送到串口或其他异步通信端口。这个函数并不是直接发送数据,而是为数据发送做准备。

这个函数是把写入的数据更新到port->write_fifo,但是分2种情况,如果是非SIO的设备,比较简单,直接拷贝就可以了。

count = kfifo_out_locked(&port->write_fifo, dest, size, &port->lock);
port->icount.tx += count;

而对于SIO的设备,需要每个packet的第一个字节特殊处理,该字节的位0为1,位1为0,位2-7为该packet内数据的长度。

unsigned char *buffer = dest;
int i, len, c;

count = 0;
spin_lock_irqsave(&port->lock, flags);
for (i = 0; i < size - 1; i += priv->max_packet_size) {
	len = min_t(int, size - i, priv->max_packet_size) - 1;
	c = kfifo_out(&port->write_fifo, &buffer[i + 1], len);
	if (!c)
		break;
	port->icount.tx += c;
	buffer[i] = (c << 2) + 1;
	count += c + 1;
}
spin_unlock_irqrestore(&port->lock, flags);

真正写数据是标准的usb写,所以在ftdi_sio中没有处理。

2. ftdi_process_read_urb

static void ftdi_process_read_urb(struct urb *urb)

当 URB 完成从 FTDI 芯片读取数据后,ftdi_process_read_urb将被调用来解析和处理这些数据。它会检查读取的数据并将其适当地传递给更高层的串行通信协议栈。

数据在urb->transfer_buffer

for (i = 0; i < urb->actual_length; i += priv->max_packet_size) {
	len = min_t(int, urb->actual_length - i, priv->max_packet_size);
	count += ftdi_process_packet(port, priv, &data[i], len);
}

而tty_flip_buffer_push是通知tty去线路规程获取从串口过来的数据

if (count)
	tty_flip_buffer_push(&port->port);

对于ftdi_process_packet,这个函数用于处理FTDI芯片的USB串口数据包。该数据包分3个部分。

  • 第一个字节的处理:
#define FTDI_STATUS_B0_MASK	(FTDI_RS0_CTS | FTDI_RS0_DSR | FTDI_RS0_RI | FTDI_RS0_RLSD)

status = buf[0] & FTDI_STATUS_B0_MASK;
if (status != priv->prev_status) {
	char diff_status = status ^ priv->prev_status;

	if (diff_status & FTDI_RS0_CTS)
		port->icount.cts++;
	if (diff_status & FTDI_RS0_DSR)
		port->icount.dsr++;
	if (diff_status & FTDI_RS0_RI)
		port->icount.rng++;
	if (diff_status & FTDI_RS0_RLSD) {
		struct tty_struct *tty;

		port->icount.dcd++;
		tty = tty_port_tty_get(&port->port);
		if (tty)
			usb_serial_handle_dcd_change(port, tty,
						status & FTDI_RS0_RLSD);
		tty_kref_put(tty);
	}

	wake_up_interruptible(&port->port.delta_msr_wait);
	priv->prev_status = status;
}

如果状态发生变化,则根据变化的状态值更新相应的计数器(如CTS、DSR、RI和DCD)。如果RLSD状态发生变化,则进一步处理DCD状态的变化,包括更新计数器和调用处理函数。最后,唤醒等待状态变化的进程,并更新之前的状志值。

  • 第二个字节的处理:
/* save if the transmitter is empty or not */
if (buf[1] & FTDI_RS_TEMT)
	priv->transmit_empty = 1;
else
	priv->transmit_empty = 0;

if (len == 2)
	return 0;	/* status only */

检查FTDI设备的发送缓冲区是否为空,并设置相应的状态标志。

#define FTDI_RS_ERR_MASK (FTDI_RS_BI | FTDI_RS_PE | FTDI_RS_FE | FTDI_RS_OE)

if (buf[1] & FTDI_RS_ERR_MASK) {
	/*
	* Break takes precedence over parity, which takes precedence
	* over framing errors. Note that break is only associated
	* with the last character in the buffer and only when it's a
	* NUL.
	*/
	if (buf[1] & FTDI_RS_BI && buf[len - 1] == '\0') {
		port->icount.brk++;
		brkint = true;
	}
	if (buf[1] & FTDI_RS_PE) {
		flag = TTY_PARITY;
		port->icount.parity++;
	} else if (buf[1] & FTDI_RS_FE) {
		flag = TTY_FRAME;
		port->icount.frame++;
	}
	/* Overrun is special, not associated with a char */
	if (buf[1] & FTDI_RS_OE) {
		port->icount.overrun++;
		tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
	}
}

这里是处理串口通信数据错误的。它根据buf[1]中的位来判断出现了哪种错误:break(断路)、parity(奇偶校验错误)、framing(帧错误)或overrun(缓冲区溢出错误)。每种错误都有相应的处理方式,如计数器增加或插入特定字符到缓冲区。其中,break错误仅在最后一个字符为NUL时才被计数。

  • 其他数据
if (brkint || port->sysrq) {
	for (i = 2; i < len; i++) {
		if (brkint && i == len - 1) {
			if (usb_serial_handle_break(port))
				return len - 3;
			flag = TTY_BREAK;
		}
		if (usb_serial_handle_sysrq_char(port, buf[i]))
			continue;
		tty_insert_flip_char(&port->port, buf[i], flag);
	}
} else {
	tty_insert_flip_string_fixed_flag(&port->port, buf + 2, flag,
			len - 2);
}

根据条件处理USB串行端口的数据,并将其插入到tty flip缓冲区中。如果brkintport->sysrq为真,则对每个字符进行特殊处理;否则,直接将数据插入到缓冲区。在特殊处理过程中,如果遇到特定条件,会调用usb_serial_handle_break函数处理中断,或调用usb_serial_handle_sysrq_char函数处理系统请求字符。最终,数据通过tty_insert_flip_char函数插入到缓冲区中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值