主要源文件关系及数据流向
特定tty设备驱动的主体工作是填充tty_diver结构体的成员。
linux提供了一组函数用于操作tty_driver结构体及tty设备:
1)struct tty_diver *alloc_tty_driver(int lines);返回tty_driver指针,分配tty驱动
2)int tty_register_driver(struct tty_driver *driver);注册tty驱动
3)int tty_unregister_driver(struct tty_driver *driver);注销tty驱动
4)int tty_register_device(struct tty_driver *driver,unsigned index,struct device *device);注册tty设备
5)int tty_unregister_device(struct tty_driver *driver,unsigned index);注销tty设备
6)void tty_set_operation(struct tty_driver *driver,struct tty_operations *op);设置tty驱动操作
该函数会将tty_operation结构体中的函数指针复制到tty_driver 对应的函数指针。
3.设备的初始化与释放
加载函数
static int __init xxx_init(void){
xxx_tty_driver=alloc_tty_driver(xxx_ports);分配tty_driver 结构体
初始化tty_driver 结构体
。。。
注册tty设备
ret=tty_register_driver(xxx_tty_driver);
if(ret)
{
printk(...);
}
...
ret=reqeust_irq(...);硬件资源申请
...
}
打开和关闭函数
tty_driver中的open()函数第一个参数为tty_struct 结构体指针,第二个为文件指针。
tty_struct 结构体用来保存当前tty 端口的状态
数据发送和接收
用户所有的数据发动到终端时,通过write()系统调用——tty核心——线路规程的层层调用,最终调用tty_driver结构体中的write()函数来完成发送。 tty_driver结构体中的write()原型为:
static int xxx_write(struct tty_struct *tty,const unsigned char *buf,int count);//发送对象,指针和要发送的字节数
tty路线设置
tty驱动的ioctl函数
int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct tty_struct *tty, *real_tty;
int retval;
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
return -EINVAL;
real_tty = tty;
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
/*
* Break handling by driver
*/
if (!tty->driver.break_ctl) {
switch(cmd) {
case TIOCSBRK:
case TIOCCBRK:
if (tty->driver.ioctl)
return tty->driver.ioctl(tty, file, cmd, arg);
return -EINVAL;
/* These two ioctl's always return success; even if */
/* the driver doesn't support them. */
case TCSBRK:
case TCSBRKP:
if (!tty->driver.ioctl)
return 0;
retval = tty->driver.ioctl(tty, file, cmd, arg);
if (retval == -ENOIOCTLCMD)
retval = 0;
return retval;
}
}
/*
* Factor out some common prep work
*/
switch (cmd) {
case TIOCSETD:
case TIOCSBRK:
case TIOCCBRK:
case TCSBRK:
case TCSBRKP:
retval = tty_check_change(tty);
if (retval)
return retval;
if (cmd != TIOCCBRK) {
tty_wait_until_sent(tty, 0);
if (signal_pending(current))
return -EINTR;
}
break;
}
switch (cmd) {
case TIOCSTI:
return tiocsti(tty, (char *)arg);
case TIOCGWINSZ:
return tiocgwinsz(tty, (struct winsize *) arg);
case TIOCSWINSZ:
return tiocswinsz(tty, real_tty, (struct winsize *) arg);
case TIOCCONS:
return tioccons(inode, tty, real_tty);
case FIONBIO:
return fionbio(file, (int *) arg);
case TIOCEXCL:
set_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCNXCL:
clear_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCNOTTY:
if (current->tty != tty)
return -ENOTTY;
if (current->leader)
disassociate_ctty(0);
task_lock(current);
current->tty = NULL;
task_unlock(current);
return 0;
case TIOCSCTTY:
return tiocsctty(tty, arg);
case TIOCGPGRP:
return tiocgpgrp(tty, real_tty, (pid_t *) arg);
case TIOCSPGRP:
return tiocspgrp(tty, real_tty, (pid_t *) arg);
case TIOCGSID:
return tiocgsid(tty, real_tty, (pid_t *) arg);
case TIOCGETD:
return put_user(tty->ldisc.num, (int *) arg);
case TIOCSETD:
return tiocsetd(tty, (int *) arg);
#ifdef CONFIG_VT
case TIOCLINUX:
return tioclinux(tty, arg);
#endif
case TIOCTTYGSTRUCT:
return tiocttygstruct(tty, (struct tty_struct *) arg);
/*
* Break handling
*/
case TIOCSBRK: /* Turn break on, unconditionally */
tty->driver.break_ctl(tty, -1);
return 0;
case TIOCCBRK: /* Turn break off, unconditionally */
tty->driver.break_ctl(tty, 0);
return 0;
case TCSBRK: /* SVID version: non-zero arg --> no break */
/*
* XXX is the above comment correct, or the
* code below correct? Is this ioctl used at
* all by anyone?
*/
if (!arg)
return send_break(tty, HZ/4);
return 0;
case TCSBRKP: /* support for POSIX tcsendbreak() */
return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
}
if (tty->driver.ioctl) {
int retval = (tty->driver.ioctl)(tty, file, cmd, arg);
if (retval != -ENOIOCTLCMD)
return retval;
}
if (tty->ldisc.ioctl) {
int retval = (tty->ldisc.ioctl)(tty, file, cmd, arg);
if (retval != -ENOIOCTLCMD)
return retval;
}
return -EINVAL;
}