📄 serial_core.c
字号:
ports[idx].membase == NULL)) for (idx = 0; idx < nr; idx++) if (ports[idx].iobase != 0 || ports[idx].membase != NULL) break; co->index = idx; return ports + idx;}/** * uart_parse_options - Parse serial port baud/parity/bits/flow contro. * @options: pointer to option string * @baud: pointer to an 'int' variable for the baud rate. * @parity: pointer to an 'int' variable for the parity. * @bits: pointer to an 'int' variable for the number of data bits. * @flow: pointer to an 'int' variable for the flow control character. * * uart_parse_options decodes a string containing the serial console * options. The format of the string is <baud><parity><bits><flow>, * eg: 115200n8r */void __inituart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow){ char *s = options; *baud = simple_strtoul(s, NULL, 10); while (*s >= '0' && *s <= '9') s++; if (*s) *parity = *s++; if (*s) *bits = *s++ - '0'; if (*s) *flow = *s;}struct baud_rates { unsigned int rate; unsigned int cflag;};static const struct baud_rates baud_rates[] = { { 921600, B921600 }, { 460800, B460800 }, { 230400, B230400 }, { 115200, B115200 }, { 57600, B57600 }, { 38400, B38400 }, { 19200, B19200 }, { 9600, B9600 }, { 4800, B4800 }, { 2400, B2400 }, { 1200, B1200 }, { 0, B38400 }};/** * uart_set_options - setup the serial console parameters * @port: pointer to the serial ports uart_port structure * @co: console pointer * @baud: baud rate * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) * @bits: number of data bits * @flow: flow control character - 'r' (rts) */int __inituart_set_options(struct uart_port *port, struct console *co, int baud, int parity, int bits, int flow){ struct termios termios; int i; /* * Ensure that the serial console lock is initialised * early. */ spin_lock_init(&port->lock); memset(&termios, 0, sizeof(struct termios)); termios.c_cflag = CREAD | HUPCL | CLOCAL; /* * Construct a cflag setting. */ for (i = 0; baud_rates[i].rate; i++) if (baud_rates[i].rate <= baud) break; termios.c_cflag |= baud_rates[i].cflag; if (bits == 7) termios.c_cflag |= CS7; else termios.c_cflag |= CS8; switch (parity) { case 'o': case 'O': termios.c_cflag |= PARODD; /*fall through*/ case 'e': case 'E': termios.c_cflag |= PARENB; break; } 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; mutex_lock(&state->mutex); if (state->info && state->info->flags & UIF_INITIALIZED) { const struct uart_ops *ops = port->ops; spin_lock_irq(&port->lock); ops->stop_tx(port); 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); mutex_unlock(&state->mutex); return 0;}int uart_resume_port(struct uart_driver *drv, struct uart_port *port){ struct uart_state *state = drv->state + port->line; mutex_lock(&state->mutex); 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) { const struct uart_ops *ops = port->ops; int ret; ops->set_mctrl(port, 0); ret = ops->startup(port); if (ret == 0) { uart_change_speed(state, NULL); spin_lock_irq(&port->lock); ops->set_mctrl(port, port->mctrl); ops->start_tx(port); spin_unlock_irq(&port->lock); } else { /* * Failed to resume - maybe hardware went away? * Clear the "initialized" flag so we won't try * to call the low level drivers shutdown method. */ state->info->flags &= ~UIF_INITIALIZED; uart_shutdown(state); } } mutex_unlock(&state->mutex); return 0;}static inline voiduart_report_port(struct uart_driver *drv, struct uart_port *port){ char address[64]; switch (port->iotype) { case UPIO_PORT: snprintf(address, sizeof(address), "I/O 0x%x", port->iobase); break; case UPIO_HUB6: snprintf(address, sizeof(address), "I/O 0x%x offset 0x%x", port->iobase, port->hub6); break; case UPIO_MEM: case UPIO_MEM32: case UPIO_AU: snprintf(address, sizeof(address), "MMIO 0x%lx", port->mapbase); break; default: strlcpy(address, "*unknown*", sizeof(address)); break; } printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", port->dev ? port->dev->bus_id : "", port->dev ? ": " : "", drv->dev_name, port->line, address, 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) 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; port->ops->config_port(port, flags); } if (port->type != PORT_UNKNOWN) { unsigned long flags; uart_report_port(drv, port); /* * 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); port->ops->set_mctrl(port, 0); spin_unlock_irqrestore(&port->lock, flags); /* * Power down all ports by default, except the * console if we have one. */ if (!uart_console(port)) uart_change_pm(state, 3); }}/* * 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); mutex_lock(&state->mutex); 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); } mutex_unlock(&state->mutex);}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 */ mutex_init(&state->mutex); } 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; BUG_ON(in_interrupt()); if (port->line >= drv->nr) return -EINVAL; state = drv->state + port->line; mutex_lock(&port_mutex); if (state->port) { ret = -EINVAL; goto out; } state->port = port; port->cons = drv->cons; port->info = state->info; /* * If this port is a console, then the spinlock is already * initialised. */ if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) spin_lock_init(&port->lock); uart_configure_port(drv, state, port); /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ tty_register_device(drv->tty_driver, port->line, port->dev); /* * 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: mutex_unlock(&port_mutex); 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); mutex_lock(&port_mutex); /* * Remove the devices from devfs */ tty_unregister_device(drv->tty_driver, port->line); uart_unconfigure_port(drv, state); state->port = NULL; mutex_unlock(&port_mutex); 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->mapbase == port2->mapbase); } return 0;}EXPORT_SYMBOL(uart_match_port);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_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 + -