Linux串口大致过程

来源:互联网 发布:智睿高清网络电视下载 编辑:程序博客网 时间:2024/05/23 14:56

drivers\tty\serial\imx.c

#define SERIAL_IMX_MAJOR 207
#define MINOR_START  16
#define DEV_NAME  "ttymxc"

/*
 * This determines how often we check the modem status signals
 * for any change.  They generally aren't connected to an IRQ
 * so we have to poll them.  We also check immediately before
 * filling the TX fifo incase CTS has been dropped.
 */
#define MCTRL_TIMEOUT (250*HZ/1000)

#define DRIVER_NAME "IMX-uart"

#define UART_NR 8

static struct imx_port *imx_ports[UART_NR];

static struct uart_driver imx_reg = {
 .owner          = THIS_MODULE,
 .driver_name    = DRIVER_NAME,
 .dev_name       = DEV_NAME,
 .major          = SERIAL_IMX_MAJOR,
 .minor          = MINOR_START,
 .nr             = ARRAY_SIZE(imx_ports),
 .cons           = IMX_CONSOLE,
};

static struct platform_driver serial_imx_driver = {
 .probe  = serial_imx_probe,
 .remove  = serial_imx_remove,

 .suspend = serial_imx_suspend,
 .resume  = serial_imx_resume,
 .id_table = imx_uart_devtype,
 .driver  = {
  .name = "imx-uart",
  .of_match_table = imx_uart_dt_ids,
 },
};

static struct uart_ops imx_pops = {
 .tx_empty = imx_tx_empty,
 .set_mctrl = imx_set_mctrl,//设置串口modem控制模式
 .get_mctrl = imx_get_mctrl,//获取串口modem控制模式
 .stop_tx = imx_stop_tx,//停止发送
 .start_tx = imx_start_tx,//开始发送
 .stop_rx = imx_stop_rx,//停止接收
 .enable_ms = imx_enable_ms,//使能modem状态信息
 .break_ctl = imx_break_ctl,
 .startup = imx_startup,//打开串口
 .shutdown = imx_shutdown,//关闭串口
 .flush_buffer = imx_flush_buffer,
 .set_termios = imx_set_termios,//设置串口参数
 .type  = imx_type,//io访问类型
 .config_port = imx_config_port,//配置端口
 .verify_port = imx_verify_port,//校验端口
#if defined(CONFIG_CONSOLE_POLL)
 .poll_init      = imx_poll_init,
 .poll_get_char  = imx_poll_get_char,
 .poll_put_char  = imx_poll_put_char,
#endif
};

imx_serial_init
 uart_register_driver(&imx_reg);-------------------------------> uart_register_driver
 platform_driver_register(&serial_imx_driver);


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct uart_state {
 struct tty_port  port;

 enum uart_pm_state pm_state;
 struct circ_buf  xmit;

 struct uart_port *uart_port;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct ktermios tty_std_termios = { /* for the benefit of tty drivers  */
 .c_iflag = ICRNL | IXON,
 .c_oflag = OPOST | ONLCR,
 .c_cflag = B38400 | CS8 | CREAD | HUPCL,
 .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
     ECHOCTL | ECHOKE | IEXTEN,
 .c_cc = INIT_C_CC,
 .c_ispeed = 38400,
 .c_ospeed = 38400
};


// drivers\tty\serial\serial_core.c
int uart_register_driver(struct uart_driver *drv)
 struct tty_driver *normal;
 drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);// 分配8个struct uart_state
 normal = alloc_tty_driver(drv->nr);// drv->nr = 8
    struct tty_driver *ret = tty_alloc_driver(lines, 0);// struct tty_driver,分配lines = 8
           __tty_alloc_driver(lines, THIS_MODULE, flags)// lines = 8 flags=0
            struct tty_driver *driver;
             driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
             if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
              driver->ttys = kcalloc(lines, sizeof(*driver->ttys), GFP_KERNEL);// 分配struct tty_struct **ttys;
              driver->termios = kcalloc(lines, sizeof(*driver->termios), GFP_KERNEL);// 分配struct ktermios **termios;
             }
             if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
              driver->ports = kcalloc(lines, sizeof(*driver->ports), GFP_KERNEL);// 分配struct tty_port **ports;
              cdevs = lines;
             }
             driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);// 分配一个cdev
 drv->tty_driver = normal;// uart_driver的tty_driver指向分配的normal(tty_driver)
 
 normal->driver_name = drv->driver_name;// 驱动名"IMX-uart"
 normal->name  = drv->dev_name;// 设备名"ttymxc"
 normal->major  = drv->major;// 主设备号207
 normal->minor_start = drv->minor;// 次设备好开始值16
 normal->type  = TTY_DRIVER_TYPE_SERIAL;
 normal->subtype  = SERIAL_TYPE_NORMAL;
 normal->init_termios = tty_std_termios;
 normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
 normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
 normal->flags  = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
 normal->driver_state    = drv;// tty_driver的driver_state回指uart_driver
 tty_set_operations(normal, &uart_ops);
  driver->ops = op;// normal(tty_driver)的ops指向op----------------------> uart_ops

 /*
  * Initialise the UART state(s).
  */
 for (i = 0; i < drv->nr; i++) {
  struct uart_state *state = drv->state + i;
  struct tty_port *port = &state->port;

  tty_port_init(port);// drivers\tty\tty_port.c
   tty_buffer_init(port);// drivers\tty\tty_buffer.c
    INIT_WORK(&buf->work, flush_to_ldisc);// 初始化接收数据工作队列 ---------------------------->接收数据中断(imx_rxint)时会调度工作队列
  port->ops = &uart_port_ops;
 }

 retval = tty_register_driver(normal);// drivers\tty\tty_io.c
    retval = tty_register_driver(normal);
       register_chrdev_region(dev, driver->num, driver->name);// 驱动名"IMX-uart"
       tty_cdev_add(driver, dev, 0, driver->num);
         cdev_init(&driver->cdevs[index], &tty_fops);
         driver->cdevs[index].owner = driver->owner;
         return cdev_add(&driver->cdevs[index], dev, count);
 list_add(&driver->tty_drivers, &tty_drivers);// 把tty_drivers挂到tty驱动列表上
 for (i = 0; i < driver->num; i++) {
  d = tty_register_device(driver, i, NULL);
    struct device *dev = NULL;
    retval = tty_cdev_add(driver, devt, index, 1);
     dev = kzalloc(sizeof(*dev), GFP_KERNEL);

     dev->devt = devt;
     dev->class = tty_class;
     dev->parent = device;
     dev->release = tty_device_create_release;
     dev_set_name(dev, "%s", name);
     dev->groups = attr_grp;
     dev_set_drvdata(dev, drvdata);

     retval = device_register(dev);// 注册tty设备
 proc_tty_register_driver(driver);
  proc_create_data(driver->driver_name, 0, proc_tty_driver, driver->ops->proc_fops, driver);// /proc/tty/driver/
 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
 
static const struct tty_operations uart_ops = {// drivers\tty\serial\serial_core.c
 .open  = uart_open,
 .close  = uart_close,
 .write  = uart_write,
 .put_char = uart_put_char,
 .flush_chars = uart_flush_chars,
 .write_room = uart_write_room,
 .chars_in_buffer= uart_chars_in_buffer,
 .flush_buffer = uart_flush_buffer,
 .ioctl  = uart_ioctl,
 .throttle = uart_throttle,
 .unthrottle = uart_unthrottle,
 .send_xchar = uart_send_xchar,
 .set_termios = uart_set_termios,
 .set_ldisc = uart_set_ldisc,
 .stop  = uart_stop,
 .start  = uart_start,
 .hangup  = uart_hangup,
 .break_ctl = uart_break_ctl,
 .wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
 .proc_fops = &uart_proc_fops,
#endif
 .tiocmget = uart_tiocmget,
 .tiocmset = uart_tiocmset,
 .get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
 .poll_init = uart_poll_init,
 .poll_get_char = uart_poll_get_char,
 .poll_put_char = uart_poll_put_char,
#endif
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


serial_imx_probe(struct platform_device *pdev)
 struct imx_port *sport;
 sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL);
 
 // 设置struct uart_port
 sport->port.dev = &pdev->dev;
 sport->port.mapbase = res->start;
 sport->port.membase = base;
 sport->port.type = PORT_IMX,
 sport->port.iotype = UPIO_MEM;
 sport->port.irq = rxirq;
 sport->port.fifosize = 32;
 sport->port.ops = &imx_pops;// uart_ops
 sport->port.rs485_config = imx_rs485_config;
 sport->port.rs485.flags = SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
 sport->port.flags = UPF_BOOT_AUTOCONF;
 init_timer(&sport->timer);
 sport->timer.function = imx_timeout;
 sport->timer.data     = (unsigned long)sport;

 /*
  * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
  * chips only have one interrupt.
  */
 if (txirq > 0) {
  ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, dev_name(&pdev->dev), sport);// 接收数据中断
  if (ret)
   return ret;
  ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, dev_name(&pdev->dev), sport);
  if (ret)
   return ret;
 } else {
  ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, dev_name(&pdev->dev), sport);
  if (ret)
   return ret;
 }
 imx_ports[sport->port.line] = sport;
 platform_set_drvdata(pdev, sport);
  dev_set_drvdata(&pdev->dev, data);
   dev->driver_data = data;
 return uart_add_one_port(&imx_reg, &sport->port);--------------------------------------->uart_add_one_port


// --------------------------------------------------------------------------------------------------------------------------------
static const struct file_operations tty_fops = {// drivers\tty\tty_io.c
 .llseek  = no_llseek,
 .read  = tty_read,
 .write  = tty_write,
 .poll  = tty_poll,
 .unlocked_ioctl = tty_ioctl,
 .compat_ioctl = tty_compat_ioctl,
 .open  = tty_open,
 .release = tty_release,
 .fasync  = tty_fasync,
};

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)// drivers\tty\serial\serial_core.c
 struct uart_state *state;
 struct tty_port *port;
 state = drv->state + uport->line;// 获取当前的uart_state
 port = &state->port;// 获取当前的struct tty_port
 state->uart_port = uport;// uport = sport->port (struct uart_port)
 uport->state = state;// uart_port的state回指state
 uart_configure_port(drv, state, uport);
  port->ops->config_port(port, flags);// 调用uart_port的uart_ops的config_port方法,即调用uart_ops imx_pops的imx_config_port(配置端口)
  port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);// 调用uart_port的uart_ops的set_mctrl方法,即调用uart_ops imx_pops的imx_set_mctrl(设置串口modem控制模式)
 tty_dev = tty_port_register_device_attr(port, drv->tty_driver, uport->line, uport->dev, port, uport->tty_groups);
    tty_port_link_device(port, driver, index);
     driver->ports[index] = port;
    return tty_register_device_attr(driver, index, device, drvdata, attr_grp);// drivers\tty\tty_port.c
       tty_cdev_add(driver, devt, index, 1);// drivers\tty\tty_io.c
         cdev_init(&driver->cdevs[index], &tty_fops);
         driver->cdevs[index].owner = driver->owner;
         return cdev_add(&driver->cdevs[index], dev, count);

 
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct tty_ldisc_ops tty_ldisc_N_TTY = {// drivers\rty\N_tty.c

 .magic           = TTY_LDISC_MAGIC,
 .name            = "n_tty",
 .open            = n_tty_open,
 .close           = n_tty_close,
 .flush_buffer    = n_tty_flush_buffer,
 .chars_in_buffer = n_tty_chars_in_buffer,
 .read            = n_tty_read,
 .write           = n_tty_write,
 .ioctl           = n_tty_ioctl,
 .set_termios     = n_tty_set_termios,
 .poll            = n_tty_poll,
 .receive_buf     = n_tty_receive_buf,
 .write_wakeup    = n_tty_write_wakeup,
 .fasync   = n_tty_fasync,
 .receive_buf2  = n_tty_receive_buf2,
};


void __init console_init(void)// drivers\tty\tty_io.c
  tty_ldisc_begin();
   (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
     tty_ldiscs[disc] = new_ldisc;
     new_ldisc->num = disc;
 
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
static const struct file_operations tty_fops = {// drivers\tty\tty_io.c
 .llseek  = no_llseek,
 .read  = tty_read,
 .write  = tty_write,
 .poll  = tty_poll,
 .unlocked_ioctl = tty_ioctl,
 .compat_ioctl = tty_compat_ioctl,
 .open  = tty_open,
 .release = tty_release,
 .fasync  = tty_fasync,
};
 
int __init tty_init(void)
 cdev_init(&tty_cdev, &tty_fops);
 cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1)
 register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty")
 device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app->open:
tty_open->uart_open->startup(驱动实现的startup)

// 应用程序open->tty_open
static int tty_open(struct inode *inode, struct file *filp)// drivers\tty\tty_io.c
 if (tty->ops->open)
  retval = tty->ops->open(tty, filp);// tty_operations -> open() --> tty_operations-> open(即uart_open,drivers\tty\serial\serial_core.c)
     static int uart_open(struct tty_struct *tty, struct file *filp)
        uart_startup(tty, state, 0);
         uart_port_startup(tty, state, init_hw);
          state->xmit.buf = (unsigned char *) page;
          uport->ops->startup(uport);// 调用到i.mx.c里面的uart_ops imx_pops中的imx_startup
              static int imx_startup(struct uart_port *port)

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app->read:
接收数据中断->把数据放入到tty_buffer类型的buf->data中
tty_read->ldisc.read(n_tty_read)->从buf->data中取数据(过程复杂)->put_user(x, ptr);


static irqreturn_t imx_rxint(int irq, void *dev_id)// imx.c
 tty_insert_flip_char(port, rx, flg);// include\linux\tty_flip.h
  tty_insert_flip_string_flags(port, &ch, &flag, 1);
   memcpy(char_buf_ptr(tb, tb->used), chars, space);
    (char *)char_buf_ptr(b, ofs) + b->size;// include\linux\tty.h ------------------------------> 存放读取到的数据
       return ((unsigned char *)b->data) + ofs;
 tty_flip_buffer_push(port);// drivers\tty\tty_buffer.c
  tty_schedule_flip(port);
   schedule_work(&buf->work);// 调度工作队列 --------------------------------> flush_to_ldisc
   
static void flush_to_ldisc(struct work_struct *work)// drivers\tty\tty_buffer.c 工作队列执行函数
 struct tty_port *port = container_of(work, struct tty_port, buf.work);
 struct tty_bufhead *buf = &port->buf;
 struct tty_struct *tty;
 struct tty_ldisc *disc;
 tty = port->itty;
 disc = tty_ldisc_ref(tty);
 while(1) {
  receive_buf(tty, head, count);// drivers\tty\tty_buffer.c
   struct tty_ldisc *disc = tty->ldisc;
   unsigned char *p = char_buf_ptr(head, head->read);------------------------> 获取存放已读取到的数据
   char       *f = NULL;

   if (~head->flags & TTYB_NORMAL)
    f = flag_buf_ptr(head, head->read);

   if (disc->ops->receive_buf2)// drivers\tty\n_tty.c
    count = disc->ops->receive_buf2(tty, p, f, count);
         return n_tty_receive_buf_common(tty, cp, fp, count, 1);
           
   else {
    count = min_t(int, count, tty->receive_room);
    if (count)
     disc->ops->receive_buf(tty, p, f, count);// drivers\tty\n_tty.c
   }
   head->read += count;
   return count;
 }


static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)// drivers\tty\tty_io.c
  ld = tty_ldisc_ref_wait(tty);
  ld->ops->read(tty, file, buf, count);--------------->ldisc->read
     

 ld = tty_ldisc_ref_wait(tty);
 if (ld->ops->read)
  i = ld->ops->read(tty, file, buf, count);-------------> n_tty_read
 
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr)// drivers\tty\N_tty.c
 DEFINE_WAIT_FUNC(wait, woken_wake_function);
 if (file->f_flags & O_NONBLOCK) {
  if (!mutex_trylock(&ldata->atomic_read_lock))
   return -EAGAIN;
 } else {
  if (mutex_lock_interruptible(&ldata->atomic_read_lock))
   return -ERESTARTSYS;
 }
 add_wait_queue(&tty->read_wait, &wait);
 tty_put_user(tty, cs, b++)// drivers\tty\N_tty.c
  tty_audit_add_data(tty, &x, 1, ldata->icanon);// drivers\tty\tty_audit.c
   do {
    memcpy(buf->data + buf->valid, data, run);// 把读到的数据存入到了tty_buffer类型的buf->data中
   } while (size != 0);
  return put_user(x, ptr);// 返回数据给APP

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app写:tty_write->ldisc.write->uart_write->start_tx(驱动实现的start_tx)


static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 ld = tty_ldisc_ref_wait(tty);
 do_tty_write(ld->ops->write, tty, file, buf, count);// ld->ops->write--------------->ldisc->write(n_tty_write)
 ret = write(tty, file, tty->write_buf, size);// --------------->ldisc->write

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr)// drivers\tty\N_tty.c
 add_wait_queue(&tty->write_wait, &wait);
 tty->ops->write(tty, b, nr);// tty_operations->write(即uart_write: drivers\tty\serial\serial_core.c) ------------------> uart_write

static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)// drivers\tty\serial\serial_core.c
 port = state->uart_port;
 circ = &state->xmit;
 __uart_start(tty);
  port->ops->start_tx(port);// 调用驱动imx.c里面的imx_start_tx -------------------------> imx_start_tx

static void imx_start_tx(struct uart_port *port)// drivers\tty\serial\imx.c
 schedule_delayed_work(&sport->tsk_dma_tx, 0);
  dma_tx_work(struct work_struct *w)
   sport->tx_bytes = uart_circ_chars_pending(xmit);// 将desc提交到DMA驱动等待队列
   

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
app异步操作:

static int tty_fasync(int fd, struct file *filp, int on)// drivers\tty\tty_io.c
 __tty_fasync(fd, filp, on);
  fasync_helper(fd, filp, on, &tty->fasync);
  ldisc = tty_ldisc_ref(tty);
  ldisc->ops->fasync(tty, on);----------------> ldisc的n_tty_fasync

static void n_tty_fasync(struct tty_struct *tty, int on)// drivers\tty\n_tty.c
 struct n_tty_data *ldata = tty->disc_data;

 if (!waitqueue_active(&tty->read_wait)) {
  if (on)
   ldata->minimum_to_wake = 1;
  else if (!tty->fasync)
   ldata->minimum_to_wake = N_TTY_BUF_SIZE;
 }


0 0
原创粉丝点击