📄 serial_core.c
字号:
} if (flow == 'r') termios.c_cflag |= CRTSCTS; port->ops->set_termios(port, &termios, NULL); co->cflag = termios.c_cflag; return 0;}#endif /* CONFIG_SERIAL_CORE_CONSOLE */static void uart_change_pm(struct uart_state *state, int pm_state){ struct uart_port *port = state->port; if (port->ops->pm) port->ops->pm(port, pm_state, state->pm_state); state->pm_state = pm_state;}int uart_suspend_port(struct uart_driver *drv, struct uart_port *port){ struct uart_state *state = drv->state + port->line; down(&state->sem); if (state->info && state->info->flags & UIF_INITIALIZED) { struct uart_ops *ops = port->ops; spin_lock_irq(&port->lock); ops->stop_tx(port, 0); ops->set_mctrl(port, 0); ops->stop_rx(port); spin_unlock_irq(&port->lock); /* * Wait for the transmitter to empty. */ while (!ops->tx_empty(port)) { msleep(10); } ops->shutdown(port); } /* * Disable the console device before suspending. */ if (uart_console(port)) console_stop(port->cons); uart_change_pm(state, 3); up(&state->sem); return 0;}int uart_resume_port(struct uart_driver *drv, struct uart_port *port){ struct uart_state *state = drv->state + port->line; down(&state->sem); uart_change_pm(state, 0); /* * Re-enable the console device after suspending. */ if (uart_console(port)) { struct termios termios; /* * First try to use the console cflag setting. */ memset(&termios, 0, sizeof(struct termios)); termios.c_cflag = port->cons->cflag; /* * If that's unset, use the tty termios setting. */ if (state->info && state->info->tty && termios.c_cflag == 0) termios = *state->info->tty->termios; port->ops->set_termios(port, &termios, NULL); console_start(port->cons); } if (state->info && state->info->flags & UIF_INITIALIZED) { struct uart_ops *ops = port->ops; ops->set_mctrl(port, 0); ops->startup(port); uart_change_speed(state, NULL); spin_lock_irq(&port->lock); ops->set_mctrl(port, port->mctrl); ops->start_tx(port, 0); spin_unlock_irq(&port->lock); } up(&state->sem); return 0;}static inline voiduart_report_port(struct uart_driver *drv, struct uart_port *port){ printk(KERN_ERR "enter uart_report_port() line %d passed\n",__LINE__); printk("%s%d", drv->dev_name, port->line); printk(" at "); switch (port->iotype) { case UPIO_PORT: printk("I/O 0x%x", port->iobase); break; case UPIO_HUB6: printk("I/O 0x%x offset 0x%x", port->iobase, port->hub6); break; case UPIO_MEM: case UPIO_MEM32: printk("MMIO 0x%lx", port->mapbase); break; } printk(" (irq = %d) is a %s\n", port->irq, uart_type(port));}static voiduart_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) {printk(KERN_ERR "line %d passed\n",__LINE__); return;} /* * Now do the auto configuration stuff. Note that config_port * is expected to claim the resources and map the port for us. */ flags = UART_CONFIG_TYPE; if (port->flags & UPF_AUTO_IRQ) flags |= UART_CONFIG_IRQ; if (port->flags & UPF_BOOT_AUTOCONF) { port->type = PORT_UNKNOWN; printk(KERN_ERR "line %d passed\n",__LINE__); port->ops->config_port(port, flags); printk(KERN_ERR "line %d passed\n",__LINE__); } if (port->type != PORT_UNKNOWN) { unsigned long flags;printk(KERN_ERR "IN!!line %d passed\n",__LINE__); uart_report_port(drv, port); printk(KERN_ERR "line %d passed\n",__LINE__); /* * Ensure that the modem control lines are de-activated. * We probably don't need a spinlock around this, but */ spin_lock_irqsave(&port->lock, flags); printk(KERN_ERR "line %d passed\n",__LINE__); port->ops->set_mctrl(port, 0); printk(KERN_ERR "line %d passed\n",__LINE__); spin_unlock_irqrestore(&port->lock, flags); /* * Power down all ports by default, except the * console if we have one. */ if (!uart_console(port)) {printk(KERN_ERR "line %d passed\n",__LINE__); uart_change_pm(state, 3);} } else printk(KERN_ERR "OUT!!line %d passed\n",__LINE__);}/* * This reverses the effects of uart_configure_port, hanging up the * port before removal. */static voiduart_unconfigure_port(struct uart_driver *drv, struct uart_state *state){ struct uart_port *port = state->port; struct uart_info *info = state->info; if (info && info->tty) tty_vhangup(info->tty); down(&state->sem); state->info = NULL; /* * Free the port IO and memory resources, if any. */ if (port->type != PORT_UNKNOWN) port->ops->release_port(port); /* * Indicate that there isn't a port here anymore. */ port->type = PORT_UNKNOWN; /* * Kill the tasklet, and free resources. */ if (info) { tasklet_kill(&info->tlet); kfree(info); } up(&state->sem);}static struct tty_operations uart_ops = { .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, .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 .read_proc = uart_read_proc,#endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset,};/** * uart_register_driver - register a driver with the uart core layer * @drv: low level driver structure * * Register a uart driver with the core driver. We in turn register * with the tty layer, and initialise the core driver per-port state. * * We have a proc file in /proc/tty/driver which is named after the * normal driver. * * drv->port should be NULL, and the per-port structures should be * registered using uart_add_one_port after this call has succeeded. */int uart_register_driver(struct uart_driver *drv){ struct tty_driver *normal = NULL; 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 = kmalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); retval = -ENOMEM; if (!drv->state) goto out; memset(drv->state, 0, sizeof(struct uart_state) * drv->nr); normal = alloc_tty_driver(drv->nr); if (!normal) goto out; drv->tty_driver = normal; normal->owner = drv->owner; normal->driver_name = drv->driver_name; normal->devfs_name = drv->devfs_name; normal->name = drv->dev_name; normal->major = drv->major; normal->minor_start = drv->minor; 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->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; 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; state->close_delay = 500; /* .5 seconds */ state->closing_wait = 30000; /* 30 seconds */ init_MUTEX(&state->sem); } retval = tty_register_driver(normal); out: if (retval < 0) { put_tty_driver(normal); kfree(drv->state); } return retval;}/** * uart_unregister_driver - remove a driver from the uart core layer * @drv: low level driver structure * * Remove all references to a driver from the core driver. The low * level driver must have removed all its ports via the * uart_remove_one_port() if it registered them with uart_add_one_port(). * (ie, drv->port == NULL) */void uart_unregister_driver(struct uart_driver *drv){ struct tty_driver *p = drv->tty_driver; tty_unregister_driver(p); put_tty_driver(p); kfree(drv->state); drv->tty_driver = NULL;}struct tty_driver *uart_console_device(struct console *co, int *index){ struct uart_driver *p = co->data; *index = co->index; return p->tty_driver;}/** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port * @port: uart port structure to use for this port. * * This allows the driver to register its own uart_port structure * with the core driver. The main purpose is to allow the low * level uart drivers to expand uart_port, rather than having yet * more levels of structures. */int uart_add_one_port(struct uart_driver *drv, struct uart_port *port){ struct uart_state *state; int ret = 0;printk(KERN_ERR "line %d passed\n",__LINE__); BUG_ON(in_interrupt());printk(KERN_ERR "line %d passed\n",__LINE__); if (port->line >= drv->nr) {printk(KERN_ERR "line %d passed\n",__LINE__); return -EINVAL;} state = drv->state + port->line; down(&port_sem); if (state->port) { ret = -EINVAL; printk(KERN_ERR "line %d passed\n",__LINE__); goto out; } state->port = port; spin_lock_init(&port->lock); port->cons = drv->cons; port->info = state->info;printk(KERN_ERR "before uart_configure_port!\n"); uart_configure_port(drv, state, port); printk(KERN_ERR "after uart_configure_port!\n"); /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ printk(KERN_ERR "line %d passed\n",__LINE__); tty_register_device(drv->tty_driver, port->line, port->dev);printk(KERN_ERR "line %d passed\n",__LINE__); /* * 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->type != PORT_UNKNOWN && port->cons && !(port->cons->flags & CON_ENABLED)) register_console(port->cons); out: up(&port_sem); return ret;}/** * uart_remove_one_port - detach a driver defined port structure * @drv: pointer to the uart low level driver structure for this port * @port: uart port structure for this port * * This unhooks (and hangs up) the specified port structure from the * core driver. No further calls will be made to the low-level code * for this port. */int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port){ struct uart_state *state = drv->state + port->line; BUG_ON(in_interrupt()); if (state->port != port) printk(KERN_ALERT "Removing wrong port: %p != %p\n", state->port, port); down(&port_sem); /* * Remove the devices from devfs */ tty_unregister_device(drv->tty_driver, port->line); uart_unconfigure_port(drv, state); state->port = NULL; up(&port_sem); return 0;}/* * Are the two ports equivalent? */int uart_match_port(struct uart_port *port1, struct uart_port *port2){ if (port1->iotype != port2->iotype) return 0; switch (port1->iotype) { case UPIO_PORT: return (port1->iobase == port2->iobase); case UPIO_HUB6: return (port1->iobase == port2->iobase) && (port1->hub6 == port2->hub6); case UPIO_MEM: return (port1->membase == port2->membase); } return 0;}EXPORT_SYMBOL(uart_match_port);/* * Try to find an unused uart_state slot for a port. */static struct uart_state *uart_find_match_or_unused(struct uart_driver *drv, struct uart_port *port){ int i; /* * First, find a port entry which matches. Note: if we do * find a matching entry, and it has a non-zero use count, * then we can't register the port. */ for (i = 0; i < drv->nr; i++) if (uart_match_port(drv->state[i].port, port)) return &drv->state[i]; /* * We didn't find a matching entry, so look for the first * free entry. We look for one which hasn't been previously * used (indicated by zero iobase). */ for (i = 0; i < drv->nr; i++) if (drv->state[i].port->type == PORT_UNKNOWN && drv->state[i].port->iobase == 0 && drv->state[i].count == 0) return &drv->state[i]; /* * That also failed. Last resort is to find any currently * entry which doesn't have a real port associated with it. */ for (i = 0; i < drv->nr; i++) if (drv->state[i].port->type == PORT_UNKNOWN && drv->state[i].count == 0) return &drv->state[i]; return NULL;}/** * uart_register_port: register uart settings with a port * @drv: pointer to the uart low level driver structure for this port * @port: uart port structure describing the port * * Register UART settings with the specified low level driver. Detect * the type of the port if UPF_BOOT_AUTOCONF is set, and detect the * IRQ if UPF_AUTO_IRQ is set. * * We try to pick the same port for the same IO base address, so that * when a modem is plugged in, unplugged and plugged back in, it gets * allocated the same port. * * Returns negative error, or positive line number. */int uart_register_port(struct uart_driver *drv, struct uart_port *port){ struct uart_state *state; int ret; down(&port_sem); state = uart_find_match_or_unused(drv, port); if (state) { /* * Ok, we've found a line that we can use. * * If we find a port that matches this one, and it appears * to be in-use (even if it doesn't have a type) we shouldn't * alter it underneath itself - the port may be open and * trying to do useful work. */ if (uart_users(state) != 0) { ret = -EBUSY; goto out; } /* * If the port is already initialised, don't touch it. */ if (state->port->type == PORT_UNKNOWN) { state->port->iobase = port->iobase; state->port->membase = port->membase; state->port->irq = port->irq; state->port->uartclk = port->uartclk; state->port->fifosize = port->fifosize; state->port->regshift = port->regshift; state->port->iotype = port->iotype; state->port->flags = port->flags; state->port->line = state - drv->state; state->port->mapbase = port->mapbase; uart_configure_port(drv, state, state->port); } ret = state->port->line; } else ret = -ENOSPC; out: up(&port_sem); return ret;}/** * uart_unregister_port - de-allocate a port * @drv: pointer to the uart low level driver structure for this port * @line: line index previously returned from uart_register_port() * * Hang up the specified line associated with the low level driver, * and mark the port as unused. */void uart_unregister_port(struct uart_driver *drv, int line){ struct uart_state *state; if (line < 0 || line >= drv->nr) { printk(KERN_ERR "Attempt to unregister "); printk("%s%d", drv->dev_name, line); printk("\n"); return; } state = drv->state + line; down(&port_sem); uart_unconfigure_port(drv, state); up(&port_sem);}EXPORT_SYMBOL(uart_write_wakeup);EXPORT_SYMBOL(uart_register_driver);EXPORT_SYMBOL(uart_unregister_driver);EXPORT_SYMBOL(uart_suspend_port);EXPORT_SYMBOL(uart_resume_port);EXPORT_SYMBOL(uart_register_port);EXPORT_SYMBOL(uart_unregister_port);EXPORT_SYMBOL(uart_add_one_port);EXPORT_SYMBOL(uart_remove_one_port);MODULE_DESCRIPTION("Serial driver core");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -