ftdi_sio驱动学习笔记 4 - 串口操作

目录

1. open

2. set_termios

2.1 设置波特率

2.2 设置数据位数

2.3 设置停止位

2.4 设置校验方式

2.5 设置流控

3. ioctl

4. get_serial

5. set_serial

6. break_ctl

7. tx_empty

8. tiocmget

9. tiocmset


并不是所有的串口操作都有实现。具体如下:

1. open

当一个串行端口被打开时调用。在标准打开的函数usb_serial_generic_open前复位芯片

usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
		FTDI_SIO_RESET_SIO,
		priv->channel, NULL, 0, WDR_TIMEOUT);

然后通过调用ftdi_set_termios,更新与tty关联的串行通信设置,例如波特率、数据位、停止位和奇偶校验,以适应FTDI设备的要求。 

if (tty)
	ftdi_set_termios(tty, port, NULL);

2. set_termios

设置新的终端属性设置,如波特率、数据位、奇偶校验等。

2.1 设置波特率

if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) {
	dev_dbg(ddev, "%s: forcing baud rate for this device\n", __func__);
	tty_encode_baud_rate(tty, priv->force_baud,
					priv->force_baud);
}

force_baud是表示设备会被强制设置的波特率,而不是根据参数termiso->c_cflag里面的值设置。tty_encode_baud_rate后面2个参数分别表示读写的波特率,这个函数只是修改tty->c_cflag,没有实际操作芯片。

mutex_lock(&priv->cfg_lock);
if (change_speed(tty, port))
	dev_err(ddev, "%s urb failed to set baudrate\n", __func__);
mutex_unlock(&priv->cfg_lock);

change_speed是设置波特率的函数。 

2.2 设置数据位数

数据位,停止位和校验方式是通过一个命令写入的。

if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		FTDI_SIO_SET_DATA_REQUEST,
		FTDI_SIO_SET_DATA_REQUEST_TYPE,
		value, priv->channel,
		NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n",
	__func__);
}

 数据位有5/7/8三种方式,8位为默认方式。

switch (cflag & CSIZE) {
case CS5:
	dev_dbg(ddev, "Setting CS5 quirk\n");
	break;
case CS7:
	value |= 7;
	dev_dbg(ddev, "Setting CS7\n");
	break;
default:
case CS8:
	value |= 8;
	dev_dbg(ddev, "Setting CS8\n");
	break;
}

2.3 设置停止位

停止位有1或2两种设定

value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 :
	FTDI_SIO_SET_DATA_STOP_BITS_1);

2.4 设置校验方式

支持5种校验方式。

if (cflag & PARENB) {
	if (cflag & CMSPAR)
		value |= cflag & PARODD ?
				FTDI_SIO_SET_DATA_PARITY_MARK :
				FTDI_SIO_SET_DATA_PARITY_SPACE;
	else
		value |= cflag & PARODD ?
				FTDI_SIO_SET_DATA_PARITY_ODD :
				FTDI_SIO_SET_DATA_PARITY_EVEN;
} else {
	value |= FTDI_SIO_SET_DATA_PARITY_NONE;
}

2.5 设置流控

和波特率一样,流控也是有强制设置的

if (priv->force_rtscts) {
	dev_dbg(ddev, "%s: forcing rtscts for this device\n", __func__);
	termios->c_cflag |= CRTSCTS;
}

如果波特率设置为0,也需要关闭流控。

if ((cflag & CBAUD) == B0) {
	/* Disable flow control */
	if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
			FTDI_SIO_SET_FLOW_CTRL_REQUEST,
			FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
			0, priv->channel,
			NULL, 0, WDR_TIMEOUT) < 0) {
	    dev_err(ddev, "%s error from disable flowcontrol urb\n",
			__func__);
	}
	/* Drop RTS and DTR */
	clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
}

 最后是根据流控方式设置RTS/CTS或XON/XOFF的流控

value = 0;

if (C_CRTSCTS(tty)) {
	dev_dbg(&port->dev, "enabling rts/cts flow control\n");
	index = FTDI_SIO_RTS_CTS_HS;
} else if (I_IXON(tty)) {
	dev_dbg(&port->dev, "enabling xon/xoff flow control\n");
	index = FTDI_SIO_XON_XOFF_HS;
	value = STOP_CHAR(tty) << 8 | START_CHAR(tty);
} else {
	dev_dbg(&port->dev, "disabling flow control\n");
	index = FTDI_SIO_DISABLE_FLOW_CTRL;
}

index |= priv->channel;

ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
		FTDI_SIO_SET_FLOW_CTRL_REQUEST,
		FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
		value, index, NULL, 0, WDR_TIMEOUT);

3. ioctl

处理来自应用程序的输入输出控制命令,这里只处理TIOCSERGETLSR命令,调用`get_lsr_info`函数来获取线路状态寄存器(LSR)的信息。

case TIOCSERGETLSR:
	return get_lsr_info(port, argp);

4. get_serial

提供串行端口的状态信息给用户空间程序。

ss->flags = priv->flags;
ss->baud_base = priv->baud_base;
ss->custom_divisor = priv->custom_divisor;
  • 将`priv->flags`复制到`ss->flags`:串行端口的标志或状态信息,例如数据位数等信息。
  • 将`priv->baud_base`复制到`ss->baud_base`:串行端口的基本波特率,用于计算实际的波特率设置。
  • 将`priv->custom_divisor`复制到`ss->custom_divisor`:这是一个自定义的除数,用于调整波特率。

5. set_serial

允许用户空间程序更改串行端口的状态信息。

首先要判断权限,通过capable(CAP_SYS_ADMIN)函数检查当前进程是否具有管理员权限。如果当前进程没有管理员权限,则进一步检查ss->flagspriv->flags的差异是否涉及ASYNC_USR_MASK之外的位。如果存在差异且涉及其他位,则释放priv->cfg_lock互斥锁并返回-EPERM错误码,表示权限不足。

if (!capable(CAP_SYS_ADMIN)) {
	if ((ss->flags ^ priv->flags) & ~ASYNC_USR_MASK) {
		mutex_unlock(&priv->cfg_lock);
		return -EPERM;
	}
}

更新latency时间和波特率

old_flags = priv->flags;
old_divisor = priv->custom_divisor;

priv->flags = ss->flags & ASYNC_FLAGS;
priv->custom_divisor = ss->custom_divisor;

write_latency_timer(port);

if ((priv->flags ^ old_flags) & ASYNC_SPD_MASK ||
	((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
	priv->custom_divisor != old_divisor)) {

	/* warn about deprecation unless clearing */
	if (priv->flags & ASYNC_SPD_MASK)
		dev_warn_ratelimited(&port->dev, "use of SPD flags is deprecated\n");

	change_speed(tty, port);
}

6. break_ctl

控制断电信号(Break Condition),即在串行线路上发送一个持续的低电平信号。

if (break_state)
	value = priv->last_set_data_value | FTDI_SIO_SET_BREAK;
else
	value = priv->last_set_data_value;

ret = usb_control_msg(port->serial->dev,
		usb_sndctrlpipe(port->serial->dev, 0),
		FTDI_SIO_SET_DATA_REQUEST,
		FTDI_SIO_SET_DATA_REQUEST_TYPE,
		value, priv->channel,
		NULL, 0, WDR_TIMEOUT);

7. tx_empty

检查发送缓冲区是否为空。

从FTDI芯片读取2个字节的modem status,其中第二个字节的位6表示该状态。

 * Byte 1: Line Status
 *
 * Offset	Description
 * B0	Data Ready (DR)
 * B1	Overrun Error (OE)
 * B2	Parity Error (PE)
 * B3	Framing Error (FE)
 * B4	Break Interrupt (BI)
 * B5	Transmitter Holding Register (THRE)
 * B6	Transmitter Empty (TEMT)
 * B7	Error in RCVR FIFO

ret = ftdi_get_modem_status(port, buf);
if (ret == 2) {
	if (!(buf[1] & FTDI_RS_TEMT))
		return false;
}

8. tiocmget

获取串行端口的调制解调器状态。

也是通过ftdi_get_modem_status获取FTDI芯片的2个字节modom status。主要是返回第一个字节和dtr/rts的状态。

 * Byte 0: Modem Status
 *
 * Offset	Description
 * B0	Reserved - must be 1
 * B1	Reserved - must be 0
 * B2	Reserved - must be 0
 * B3	Reserved - must be 0
 * B4	Clear to Send (CTS)
 * B5	Data Set Ready (DSR)
 * B6	Ring Indicator (RI)
 * B7	Receive Line Signal Detect (RLSD)

#define TIOCM_DSR	0x100
#define TIOCM_CTS	0x020
#define TIOCM_RNG	0x080
#define TIOCM_RI	TIOCM_RNG
#define TIOCM_CAR	0x040
#define TIOCM_CD	TIOCM_CAR

ret =	(buf[0] & FTDI_SIO_DSR_MASK  ? TIOCM_DSR : 0) |
	(buf[0] & FTDI_SIO_CTS_MASK  ? TIOCM_CTS : 0) |
	(buf[0] & FTDI_SIO_RI_MASK   ? TIOCM_RI  : 0) |
	(buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD  : 0) |
	priv->last_dtr_rts;

9. tiocmset

设置串行端口的调制解调器状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值