📄 serial_amba.c
字号:
break; if (timeout && time_after(jiffies, expire)) break; status = UART_GET_FR(info->port); } set_current_state(TASK_RUNNING);}static void ambauart_hangup(struct tty_struct *tty){ struct amba_info *info = tty->driver_data; struct amba_state *state = info->state; ambauart_flush_buffer(tty); if (info->flags & ASYNC_CLOSING) return; ambauart_shutdown(info); info->event = 0; state->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); info->tty = NULL; wake_up_interruptible(&info->open_wait);}static int block_til_ready(struct tty_struct *tty, struct file *filp, struct amba_info *info){ DECLARE_WAITQUEUE(wait, current); struct amba_state *state = info->state; unsigned long flags; int do_clocal = 0, extra_count = 0, retval; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; } /* * If this is a callout device, then just make sure the normal * device isn't being used. */ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { if (info->flags & ASYNC_NORMAL_ACTIVE) return -EBUSY; if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_SESSION_LOCKOUT) && (info->session != current->session)) return -EBUSY; if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_PGRP_LOCKOUT) && (info->pgrp != current->pgrp)) return -EBUSY; info->flags |= ASYNC_CALLOUT_ACTIVE; return 0; } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { if (info->flags & ASYNC_CALLOUT_ACTIVE) return -EBUSY; info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (info->flags & ASYNC_CALLOUT_ACTIVE) { if (state->normal_termios.c_cflag & CLOCAL) do_clocal = 1; } else { if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, state->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait); save_flags(flags); cli(); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } restore_flags(flags); info->blocked_open++; while (1) { save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) { info->mctrl = TIOCM_DTR | TIOCM_RTS; info->port->set_mctrl(info->port, info->mctrl); } restore_flags(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (do_clocal || (UART_GET_FR(info->port) & AMBA_UARTFR_DCD))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); if (extra_count) state->count++; info->blocked_open--; if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static struct amba_info *ambauart_get(int line){ struct amba_info *info; struct amba_state *state = amba_state + line; state->count++; if (state->info) return state->info; info = kmalloc(sizeof(struct amba_info), GFP_KERNEL); if (info) { memset(info, 0, sizeof(struct amba_info)); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); info->flags = state->flags; info->state = state; info->port = amba_ports + line; tasklet_init(&info->tlet, ambauart_tasklet_action, (unsigned long)info); } if (state->info) { kfree(info); return state->info; } state->info = info; return info;}static int ambauart_open(struct tty_struct *tty, struct file *filp){ struct amba_info *info; int retval, line = MINOR(tty->device) - tty->driver.minor_start;#if DEBUG printk("ambauart_open(%d) called\n", line);#endif // is this a line that we've got? MOD_INC_USE_COUNT; if (line >= SERIAL_AMBA_NR) { MOD_DEC_USE_COUNT; return -ENODEV; } info = ambauart_get(line); if (!info) return -ENOMEM; tty->driver_data = info; info->tty = tty; info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * Make sure we have the temporary buffer allocated */ if (!tmp_buf) { unsigned long page = get_zeroed_page(GFP_KERNEL); if (tmp_buf) free_page(page); else if (!page) { MOD_DEC_USE_COUNT; return -ENOMEM; } tmp_buf = (u_char *)page; } /* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); MOD_DEC_USE_COUNT; return -EAGAIN; } /* * Start up the serial port */ retval = ambauart_startup(info); if (retval) { MOD_DEC_USE_COUNT; return retval; } retval = block_til_ready(tty, filp, info); if (retval) { MOD_DEC_USE_COUNT; return retval; } if ((info->state->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) *tty->termios = info->state->normal_termios; else *tty->termios = info->state->callout_termios; }#ifdef CONFIG_SERIAL_AMBA_CONSOLE if (ambauart_cons.cflag && ambauart_cons.index == line) { tty->termios->c_cflag = ambauart_cons.cflag; ambauart_cons.cflag = 0; }#endif ambauart_change_speed(info, NULL); info->session = current->session; info->pgrp = current->pgrp; return 0;}int __init ambauart_init(void){ int i; ambanormal_driver.magic = TTY_DRIVER_MAGIC; ambanormal_driver.driver_name = "serial_amba"; ambanormal_driver.name = SERIAL_AMBA_NAME; ambanormal_driver.major = SERIAL_AMBA_MAJOR; ambanormal_driver.minor_start = SERIAL_AMBA_MINOR; ambanormal_driver.num = SERIAL_AMBA_NR; ambanormal_driver.type = TTY_DRIVER_TYPE_SERIAL; ambanormal_driver.subtype = SERIAL_TYPE_NORMAL; ambanormal_driver.init_termios = tty_std_termios; ambanormal_driver.init_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; ambanormal_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; ambanormal_driver.refcount = &ambauart_refcount; ambanormal_driver.table = ambauart_table; ambanormal_driver.termios = ambauart_termios; ambanormal_driver.termios_locked = ambauart_termios_locked; ambanormal_driver.open = ambauart_open; ambanormal_driver.close = ambauart_close; ambanormal_driver.write = ambauart_write; ambanormal_driver.put_char = ambauart_put_char; ambanormal_driver.flush_chars = ambauart_flush_chars; ambanormal_driver.write_room = ambauart_write_room; ambanormal_driver.chars_in_buffer = ambauart_chars_in_buffer; ambanormal_driver.flush_buffer = ambauart_flush_buffer; ambanormal_driver.ioctl = ambauart_ioctl; ambanormal_driver.throttle = ambauart_throttle; ambanormal_driver.unthrottle = ambauart_unthrottle; ambanormal_driver.send_xchar = ambauart_send_xchar; ambanormal_driver.set_termios = ambauart_set_termios; ambanormal_driver.stop = ambauart_stop; ambanormal_driver.start = ambauart_start; ambanormal_driver.hangup = ambauart_hangup; ambanormal_driver.break_ctl = ambauart_break_ctl; ambanormal_driver.wait_until_sent = ambauart_wait_until_sent; ambanormal_driver.read_proc = NULL; /* * The callout device is just like the normal device except for * the major number and the subtype code. */ ambacallout_driver = ambanormal_driver; ambacallout_driver.name = CALLOUT_AMBA_NAME; ambacallout_driver.major = CALLOUT_AMBA_MAJOR; ambacallout_driver.subtype = SERIAL_TYPE_CALLOUT; ambacallout_driver.read_proc = NULL; ambacallout_driver.proc_entry = NULL; if (tty_register_driver(&ambanormal_driver)) panic("Couldn't register AMBA serial driver\n"); if (tty_register_driver(&ambacallout_driver)) panic("Couldn't register AMBA callout driver\n"); for (i = 0; i < SERIAL_AMBA_NR; i++) { struct amba_state *state = amba_state + i; state->line = i; state->close_delay = 5 * HZ / 10; state->closing_wait = 30 * HZ; state->callout_termios = ambacallout_driver.init_termios; state->normal_termios = ambanormal_driver.init_termios; } return 0;}__initcall(ambauart_init);#ifdef CONFIG_SERIAL_AMBA_CONSOLE/************** console driver *****************//* * This code is currently never used; console->read is never called. * Therefore, although we have an implementation, we don't use it. * FIXME: the "const char *s" should be fixed to "char *s" some day. * (when the definition in include/linux/console.h is also fixed) */#ifdef used_and_not_const_char_pointerstatic int ambauart_console_read(struct console *co, const char *s, u_int count){ struct amba_port *port = &amba_ports[co->index]; unsigned int status; char *w; int c;#if DEBUG printk("ambauart_console_read() called\n");#endif c = 0; w = s; while (c < count) { status = UART_GET_FR(port); if (UART_RX_DATA(status)) { *w++ = UART_GET_CHAR(port); c++; } else { // nothing more to get, return return c; } } // return the count return c;}#endif/* * Print a string to the serial port trying not to disturb * any possible real use of the port... * * The console_lock must be held when we get here. */static void ambauart_console_write(struct console *co, const char *s, u_int count){ struct amba_port *port = &amba_ports[co->index]; unsigned int status, old_cr; int i; /* * First save the CR then disable the interrupts */ old_cr = UART_GET_CR(port); UART_PUT_CR(port, AMBA_UARTCR_UARTEN); /* * Now, do each character */ for (i = 0; i < count; i++) { do { status = UART_GET_FR(port); } while (!UART_TX_READY(status)); UART_PUT_CHAR(port, s[i]); if (s[i] == '\n') { do { status = UART_GET_FR(port); } while (!UART_TX_READY(status)); UART_PUT_CHAR(port, '\r'); } } /* * Finally, wait for transmitter to become empty * and restore the TCR */ do { status = UART_GET_FR(port); } while (status & AMBA_UARTFR_BUSY); UART_PUT_CR(port, old_cr);}/* * Receive character from the serial port */static int ambauart_console_wait_key(struct console *co){ struct amba_port *port = &amba_ports[co->index]; unsigned int status; int c; do { status = UART_GET_FR(port); } while (!UART_RX_DATA(status)); c = UART_GET_CHAR(port); return c;}static kdev_t ambauart_console_device(struct console *c){ return MKDEV(SERIAL_AMBA_MAJOR, SERIAL_AMBA_MINOR + c->index);}static int __init ambauart_console_setup(struct console *co, char *options){ struct amba_port *port; int baud = 38400; int bits = 8; int parity = 'n'; u_int cflag = CREAD | HUPCL | CLOCAL; u_int lcr_h, quot; if (co->index >= SERIAL_AMBA_NR) co->index = 0; port = &amba_ports[co->index]; if (options) { char *s = options; baud = simple_strtoul(s, NULL, 10); while (*s >= '0' && *s <= '9') s++; if (*s) parity = *s++; if (*s) bits = *s - '0'; } /* * Now construct a cflag setting. */ switch (baud) { case 1200: cflag |= B1200; break; case 2400: cflag |= B2400; break; case 4800: cflag |= B4800; break; default: cflag |= B9600; baud = 9600; break; case 19200: cflag |= B19200; break; case 38400: cflag |= B38400; break; case 57600: cflag |= B57600; break; case 115200: cflag |= B115200; break; } switch (bits) { case 7: cflag |= CS7; lcr_h = AMBA_UARTLCR_H_WLEN_7; break; default: cflag |= CS8; lcr_h = AMBA_UARTLCR_H_WLEN_8; break; } switch (parity) { case 'o': case 'O': cflag |= PARODD; lcr_h |= AMBA_UARTLCR_H_PEN; break; case 'e': case 'E': cflag |= PARENB; lcr_h |= AMBA_UARTLCR_H_PEN | AMBA_UARTLCR_H_EPS; break; } co->cflag = cflag; if (port->fifosize > 1) lcr_h |= AMBA_UARTLCR_H_FEN; quot = (port->uartclk / (16 * baud)) - 1; UART_PUT_LCRL(port, (quot & 0xff)); UART_PUT_LCRM(port, (quot >> 8)); UART_PUT_LCRH(port, lcr_h); /* we will enable the port as we need it */ UART_PUT_CR(port, 0); return 0;}static struct console ambauart_cons ={ name: SERIAL_AMBA_NAME, write: ambauart_console_write,#ifdef used_and_not_const_char_pointer read: ambauart_console_read,#endif device: ambauart_console_device, wait_key: ambauart_console_wait_key, setup: ambauart_console_setup, flags: CON_PRINTBUFFER, index: -1,};void __init ambauart_console_init(void){ register_console(&ambauart_cons);}#endif /* CONFIG_SERIAL_AMBA_CONSOLE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -