赞
踩
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 uart console driver系统框图
上图为uart console的系统框架图。uart console的工作流程开始前需要建立系统框架,这包括,uart 硬件设备的注册,串口driver的注册,console信息的初始化,tty driver的注册。接下来就可以对console设备进行读写,读console和写console的执行流程是uart console driver工作的关键过程。
关键数据结构和代码片段:
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 };
总结下上面的操作:
关键数据结构:
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设备的操作在下一节整理介绍。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。