用户在使用串口的时候,需要在用户空间设置串口属性,一种是直接通过驱动的ioctl去操作,但常用的方法是使用glibc的库函数来操作,比如常用的tcsetattr()和tcgetattr()函数。以tcsetattr()为例,该函数定义在glibc的tcsetattr.c中。tcsetattr()的第一个参数为打开的串口设备描述符,第三个参数为要设置的串口新属性,第二个参数为设置操作的模式,TCSANOW表示不等数据传输完成立即改变属性,TCSADRAIN表示等待所有数据传输完成后才改变属性,TCSAFLUSH表示等所有数据传输完成并清空输入输出缓冲区才改变属性。根据不同的模式选择不同的命令,最后调用INLINE_SYSCALL()执行ioctl的系统调用。
int tcsetattr (fd, optional_actions, termios_p)
int fd;
int optional_actions;
const struct termios *termios_p;
{
struct __kernel_termios k_termios;
unsigned long int cmd;
switch (optional_actions)
{
case TCSANOW:
cmd = TCSETS;
break;
case TCSADRAIN:
cmd = TCSETSW;
break;
case TCSAFLUSH:
cmd = TCSETSF;
break;
default:
__set_errno (EINVAL);
return -1;
}
k_termios.c_iflag = termios_p->c_iflag & ~IBAUD0;
k_termios.c_oflag = termios_p->c_oflag;
k_termios.c_cflag = termios_p->c_cflag;
k_termios.c_lflag = termios_p->c_lflag;
k_termios.c_line = termios_p->c_line;
#if defined _HAVE_C_ISPEED && defined _HAVE_STRUCT_TERMIOS_C_ISPEED
k_termios.c_ispeed = termios_p->c_ispeed;
#endif
#if defined _HAVE_C_OSPEED && defined _HAVE_STRUCT_TERMIOS_C_OSPEED
k_termios.c_ospeed = termios_p->c_ospeed;
#endif
memcpy (&k_termios.c_cc[0], &termios_p->c_cc[0],
__KERNEL_NCCS * sizeof (cc_t));
return INLINE_SYSCALL (ioctl, 3, fd, cmd, &k_termios);
}
ioctl系统调用执行到tty核心层,首先调用的是tty_ioctl()函数。在该函数中并没有直接处理TCSETS/TCSETSW/TCSETSF三个命令,所以会再调用tty->ops->ioctl,即调用tty驱动的iotcl函数来处理。tty驱动的ioctl函数为uart_ioctl(),在该函数中也没有对上述三个命令进行处理,所以接着调用uart驱动uport->ops->ioctl()函数来处理。而8250/16550的驱动并没有定义ioctl操作函数,所以回到tty_ioctl()中继续调用线路规程的ld->ops->ioctl()函数来处理,该函数在n_tty.c中被定义为n_tty_ioctl()。该函数本身只处理TIOCOUTQ/TI