serial_core.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,430 行 · 第 1/4 页
C
2,430 行
* 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; 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; 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)) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(10*HZ/1000); } set_current_state(TASK_RUNNING); 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)) { uart_change_speed(state, 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("%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) 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); 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 = 5 * HZ / 10; state->closing_wait = 30 * HZ; 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; BUG_ON(in_interrupt()); if (port->line >= drv->nr) return -EINVAL; state = drv->state + port->line; down(&port_sem); if (state->port) { ret = -EINVAL; goto out; } state->port = port; spin_lock_init(&port->lock); port->cons = drv->cons; port->info = state->info; 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); 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? */static 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;}/* * 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->type = port->type; 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 + =
减小字号Ctrl + -
显示快捷键?