linux uart console driver(1)--硬件设备的注册和uart driver的注册

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dongkun152/article/details/80969776

linux uart console driver(1)–硬件设备的注册和uart driver的注册

uart console 是嵌入式设备中的console,是嵌入式设备开发调试中不可缺少的部件。uart console 在系统中的设备名是/dev/console.
linux 提供的uart driver框架位于linux/drivers/tty/serial/8250目录当中。uart console driver和tty driver 是紧密相关的(linux 3.10 kernel)。
相关的文件:
1. 8250 serial相关:8250_core.c 8250_dma.c 8250_early.c
2. tty 相关: n_tty.c pty.c tty_buffer.c tty_io.c tty_ioctl.c tty_ldisc.c tty_mutex.c tty_port.c
3. console 相关:/kernel/printk.c

结构框图

这里写图片描述
图 1 uart console driver系统框图
上图为uart console的系统框架图。uart console的工作流程开始前需要建立系统框架,这包括,uart 硬件设备的注册,串口driver的注册,console信息的初始化,tty driver的注册。接下来就可以对console设备进行读写,读console和写console的执行流程是uart console driver工作的关键过程。

uart 硬件设备的注册

关键数据结构和代码片段:

struct uart_port {
    spinlock_t      lock;           /* port lock */
    unsigned long       iobase;         /* in/out[bwl] */
    unsigned char __iomem   *membase;       /* read/write[bwl] */
    unsigned int        (*serial_in)(struct uart_port *, int);
    void            (*serial_out)(struct uart_port *, int, int);
    void            (*set_termios)(struct uart_port *,
                               struct ktermios *new,
                               struct ktermios *old);
    int         (*handle_irq)(struct uart_port *);
    void            (*pm)(struct uart_port *, unsigned int state,
                      unsigned int old);
    void            (*handle_break)(struct uart_port *);
    unsigned int        irq;            /* irq number */
    unsigned long       irqflags;       /* irq flags  */
    unsigned int        uartclk;        /* base uart clock */
    unsigned int        fifosize;       /* tx fifo size */
    unsigned char       x_char;         /* xon/xoff char */
    unsigned char       regshift;       /* reg offset shift */
    unsigned char       iotype;         /* io access style */
    unsigned char       unused1;

#define UPIO_PORT       (0)
#define UPIO_HUB6       (1)
#define UPIO_MEM        (2)
#define UPIO_MEM32      (3)
#define UPIO_AU         (4)         /* Au1x00 and RT288x type IO */
#define UPIO_TSI        (5)         /* Tsi108/109 type IO */

    unsigned int        read_status_mask;   /* driver specific */
    unsigned int        ignore_status_mask; /* driver specific */
    struct uart_state   *state;         /* pointer to parent state */
    struct uart_icount  icount;         /* statistics */

    struct console      *cons;          /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
    unsigned long       sysrq;          /* sysrq timeout */
#endif

    upf_t           flags;

#define UPF_FOURPORT        ((__force upf_t) (1 << 1))
#define UPF_SAK         ((__force upf_t) (1 << 2))
#define UPF_SPD_MASK        ((__force upf_t) (0x1030))
#define UPF_SPD_HI      ((__force upf_t) (0x0010))
#define UPF_SPD_VHI     ((__force upf_t) (0x0020))
#define UPF_SPD_CUST        ((__force upf_t) (0x0030))
#define UPF_SPD_SHI     ((__force upf_t) (0x1000))
#define UPF_SPD_WARP        ((__force upf_t) (0x1010))
#define UPF_SKIP_TEST       ((__force upf_t) (1 << 6))
#define UPF_AUTO_IRQ        ((__force upf_t) (1 << 7))
#define UPF_HARDPPS_CD      ((__force upf_t) (1 << 11))
#define UPF_LOW_LATENCY     ((__force upf_t) (1 << 13))
#define UPF_BUGGY_UART      ((__force upf_t) (1 << 14))
#define UPF_NO_TXEN_TEST    ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16))
/* Port has hardware-assisted h/w flow control (iow, auto-RTS *not* auto-CTS) */
#define UPF_HARD_FLOW       ((__force upf_t) (1 << 21))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW       ((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW       ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ       ((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR        ((__force upf_t) (1 << 25))
#define UPF_BUG_THRE        ((__force upf_t) (1 << 26))
/* The exact UART type is known and should not be probed.  */
#define UPF_FIXED_TYPE      ((__force upf_t) (1 << 27))
#define UPF_BOOT_AUTOCONF   ((__force upf_t) (1 << 28))
#define UPF_FIXED_PORT      ((__force upf_t) (1 << 29))
#define UPF_DEAD        ((__force upf_t) (1 << 30))
#define UPF_IOREMAP     ((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK     ((__force upf_t) (0x17fff))
#define UPF_USR_MASK        ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

    unsigned int        mctrl;          /* current modem ctrl settings */
    unsigned int        timeout;        /* character-based timeout */
    unsigned int        type;           /* port type */
    const struct uart_ops   *ops;
    unsigned int        custom_divisor;
    unsigned int        line;           /* port index */
    resource_size_t     mapbase;        /* for ioremap */
    struct device       *dev;           /* parent device */
    unsigned char       hub6;           /* this should be in the 8250 driver */
    unsigned char       suspended;
    unsigned char       irq_wake;
    unsigned char       unused[2];
    void            *private_data;      /* generic platform data pointer */
};


----------
struct uart_ops {
    unsigned int    (*tx_empty)(struct uart_port *);
    void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);
    unsigned int    (*get_mctrl)(struct uart_port *);
    void        (*stop_tx)(struct uart_port *);
    void        (*start_tx)(struct uart_port *);
    void        (*throttle)(struct uart_port *);
    void        (*unthrottle)(struct uart_port *);
    void        (*send_xchar)(struct uart_port *, char ch);
    void        (*stop_rx)(struct uart_port *);
    void        (*enable_ms)(struct uart_port *);
    void        (*break_ctl)(struct uart_port *, int ctl);
    int     (*startup)(struct uart_port *);
    void        (*shutdown)(struct uart_port *);
    void        (*flush_buffer)(struct uart_port *);
    void        (*set_termios)(struct uart_port *, struct ktermios *new,
                       struct ktermios *old);
    void        (*set_ldisc)(struct uart_port *, int new);
    void        (*pm)(struct uart_port *, unsigned int state,
                  unsigned int oldstate);
    int     (*set_wake)(struct uart_port *, unsigned int state);

    /*
     * Return a string describing the type of the port
     */
    const char  *(*type)(struct uart_port *);

    /*
     * Release IO and memory resources used by the port.
     * This includes iounmap if necessary.
     */
    void        (*release_port)(struct uart_port *);

    /*
     * Request IO and memory resources used by the port.
     * This includes iomapping the port if necessary.
     */
    int     (*request_port)(struct uart_port *);
    void        (*config_port)(struct uart_port *, int);
    int     (*verify_port)(struct uart_port *, struct serial_struct *);
    int     (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
    int     (*poll_init)(struct uart_port *);
    void        (*poll_put_char)(struct uart_port *, unsigned char);
    int     (*poll_get_char)(struct uart_port *);
#endif
};
----------
struct uart_8250_port {
    struct uart_port    port;
    struct timer_list   timer;      /* "no irq" timer */
    struct list_head    list;       /* ports on this IRQ */
    unsigned short      capabilities;   /* port capabilities */
    unsigned short      bugs;       /* port bugs */
    unsigned int        tx_loadsz;  /* transmit fifo load size */
    unsigned char       acr;
    unsigned char       ier;
    unsigned char       lcr;
    unsigned char       mcr;
    unsigned char       mcr_mask;   /* mask of user bits */
    unsigned char       mcr_force;  /* mask of forced bits */
    unsigned char       cur_iotype; /* Running I/O type */

    /*
     * Some bits in registers are cleared on a read, so they must
     * be saved whenever the register is read but the bits will not
     * be immediately processed.
     */
#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
    unsigned char       lsr_saved_flags;
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
    unsigned char       msr_saved_flags;

    struct uart_8250_dma    *dma;

    /* 8250 specific callbacks */
    int         (*dl_read)(struct uart_8250_port *);
    void            (*dl_write)(struct uart_8250_port *, int);
};


----------


----------
struct uart_8250_dma {
    dma_filter_fn       fn;
    void            *rx_param;
    void            *tx_param;

    int         rx_chan_id;
    int         tx_chan_id;

    struct dma_slave_config rxconf;
    struct dma_slave_config txconf;

    struct dma_chan     *rxchan;
    struct dma_chan     *txchan;

    dma_addr_t      rx_addr;
    dma_addr_t      tx_addr;

    dma_cookie_t        rx_cookie;
    dma_cookie_t        tx_cookie;

    void            *rx_buf;

    size_t          rx_size;
    size_t          tx_size;

    unsigned char       tx_running:1;
};

在8250_core.c 中的全局数组

static struct uart_8250_port serial8250_ports[UART_NR];
//UART_NR = 3 在系统框图中可以找到这个数组

设备注册: 在相应arch的serial.c文件中,这里属于bsp的代码。

int __init bsp_serial_port0_init(void)
{
    struct uart_port up;

    /* clear memory */
    memset(&up, 0, sizeof(up));

    /*
     * UART0
     */
    up.line = 0;
    up.type = PORT_16550A;
    up.uartclk = BSP_UART0_FREQ;
    up.fifosize = 32;
    up.irq = BSP_IRQ_OTHERS;
    up.flags = UPF_SKIP_TEST|UPF_SHARE_IRQ;
    up.mapbase = BSP_UART0_PADDR;
    up.membase = ioremap_nocache(up.mapbase, BSP_UART0_PSIZE);
    up.regshift = 2;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
    up.iotype = UPIO_MEM32;
    up.serial_out = dwapb_serial_out;
    up.serial_in = dwapb_serial_in;
    up.handle_irq = dwapb_serial_irq;
#else
    up.iotype = UPIO_DWAPB;
    up.private_data = (void *)BSP_UART0_USR;
#endif

    if (early_serial_setup(&up) != 0) {
        panic("Sheipa: taroko subsystem bsp_serial_init failed!");
    }

    return 0;
}

device_initcall(bsp_serial_port0_init);
//port 的注册是有三组的,因为完全一样的,这里只写一组。

----------
int __init early_serial_setup(struct uart_port *port)
{
    struct uart_port *p;

    if (port->line >= ARRAY_SIZE(serial8250_ports))
        return -ENODEV;

    serial8250_isa_init_ports();
    p = &serial8250_ports[port->line].port;
    p->iobase       = port->iobase;
    p->membase      = port->membase;
    p->irq          = port->irq;
    p->irqflags     = port->irqflags;
    p->uartclk      = port->uartclk;
    p->fifosize     = port->fifosize;
    p->regshift     = port->regshift;
    p->iotype       = port->iotype;
    p->flags        = port->flags;
    p->mapbase      = port->mapbase;
    p->private_data = port->private_data;
    p->type     = port->type;
    p->line     = port->line;

    set_io_from_upio(p);
    if (port->serial_in)
        p->serial_in = port->serial_in;
    if (port->serial_out)
        p->serial_out = port->serial_out;
    if (port->handle_irq)
        p->handle_irq = port->handle_irq;
    else
        p->handle_irq = serial8250_default_handle_irq;

    return 0;
}

static void __init serial8250_isa_init_ports(void)//初始化port数组
{
    struct uart_8250_port *up;
    static int first = 1;
    int i, irqflag = 0;

    if (!first)
        return;
    first = 0;

    if (nr_uarts > UART_NR)
        nr_uarts = UART_NR;

    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &serial8250_ports[i];
        struct uart_port *port = &up->port;

        port->line = i;
        spin_lock_init(&port->lock);

        init_timer(&up->timer);
        up->timer.function = serial8250_timeout;
        if (!(up->port.flags & UPF_SKIP_TEST))
            up->cur_iotype = 0xFF;

        /*
         * ALPHA_KLUDGE_MCR needs to be killed.
         */
        up->mcr_mask = ~ALPHA_KLUDGE_MCR;
        up->mcr_force = ALPHA_KLUDGE_MCR;

        port->ops = &serial8250_pops;//
    }

    if (share_irqs)
        irqflag = IRQF_SHARED;

    for (i = 0, up = serial8250_ports;
         i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
         i++, up++) {//这里不会执行old_serial_port数组的长度为0
        struct uart_port *port = &up->port;

        printk(KERN_INFO"serial board init\n");

        port->iobase   = old_serial_port[i].port;
        port->irq      = irq_canonicalize(old_serial_port[i].irq);
        port->irqflags = old_serial_port[i].irqflags;
        port->uartclk  = old_serial_port[i].baud_base * 16;
        port->flags    = old_serial_port[i].flags;
        port->hub6     = old_serial_port[i].hub6;
        port->membase  = old_serial_port[i].iomem_base;
        port->iotype   = old_serial_port[i].io_type;
        port->regshift = old_serial_port[i].iomem_reg_shift;
        set_io_from_upio(port);
        port->irqflags |= irqflag;
        if (serial8250_isa_config != NULL)
            serial8250_isa_config(i, &up->port, &up->capabilities);

    }
}

8250_core.c 中的struct uart_ops结构:

static struct uart_ops serial8250_pops = {
    .tx_empty   = serial8250_tx_empty,
    .set_mctrl  = serial8250_set_mctrl,
    .get_mctrl  = serial8250_get_mctrl,
    .stop_tx    = serial8250_stop_tx,
    .start_tx   = serial8250_start_tx,
    .stop_rx    = serial8250_stop_rx,
    .enable_ms  = serial8250_enable_ms,
    .break_ctl  = serial8250_break_ctl,
    .startup    = serial8250_startup,
    .shutdown   = serial8250_shutdown,
    .set_termios    = serial8250_set_termios,
    .set_ldisc  = serial8250_set_ldisc,
    .pm     = serial8250_pm,
    .type       = serial8250_type,
    .release_port   = serial8250_release_port,
    .request_port   = serial8250_request_port,
    .config_port    = serial8250_config_port,
    .verify_port    = serial8250_verify_port,
#ifdef CONFIG_CONSOLE_POLL
    .poll_get_char = serial8250_get_poll_char,
    .poll_put_char = serial8250_put_poll_char,
#endif
};

总结下上面的操作:

  1. 用bsp的硬件信息和操作函数填充uart_port.
  2. 用填充过硬件信息的uart_port 初始化serial8250_ports全局数组。

uart driver框架的建立

关键数据结构:

struct uart_driver {
    struct module       *owner;
    const char      *driver_name;
    const char      *dev_name;
    int          major;
    int          minor;
    int          nr;
    struct console      *cons;

    /*
     * these are private; the low level driver should not
     * touch these; they should be initialised to NULL
     */
    struct uart_state   *state;
    struct tty_driver   *tty_driver;
};

struct uart_state {
    struct tty_port     port;

    enum uart_pm_state  pm_state;
    struct circ_buf     xmit;

    struct uart_port    *uart_port;
};
struct tty_driver {
    int magic;      /* magic number for this structure */
    struct kref kref;   /* Reference management */
    struct cdev *cdevs;
    struct module   *owner;
    const char  *driver_name;
    const char  *name;
    int name_base;  /* offset of printed name */
    int major;      /* major device number */
    int minor_start;    /* start of minor device number */
    unsigned int    num;    /* number of devices allocated */
    short   type;       /* type of tty driver */
    short   subtype;    /* subtype of tty driver */
    struct ktermios init_termios; /* Initial termios */
    unsigned long   flags;      /* tty driver flags */
    struct proc_dir_entry *proc_entry; /* /proc fs entry */
    struct tty_driver *other; /* only used for the PTY driver */

    /*
     * Pointer to the tty data structures
     */
    struct tty_struct **ttys;
    struct tty_port **ports;
    struct ktermios **termios;
    void *driver_state;

    /*
     * Driver methods
     */

    const struct tty_operations *ops;
    struct list_head tty_drivers;
};

8250_core.c 文件中

static int __init serial8250_init(void)
{
    int ret;

    serial8250_isa_init_ports();//初始化serial8250_ports数组,此函数在注册uart硬件信息时执行一次,或者在这里执行一次。

    printk(KERN_INFO "Serial: 8250/16550 driver, "
        "%d ports, IRQ sharing %sabled\n", nr_uarts,
        share_irqs ? "en" : "dis");

    serial8250_reg.nr = UART_NR;
    ret = uart_register_driver(&serial8250_reg);//注册serial8250_reg uart_driver
    if (ret)
        goto out;

    serial8250_isa_devs = platform_device_alloc("serial8250",
                            PLAT8250_DEV_LEGACY);//alloc 一个platform device
    if (!serial8250_isa_devs) {
        ret = -ENOMEM;
        goto unreg_pnp;
    }

    ret = platform_device_add(serial8250_isa_devs);
    if (ret)
        goto put_dev;

    serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);

    ret = platform_driver_register(&serial8250_isa_driver);
    if (ret == 0)
        goto out;

    platform_device_del(serial8250_isa_devs);
put_dev:
    platform_device_put(serial8250_isa_devs);
unreg_uart_drv:
    uart_unregister_driver(&serial8250_reg);
out:
    return ret;
}

上面代码中注册的uart_driver:
static struct uart_driver serial8250_reg = {
    .owner          = THIS_MODULE,
    .driver_name        = "serial",
    .dev_name       = "ttyS",
    .major          = TTY_MAJOR,
    .minor          = 64,
    .cons           = SERIAL8250_CONSOLE,
};

注册uart_driver部分:

struct tty_driver {
    int magic;      /* magic number for this structure */
    struct kref kref;   /* Reference management */
    struct cdev *cdevs;
    struct module   *owner;
    const char  *driver_name;
    const char  *name;
    int name_base;  /* offset of printed name */
    int major;      /* major device number */
    int minor_start;    /* start of minor device number */
    unsigned int    num;    /* number of devices allocated */
    short   type;       /* type of tty driver */
    short   subtype;    /* subtype of tty driver */
    struct ktermios init_termios; /* Initial termios */
    unsigned long   flags;      /* tty driver flags */
    struct proc_dir_entry *proc_entry; /* /proc fs entry */
    struct tty_driver *other; /* only used for the PTY driver */

    /*
     * Pointer to the tty data structures
     */
    struct tty_struct **ttys;
    struct tty_port **ports;
    struct ktermios **termios;
    void *driver_state;

    /*
     * Driver methods
     */

    const struct tty_operations *ops;
    struct list_head tty_drivers; //注册时 加入到tty_drivers全局链表中
};

struct tty_port {
    struct tty_bufhead  buf;        /* Locked internally */
    struct tty_struct   *tty;       /* Back pointer */
    struct tty_struct   *itty;      /* internal back ptr */
    const struct tty_port_operations *ops;  /* Port operations */
    spinlock_t      lock;       /* Lock protecting tty field */
    int         blocked_open;   /* Waiting to open */
    int         count;      /* Usage count */
    wait_queue_head_t   open_wait;  /* Open waiters */
    wait_queue_head_t   close_wait; /* Close waiters */
    wait_queue_head_t   delta_msr_wait; /* Modem status change */
    unsigned long       flags;      /* TTY flags ASY_*/
    unsigned long       iflags;     /* TTYP_ internal flags */
#define TTYP_FLUSHING           1  /* Flushing to ldisc in progress */
#define TTYP_FLUSHPENDING       2  /* Queued buffer flush pending */
    unsigned char       console:1,  /* port is a console */
                low_latency:1;  /* direct buffer flush */
    struct mutex        mutex;      /* Locking */
    struct mutex        buf_mutex;  /* Buffer alloc lock */
    unsigned char       *xmit_buf;  /* Optional buffer */
    unsigned int        close_delay;    /* Close port delay */
    unsigned int        closing_wait;   /* Delay for output */
    int         drain_delay;    /* Set to zero if no pure time
                           based drain is needed else
                           set to size of fifo */
    struct kref     kref;       /* Ref counter */
};


int uart_register_driver(struct uart_driver *drv)
{
    struct tty_driver *normal;
    int i, retval;

    BUG_ON(drv->state);

    /*
     * Maybe we should be using a slab cache for this, especially if
     * we have a large number of ports to handle.
     */
    drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);//创建struct uart_state 数组, 结合框架图, driver的state指向struct uart_state 数组。
    if (!drv->state)
        goto out;

    normal = alloc_tty_driver(drv->nr);//分配tty_driver
    if (!normal)
        goto out_kfree;

    drv->tty_driver = normal;
//初始化tty_driver的相关信息
    normal->driver_name = drv->driver_name;
    normal->name        = drv->dev_name;
    normal->major       = drv->major;//4
    normal->minor_start = drv->minor;//64
    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;//设置flag,
    normal->driver_state    = drv;
    tty_set_operations(normal, &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);//初始化uart_state内部的tty_port,同时初始化tty_port内部的tty_bufhead
        port->ops = &uart_port_ops;//设置port的ops
        port->close_delay     = HZ / 2; /* .5 seconds */
        port->closing_wait    = 30 * HZ;/* 30 seconds */
    }

    retval = tty_register_driver(normal);//注册tty驱动
    if (retval >= 0)
        return retval;

    for (i = 0; i < drv->nr; i++)
        tty_port_destroy(&drv->state[i].port);
    put_tty_driver(normal);
out_kfree:
    kfree(drv->state);
out:
    return -ENOMEM;
}
//分配tty_driver flags = 0, lines = 3
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
        unsigned long flags)
{
    struct tty_driver *driver;
    unsigned int cdevs = 1;
    int err;

    if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
        return ERR_PTR(-EINVAL);

    driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
    if (!driver)
        return ERR_PTR(-ENOMEM);

    kref_init(&driver->kref);
    driver->magic = TTY_DRIVER_MAGIC;
    driver->num = lines;//设置tty_driver支持的设备数量
    driver->owner = owner;
    driver->flags = flags;

    if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
        driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
                GFP_KERNEL);//分配长度为3的struct tty_struct *的数组
        driver->termios = kcalloc(lines, sizeof(*driver->termios),
                GFP_KERNEL);//分配终端属性结构struct ktermios *数组
        if (!driver->ttys || !driver->termios) {
            err = -ENOMEM;
            goto err_free_all;
        }
    }

    if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
        driver->ports = kcalloc(lines, sizeof(*driver->ports),
                GFP_KERNEL);//分配长度为3的struct tty_port *数组
        if (!driver->ports) {
            err = -ENOMEM;
            goto err_free_all;
        }
        cdevs = lines;
    }

    driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);//分配长度为三的字符设备结构
    if (!driver->cdevs) {
        err = -ENOMEM;
        goto err_free_all;
    }

    return driver;
err_free_all:
    kfree(driver->ports);
    kfree(driver->ttys);
    kfree(driver->termios);
    kfree(driver);
    return ERR_PTR(err);
}
EXPORT_SYMBOL(__tty_alloc_driver);
//注册tty驱动函数
/*
 * Called by a tty driver to register itself.
 */
int tty_register_driver(struct tty_driver *driver)
{
    int error;
    int i;
    dev_t dev;
    struct device *d;

    if (!driver->major) {
        error = alloc_chrdev_region(&dev, driver->minor_start,
                        driver->num, driver->name);
        if (!error) {
            driver->major = MAJOR(dev);
            driver->minor_start = MINOR(dev);
        }
    } else {
        dev = MKDEV(driver->major, driver->minor_start);//设备号4:64
        error = register_chrdev_region(dev, driver->num, driver->name);//注册字符设备驱动设备号空间
    }
    if (error < 0)
        goto err;

    if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {//没有执行
        error = tty_cdev_add(driver, dev, 0, driver->num);
        if (error)
            goto err_unreg_char;
    }

    mutex_lock(&tty_mutex);
    list_add(&driver->tty_drivers, &tty_drivers);//将新的ttydriver加入到tty_drivers全局链表中,查找driver时会根据设备号遍历这个链表。
    mutex_unlock(&tty_mutex);

    if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {//没有执行
        for (i = 0; i < driver->num; i++) {
            d = tty_register_device(driver, i, NULL);
            if (IS_ERR(d)) {
                error = PTR_ERR(d);
                goto err_unreg_devs;
            }
        }
    }
    proc_tty_register_driver(driver);//在/proc/tty/driver中创建proc entry
    driver->flags |= TTY_DRIVER_INSTALLED;
    return 0;

err_unreg_devs:
    for (i--; i >= 0; i--)
        tty_unregister_device(driver, i);

    mutex_lock(&tty_mutex);
    list_del(&driver->tty_drivers);
    mutex_unlock(&tty_mutex);

err_unreg_char:
    unregister_chrdev_region(dev, driver->num);
err:
    return error;
}
EXPORT_SYMBOL(tty_register_driver);
//注册uart_driver 完成

添加port部分:

static void __init
serial8250_register_ports(struct uart_driver *drv, struct device *dev)
{
    int i;

    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &serial8250_ports[i];

        if (up->port.dev)
            continue;

        up->port.dev = dev;

        if (up->port.flags & UPF_FIXED_TYPE)
            serial8250_init_fixed_type_port(up, up->port.type);

        uart_add_one_port(drv, &up->port);
    }
}

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
    struct uart_state *state;
    struct tty_port *port;
    int ret = 0;
    struct device *tty_dev;

    BUG_ON(in_interrupt());

    if (uport->line >= drv->nr)
        return -EINVAL;

    state = drv->state + uport->line;
    port = &state->port;//参考系统框图tty_port 和uart_port建立的直观联系

    mutex_lock(&port_mutex);
    mutex_lock(&port->mutex);
    if (state->uart_port) {
        ret = -EINVAL;
        goto out;
    }

    state->uart_port = uport;
    state->pm_state = UART_PM_STATE_UNDEFINED;

    uport->cons = drv->cons;
    uport->state = state;

    /*
     * If this port is a console, then the spinlock is already
     * initialised.
     */
    if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
        spin_lock_init(&uport->lock);
        lockdep_set_class(&uport->lock, &port_lock_key);
    }//当前没有注册console

    uart_configure_port(drv, state, uport);

    /*
     * Register the port whether it's detected or not.  This allows
     * setserial to be used to alter this ports parameters.
     */
    tty_dev = tty_port_register_device_attr(port, drv->tty_driver,
            uport->line, uport->dev, port, tty_dev_attr_groups);
    if (likely(!IS_ERR(tty_dev))) {
        device_set_wakeup_capable(tty_dev, 1);
    } else {
        printk(KERN_ERR "Cannot register tty device on line %d\n",
               uport->line);
    }

    /*
     * Ensure UPF_DEAD is not set.
     */
    uport->flags &= ~UPF_DEAD;

 out:
    mutex_unlock(&port->mutex);
    mutex_unlock(&port_mutex);

    return ret;
}

static void
uart_configure_port(struct uart_driver *drv, struct uart_state *state,
            struct uart_port *port)
{
    unsigned int flags;

    /*
     * If there isn't a port here, don't do anything further.
     */
    if (!port->iobase && !port->mapbase && !port->membase)
        return;

    /*
     * Now do the auto configuration stuff.  Note that config_port
     * is expected to claim the resources and map the port for us.
     */
    flags = 0;
    if (port->flags & UPF_AUTO_IRQ)
        flags |= UART_CONFIG_IRQ;
    if (port->flags & UPF_BOOT_AUTOCONF) {
        if (!(port->flags & UPF_FIXED_TYPE)) {
            port->type = PORT_UNKNOWN;
            flags |= UART_CONFIG_TYPE;
        }
        port->ops->config_port(port, flags);
    }

    if (port->type != PORT_UNKNOWN) {
        unsigned long flags;

        uart_report_port(drv, port); //输出注册port的打印信息

        /* Power up port for set_mctrl() */
        uart_change_pm(state, UART_PM_STATE_ON);

        /*
         * Ensure that the modem control lines are de-activated.
         * keep the DTR setting that is set in uart_set_options()
         * We probably don't need a spinlock around this, but
         */
        spin_lock_irqsave(&port->lock, flags);
        port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);
        spin_unlock_irqrestore(&port->lock, flags);

        /*
         * If this driver supports console, and it hasn't been
         * successfully registered yet, try to re-register it.
         * It may be that the port was not available.
         */
        if (port->cons && !(port->cons->flags & CON_ENABLED))
            register_console(port->cons);//注册console

        /*
         * Power down all ports by default, except the
         * console if we have one.
         */
        if (!uart_console(port))
            uart_change_pm(state, UART_PM_STATE_OFF);
    }
}

console 相关:

struct console {
    char    name[16];
    void    (*write)(struct console *, const char *, unsigned);
    int (*read)(struct console *, char *, unsigned);
    struct tty_driver *(*device)(struct console *, int *);
    void    (*unblank)(void);
    int (*setup)(struct console *, char *);
    int (*early_setup)(void);
    short   flags;
    short   index;
    int cflag;
    void    *data;
    struct   console *next;
};

static struct console serial8250_console = {
    .name       = "ttyS",
    .write      = serial8250_console_write,
    .device     = uart_console_device,
    .setup      = serial8250_console_setup,
    .early_setup    = serial8250_console_early_setup,
    .flags      = CON_PRINTBUFFER | CON_ANYTIME,
    .index      = -1,
    .data       = &serial8250_reg,
};
//注册console
void register_console(struct console *newcon)
{
    int i;
    unsigned long flags;
    struct console *bcon = NULL;

    /*
     * before we register a new CON_BOOT console, make sure we don't
     * already have a valid console
     */
    if (console_drivers && newcon->flags & CON_BOOT) { //此时console_driver有值, 此时注册的console不是boot console, console_driver是指向console的指针,管理console链表
        /* find the last or real console */
        for_each_console(bcon) {
            if (!(bcon->flags & CON_BOOT)) {
                printk(KERN_INFO "Too late to register bootconsole %s%d\n",
                    newcon->name, newcon->index);
                return;
            }
        }
    }

    if (console_drivers && console_drivers->flags & CON_BOOT)//此时console是boot console
        bcon = console_drivers; 

    if (preferred_console < 0 || bcon || !console_drivers)
        preferred_console = selected_console;

    if (newcon->early_setup)//无任何功能的函数
        newcon->early_setup();

    /*
     *  See if we want to use this console driver. If we
     *  didn't select a console we take the first one
     *  that registers here.
     */
    if (preferred_console < 0) { //会执行
        if (newcon->index < 0)
            newcon->index = 0;
        if (newcon->setup == NULL ||
            newcon->setup(newcon, NULL) == 0) { 
            newcon->flags |= CON_ENABLED;//设置为使能
            if (newcon->device) {
                newcon->flags |= CON_CONSDEV; //此console确为console
                preferred_console = 0; //设置找到标志
            }
        }
    }

    /*
     *  See if this console matches one we selected on
     *  the command line.
     */
    for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
            i++) { //console_cmdline 从传递给kernel的参数中获得信息
        if (strcmp(console_cmdline[i].name, newcon->name) != 0)
            continue;
        if (newcon->index >= 0 &&
            newcon->index != console_cmdline[i].index) //参数为ttys1 此时newcon index =-1
            continue;
        if (newcon->index < 0)
            newcon->index = console_cmdline[i].index; //设置index = 1
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
        if (console_cmdline[i].brl_options) {
            newcon->flags |= CON_BRL;
            braille_register_console(newcon,
                    console_cmdline[i].index,
                    console_cmdline[i].options,
                    console_cmdline[i].brl_options);
            return;
        }
#endif
        if (newcon->setup &&
            newcon->setup(newcon, console_cmdline[i].options) != 0) //重新设置波特率等信息
            break;
        newcon->flags |= CON_ENABLED;
        newcon->index = console_cmdline[i].index;
        if (i == selected_console) {
            newcon->flags |= CON_CONSDEV;
            preferred_console = selected_console;
        }
        break;
    }

    if (!(newcon->flags & CON_ENABLED))
        return;

    /*
     * If we have a bootconsole, and are switching to a real console,
     * don't print everything out again, since when the boot console, and
     * the real console are the same physical device, it's annoying to
     * see the beginning boot messages twice
     */
    if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
        newcon->flags &= ~CON_PRINTBUFFER;

    /*
     *  Put this console in the list - keep the
     *  preferred driver at the head of the list.
     */
    console_lock();
    if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
        newcon->next = console_drivers; //加入console_driver 链表
        console_drivers = newcon;
        if (newcon->next)
            newcon->next->flags &= ~CON_CONSDEV; //取消boot console的系统console标志
    } else {
        newcon->next = console_drivers->next;
        console_drivers->next = newcon;
    }
    if (newcon->flags & CON_PRINTBUFFER) {
        /*
         * console_unlock(); will print out the buffered messages
         * for us.
         */
        raw_spin_lock_irqsave(&logbuf_lock, flags);
        console_seq = syslog_seq;
        console_idx = syslog_idx;
        console_prev = syslog_prev;
        raw_spin_unlock_irqrestore(&logbuf_lock, flags);
        /*
         * We're about to replay the log buffer.  Only do this to the
         * just-registered console to avoid excessive message spam to
         * the already-registered consoles.
         */
        exclusive_console = newcon;
    }
    console_unlock();
    console_sysfs_notify();

    /*
     * By unregistering the bootconsoles after we enable the real console
     * we get the "console xxx enabled" message on all the consoles -
     * boot consoles, real consoles, etc - this is to ensure that end
     * users know there might be something in the kernel's log buffer that
     * went to the bootconsole (that they do not see on the real console)
     */
    if (bcon &&
        ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
        !keep_bootcon) {
        /* we need to iterate through twice, to make sure we print
         * everything out, before we unregister the console(s)
         */
        printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",
            newcon->name, newcon->index); //打印注册新console成功
        for_each_console(bcon)
            if (bcon->flags & CON_BOOT)
                unregister_console(bcon);
    } else {
        printk(KERN_INFO "%sconsole [%s%d] enabled\n",
            (newcon->flags & CON_BOOT) ? "boot" : "" ,
            newcon->name, newcon->index);
    }
}
EXPORT_SYMBOL(register_console);

struct device *tty_port_register_device_attr(struct tty_port *port,
        struct tty_driver *driver, unsigned index,
        struct device *device, void *drvdata,
        const struct attribute_group **attr_grp)
{
    tty_port_link_device(port, driver, index);
    return tty_register_device_attr(driver, index, device, drvdata,
            attr_grp);
}
EXPORT_SYMBOL_GPL(tty_port_register_device_attr);

void tty_port_link_device(struct tty_port *port,
        struct tty_driver *driver, unsigned index)
{
    if (WARN_ON(index >= driver->num))
        return;
    driver->ports[index] = port;//将tty_port保存到tty_driver的指针数组中
}

struct device *tty_register_device_attr(struct tty_driver *driver,
                   unsigned index, struct device *device,
                   void *drvdata,
                   const struct attribute_group **attr_grp)
{
    char name[64];
    dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
    struct device *dev = NULL;
    int retval = -ENODEV;
    bool cdev = false;

    if (index >= driver->num) {
        printk(KERN_ERR "Attempt to register invalid tty line number "
               " (%d).\n", index);
        return ERR_PTR(-EINVAL);
    }

    if (driver->type == TTY_DRIVER_TYPE_PTY)
        pty_line_name(driver, index, name);
    else
        tty_line_name(driver, index, name);//按设备号,根据tty_driver名ttyS设置名称

    if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {//将之前创建的三个字符设备驱动加入到设备驱动框架中
        retval = tty_cdev_add(driver, devt, index, 1);
        if (retval)
            goto error;
        cdev = true;
    }

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev) {
        retval = -ENOMEM;
        goto error;
    }

    dev->devt = devt; //设置设备号
    dev->class = tty_class; //将会出现在tty class目录下
    dev->parent = device; //父设备是serial8250 platform 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); //添加到sys设备框架中, 之后通过mdev将会在/dev创建ttySn设备
    if (retval)
        goto error;

    return dev;

error:
    put_device(dev);
    if (cdev)
        cdev_del(&driver->cdevs[index]);
    return ERR_PTR(retval);
}
EXPORT_SYMBOL_GPL(tty_register_device_attr);

到此为止,三个串口设备注册完成。系统启动后,在dev目录下会生成ttyS0, ttyS1, ttyS2,代表三个串口的三个设备节点。
总结下uart driver框架建立的全过程:
1.注册uart driver, 创建关联的tty driver和tty driver相关的字符设备驱动, 以及uart_state. uart_state 内部有tty_port.
2. 注册所有的uart port, 将uart port与tty port关联起来,同时注册系统参数指定的console, 最后将三个字符设备驱动和表示三个port的device注册到驱动模型中。
完成uart硬件设备注册和uart driver 框架建立后,就可以通过打开,读写ttySn节点来操作串口。(必要的serial_in, serial_out等硬件相关函数已经填好)。

ttySn设备的操作在下一节整理介绍。

猜你喜欢

转载自blog.csdn.net/dongkun152/article/details/80969776