首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux的tty驱动的数据链路 02

linux的tty驱动的数据链路 02

 最重要的两个函数:serial8250_isa_init_ports和uart_add_one_port
  serial8250_isa_init_ports主要的工作是初始化uart_8250_port:开启定时器和初始化uart_port.
  uart_add_one_port顾名思议,就是为uart_driver增加一个端口,在uart_driver里的state指向NR个slot, 然后,这个函数的主要工作就是为slot增加一个port.这样,uart_driver就可以通过port对ops操作函数集进行最底层的操作。
  现在来分析下连接部分,也就是tty_driver如何工作,如何连接tty核心层(或者ldisc层)和串口层uart_port.关于操作部分主要是uart_ops.
  uart_open:
  staticint uart_open(struct tty_struct *tty, struct file *filp)
  {
  ………
  retval= uart_startup(tty, state, 0);
  ……
  }
  staticint uart_startup(struct tty_struct *tty, struct uart_state *state,int init_hw)
  {
  ……
  retval= uport->ops->startup(uport);
  ………
  }
  调用了uart_port的操作函数ops的startup,在这个函数里作了一些串口初始化的工作,其中有申请接收数据中断或建立超时轮询处理。
  在startup里面申请了接收数据中断,那么这个中断服务程序就跟读操作密切相关了,从tty核心层的读操作可知,接收到的数据一定是传送到read_buf中的。现在来看是中断服务程序。
  调用receive_chars来接收数据,在receive_chars中,出现了两个传输数据的函数:
  tty_insert_flip_char和tty_flip_buffer_push.
  static inline int tty_insert_flip_char(struct tty_struct *tty,
  unsigned char ch, char flag)
  {
  struct tty_buffer *tb = tty->buf.tail;
  if(tb && tb->used < tb->size) {
  tb->flag_buf_ptr[tb->used]= flag;
  tb->char_buf_ptr[tb->used++]= ch;
  return1;
  }
  return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
  }
  当当前的tty_buffer空间不够时调用tty_insert_flip_string_flags,在这个函数里会去查找下一个tty_buffer,并将数据放到下一个tty_buffer的char_buf_ptr里。
  那么char_buf_ptr的数据怎样与线路规程中的read_buf关联的呢,我们看,在初始化tty_buffer的时候,也就是在tty_buffer_init函数中:
  void tty_buffer_init(struct tty_struct *tty)
  {
  spin_lock_init(&tty->buf.lock);
  tty->buf.head= NULL;
  tty->buf.tail= NULL;
  tty->buf.free= NULL;
  tty->buf.memory_used= 0;
  INIT_DELAYED_WORK(&tty->buf.work,flush_to_ldisc);
  }
  在函数的最后,初始化了一个工作队列。
  而这个队列在什么时候调度呢,在驱动层里receive_chars的最后调用了tty_flip_buffer_push这个函数。
  void tty_flip_buffer_push(struct tty_struct *tty)
  {
  unsigned long flags;spin_lock_irqsave(&tty->buf.lock, flags);if (tty->buf.tail != NULL)
  tty->buf.tail->commit = tty->buf.tail->used;spin_unlock_irqrestore(&tty->buf.lock, flags);
  if (tty->low_latency)
  flush_to_ldisc(&tty->buf.work.work);else schedule_delayed_work(&tty->buf.work, 1);
  }
  那么,在push数据到tty_buffer的时候有两种方式,一种是flush_to_ldisc,另一种就是调度tty缓冲区的工作队列。
  flush_to_ldisc是队列调用的函数:
  static void flush_to_ldisc(struct work_struct *work)
  {
  ……
  while((head = tty->buf.head) != NULL) {
  ………
  count= head->commit – head->read;
  ………
  char_buf= head->char_buf_ptr + head->read;
  flag_buf= head->flag_buf_ptr + head->read;
  head->read+= count;
  disc->ops->receive_buf(tty,char_buf,
  flag_buf,count);
  ………
  }
  ……
  }
  这个函数主要的功能是,从tty_buffer中找到数据缓冲区char_buf_ptr,并将这个缓冲区指针传递给线路规程的操作函数receive_buf.再来看receive_buf:
  static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char*cp,
  char *fp, int count)
  {
  ……
  if(tty->real_raw) {
  ………
  memcpy(tty->read_buf+ tty->read_head, cp, i);
  ………
  }else{
  ………
  switch(flags) {
  caseTTY_NORMAL:
  n_tty_receive_char(tty,*p);
  break;
  ……
  }
  if(tty->ops->flush_chars)
  tty->ops->flush_chars(tty);
  ………
  }
  ………
  }
  从上面这段代码可以看到,if条件成立,明显地是拷贝数据进tty的read_buf;进入else,在正常的状态下会调用n_tty_receive_char,然后会调用put_tty_queue,在这个函数里最终还是把数据拷贝到tty的read_buf中。
  到此,tty驱动的读操作数据链路基本上连通了。
  uart_write:
  static int uart_write(struct tty_struct *tty,
  const unsigned char *buf, int count)
  {
  ……
  port= state->uart_port;
  circ= &state->xmit;
  ……
  while(1){
  c= CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
  ………
  memcpy(circ->buf+ circ->head, buf, c);
  ………
  }
  ……
  uart_start(tty);
  return ret;
  }
  上面代码的意思是把要写的数据拷贝到state的缓冲区里。然后调用uart_start.
  static void __uart_start(struct tty_struct *tty)
  {
  struct uart_state *state = tty->driver_data;
  struct uart_port *port = state->uart_port;
  if(!uart_circ_empty(&state->xmit) && state->xmit.buf&&
  !tty->stopped && !tty->hw_stopped)
  port->ops->start_tx(port);
  }
  调用了uart_port的操作函数集的start_tx.
  static void serial8250_start_tx(struct uart_port *port)
  {
  struct uart_8250_port *up = container_of(port, struct uart_8250_port, port);
  ……
  transmit_chars(up);
  ………
  }
  在transmit_chars中会把state->xmit缓冲区的数据写进串口发送数据寄存器,也就是数据到达硬件层。到此,写操作的数据
返回列表