汽车电子 CIS 学习(三) 之 UART PORT代码分析
- 四、Uart Port 代码分析
- 4.1 Uart Port 初始化
- 4.2 打开函数cis_uart_open
- 4.3 关闭函数cis_uart_close
- 4.4 写函数cis_uart_write
- 4.5 读函数cis_uart_read
之所以是Uart Port 的原因,是因为我们车机中的MCU 和车机是通过UART 来通信的。
CIS 架构是为MCU 在车机上虚拟化所搭建的,
所以,软件上车机和MCU 通信,必须通过CIS 架构中 uart port 的read 或者 write。
接下来,我们主要是分析下 uart port 做的一些工作。
四、Uart Port 代码分析
4.1 Uart Port 初始化
在 init 初始化函数中,主要是调用 cis_register_port_driver 注册 uart port 端口。
static int __init cis_uart_init(void)
{
  cis_register_port_driver(&cis_uart_port);
  return CIS_OK;
}
late_initcall(cis_uart_init);cis_uart_port 定义如下:
cis_uart_attr 中主要是对 uart 的一些配置信息。
cis_uart_drv 中主要是包括了节点的读写打开关闭操作函数。
struct cis_port_attribute cis_uart_attr = {
  .termios = {
    .c_cflag = CREAD | HUPCL | CLOCAL | CS8,  //控制模式标志
      // CREAD:启用字符接收器;
      // HUPCL: 关闭时挂断调制解调器;
      // CLOCAL: 忽略所有调制解调器的状态行
      // CS8 : 8bit
    .c_iflag = IGNBRK | IGNPAR,   //输入模式标志
      // IGNBRK:忽略BREAK键输入
      // IGNPAR: 忽略输入行中的中止状态
    .c_oflag = 0, //输出模式标志
    .c_lflag = 0, //本地模式标志
    .c_cc[VTIME] = 10,
    .c_cc[VMIN]  = 1,
      //设置MIN值,read调用将一直等待,
      //直到有MIN个字符可读的时候才返回,返回是读取的字符数量。
      //到达文件结尾的时候返回0
  },
};
struct cis_port_driver cis_uart_drv = {
  .open  = cis_uart_open,
  .read  = cis_uart_read,
  .write = cis_uart_write,
  .close = cis_uart_close,
};
struct cis_port_struct cis_uart_port = {
  .port_type = CIS_PORT_TYPE_UART,
  .port_attr = &cis_uart_attr,
  .port_drv  = &cis_uart_drv,
};4.2 打开函数cis_uart_open
static struct file g_cis_file;
static struct file *filp;
static struct file_operations g_cis_fop;
static struct file_operations cis_tty_fops;
static struct file_operations *fops;
static int cis_uart_open(struct cis_port_attribute *port_attr)
{
  struct tty_struct *tty;
  mm_segment_t fs;
  //(1) 初始化默认的tty操作函数结构体
  tty_default_fops(&cis_tty_fops);
  //(2)获取设备的端口号
  num = sscanf(port_attr->name, "/dev/ttyHSL%d", &line);
  filp = &g_cis_file;
  filp->f_op = &g_cis_fop;
  filp->f_path.dentry = &dentry;
  
  inodep = &g_cis_inode;
  inodep->i_rdev = MKDEV(SERIAL_IMX_MAJOR, MINOR_START) + line; // major:207  minor:5
  
  fops = &cis_tty_fops;
  ret = fops->open(inodep, filp);  //打开节点
  
  fs = get_fs();
  set_fs(KERNEL_DS);
  if(fops->unlocked_ioctl(filp, TIOCGETD, (long)&old_ldisc) < 0){
    printk(KERN_ERR"ERR:filp->f_op->unlocked_ioctl!\n");
  } else {
    printk("Old ldisc is %d\n", old_ldisc);
  }
  if(fops->unlocked_ioctl(filp, TIOCSETD, (long)&ldisc) < 0){
    printk(KERN_ERR"ERR:filp->f_op->unlocked_ioctl!\n");
  }
  set_fs(fs);
  tty = ((struct tty_file_private *)filp->private_data)->tty;
  if (!tty) {
    printk(KERN_ERR "tty is null");
    fops->release(inodep, filp);
    return ret;
  }
  
    /* set uart port attribute */
  //memcpy(&tty->termios, &port_attr->termios, sizeof(struct ktermios));
  //(3)配置串口
  tty_set_termios(tty, &(port_attr->termios));
  
  printk(KERN_INFO"%s open %s successful...\n",
      __func__, port_attr->name);
  return ret;
}在上述代码中
(1) 初始化默认的tty操作函数结构体 tty_default_fops(&cis_tty_fops);
tty_default_fops(&cis_tty_fops);
==========>
void tty_default_fops(struct file_operations *fops)
{
  *fops = tty_fops;
}
struct file_operations {
  struct module *owner;
  loff_t (*llseek) (struct file *, loff_t, int);
  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
  ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
  int (*iterate) (struct file *, struct dir_context *);
  unsigned int (*poll) (struct file *, struct poll_table_struct *);
  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  int (*mmap) (struct file *, struct vm_area_struct *);
  int (*open) (struct inode *, struct file *);
  int (*flush) (struct file *, fl_owner_t id);
  int (*release) (struct inode *, struct file *);
  int (*fsync) (struct file *, loff_t, loff_t, int datasync);
  int (*aio_fsync) (struct kiocb *, int datasync);
  int (*fasync) (int, struct file *, int);
  int (*lock) (struct file *, int, struct file_lock *);
  ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
  unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
  int (*check_flags)(int);
  int (*flock) (struct file *, int, struct file_lock *);
  ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
  ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
  int (*setlease)(struct file *, long, struct file_lock **, void **);
  long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len);
  int (*show_fdinfo)(struct seq_file *m, struct file *f);
};(3)配置串口 tty_set_termios
/**
 *  tty_set_termios   - update termios values
 *  @tty: tty to update
 *  @new_termios: desired new value
 *
 *  Perform updates to the termios values set on this terminal. There
 *  is a bit of layering violation here with n_tty in terms of the
 *  internal knowledge of this function.
 *
 *  Locking: termios_rwsem
 */
int tty_set_termios(struct tty_struct *tty, struct ktermios *new_termios)
{
  /*
   *  Perform the actual termios internal changes under lock.
   */
  /* FIXME: we need to decide on some locking/ordering semantics
     for the set_termios notification eventually */
  old_termios = tty->termios;
  tty->termios = *new_termios;
  /* See if packet mode change of state. */
  if (tty->link && tty->link->packet) {
    int extproc = (old_termios.c_lflag & EXTPROC) |
        (tty->termios.c_lflag & EXTPROC);
    int old_flow = ((old_termios.c_iflag & IXON) &&
        (old_termios.c_cc[VSTOP] == '\023') &&
        (old_termios.c_cc[VSTART] == '\021'));
    int new_flow = (I_IXON(tty) &&
        STOP_CHAR(tty) == '\023' &&
        START_CHAR(tty) == '\021');
    if ((old_flow != new_flow) || extproc) {
      spin_lock_irqsave(&tty->ctrl_lock, flags);
      if (old_flow != new_flow) {
        tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
        if (new_flow)
          tty->ctrl_status |= TIOCPKT_DOSTOP;
        else
          tty->ctrl_status |= TIOCPKT_NOSTOP;
      }
      if (extproc)
        tty->ctrl_status |= TIOCPKT_IOCTL;
      spin_unlock_irqrestore(&tty->ctrl_lock, flags);
      wake_up_interruptible(&tty->link->read_wait);
    }
  }
  if (tty->ops->set_termios)
    (*tty->ops->set_termios)(tty, &old_termios);
  else
    tty_termios_copy_hw(&tty->termios, &old_termios);
  ld = tty_ldisc_ref(tty);
  if (ld != NULL) {
    if (ld->ops->set_termios)
      (ld->ops->set_termios)(tty, &old_termios);
    tty_ldisc_deref(ld);
  }
  up_write(&tty->termios_rwsem);
  return 0;
}
EXPORT_SYMBOL_GPL(tty_set_termios);4.3 关闭函数cis_uart_close
在关闭函数中,主要是对文件进行关闭。
void cis_uart_close(struct cis_port_attribute *port_attr)
{
  mm_segment_t fs;
  fs = get_fs();
  set_fs(KERNEL_DS);
  if(fops->unlocked_ioctl(&g_cis_file, TIOCSETD, (long)&old_ldisc) < 0){
    printk(KERN_ERR"ERR:filp->f_op->unlocked_ioctl!\n");
  }
  set_fs(fs);
  (void)filp_close(&g_cis_file, current->files);
  return;
}4.4 写函数cis_uart_write
在cis_uart_write 中主要是调用 cis_tty_write_data函数来发关数据的。
int cis_uart_write(struct cis_port_attribute *port_attr, unsigned char *data, unsigned char len)
{
  wr_num = cis_tty_write_data(data, len);
  return wr_num;
}cis_tty_write_data 函数的参数为 要发送的数据 和 数据的长度,如下:
ssize_t cis_tty_write_data(const unsigned char *buf, size_t nr)
{
  struct cis_uart *cu = gcu;
  struct tty_struct *tty = cu->tty;
  const unsigned char *b = buf;
  DECLARE_WAITQUEUE(wait, current);   // 创建一个等待队列 wait
  int c;
  ssize_t retval = 0;
  add_wait_queue(&cu->write_wait, &wait);   // 将write_wait 放入等待队列中
  while (1) {
    set_current_state(TASK_INTERRUPTIBLE);  // 将当前队列设置为任务可被中断唤醒
    while (nr > 0) {
      c = tty->ops->write(tty, b, nr);  // 调用tty 字符设备的write 函数
      if (c < 0) {
        retval = c;
        goto break_out;
      }
      if (!c)
        break;
      b += c;
      nr -= c;
    }
    if (!nr)
      break;
    schedule(); // 触发调度, 避免因为写数据占用CPU资源,导致一些更加重要的工作没法运行
  }
break_out:
  __set_current_state(TASK_RUNNING);      // 将当前进程设置为运行状态
  remove_wait_queue(&cu->write_wait, &wait);  // 将write_wait 从wait 等待队列中移除
  
  return (b - buf) ? b - buf : retval;
}4.5 读函数cis_uart_read
ssize_t cis_tty_read_data(unsigned char *buf, size_t nr)
{
  struct cis_uart *cu = gcu;
  unsigned char *b = buf;
  DECLARE_WAITQUEUE(wait, current); // 创建一个等待队列 wait
  add_wait_queue(&cu->read_wait, &wait);  //将当前 read_wait 队列加入 等待wait 中
  while (nr) {
    if  (cis_force_exit)
      break;
    /* This statement must be first before checking for input
       so that any interrupt will set the state back to TASK_RUNNING. */
    set_current_state(TASK_INTERRUPTIBLE);  // 将当前队列设置为可中断
    if (!cis_input_available_p(cu, 0)) {
      =====================>
        //不断的判断串口是否有收到数据,如果正常收到数据(且数据 != 0x1C),则开如读取数据,否则继续休眠
        if (cu->read_cnt >= (amt ? amt : 1)) {
          while (count < cu->read_cnt) {
            pos = cu->read_tail + count++;
            pos &= (CIS_TTY_BUF_SIZE - 1);
            if (cu->read_buf[pos] == 0x1C)
              return 1;
          }
        }
      <=====================
      //trace10_num++;
      timeout = schedule_timeout(timeout);
      //trace11_num++;
      continue;
    }
    __set_current_state(TASK_RUNNING);
  
    /* The copy function takes the read lock and handles
       locking internally for this case */
    uncopied = cis_copy_from_read_buf(cu, &b, &nr);
    if (uncopied)
      uncopied = cis_copy_from_read_buf(cu, &b, &nr);
    if (uncopied) {
      retval = -EFAULT; 
    }
    break;
  }
  remove_wait_queue(&cu->read_wait, &wait);
  __set_current_state(TASK_RUNNING);
  if(!retval)
    retval = b - buf;
  return retval;
}









