arm嵌入式开发之终端设备

ARM 70浏览

1.linux系统中终端是一种字符设备,用tty来简称。有几类:
串行端口终端/dev/ttySn  计算机把每个串口作为一个字符设备,这些端口所对应的设备名称为/dev/ttyS0或/dev/tts/0等设备号为(4,0);
伪终端/dev/pty 是成对的逻辑终端设备/dev/ptyp /dev/ttyp 登录程序与telnet就是通过其进行通信的;
控制台终端 /dev/ttyn,/dev/console 命令ps-ax 查看进程与哪个控制终端相连,tty可以查看它具体对应哪个实际的终端设备。
通过查看/proc/tty/drivers文件可以获知什么类型的tty设备及驱动被加载到内核。
2.

主要源文件关系及数据流向 
特定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;
}