amiserial.c
来自「linux 内核源代码」· C语言 代码 · 共 2,129 行 · 第 1/4 页
C
2,129 行
/* * ------------------------------------------------------------ * rs_open() and friends * ------------------------------------------------------------ */static int block_til_ready(struct tty_struct *tty, struct file * filp, struct async_struct *info){#ifdef DECLARE_WAITQUEUE DECLARE_WAITQUEUE(wait, current);#else struct wait_queue wait = { current, NULL };#endif struct serial_state *state = info->state; int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; /* * 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);#ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);#else return -EAGAIN;#endif } /* * 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))) { info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } 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);#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", state->line, state->count);#endif local_irq_save(flags); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } local_irq_restore(flags); info->blocked_open++; while (1) { local_irq_save(flags); if (tty->termios->c_cflag & CBAUD) rtsdtr_ctrl(SER_DTR|SER_RTS); local_irq_restore(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) {#ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS;#else retval = -EAGAIN;#endif break; } if (!(info->flags & ASYNC_CLOSING) && (do_clocal || (!(ciab.pra & SER_DCD)) )) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; }#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, state->count);#endif schedule(); } __set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); if (extra_count) state->count++; info->blocked_open--;#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", info->line, state->count);#endif if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static int get_async_struct(int line, struct async_struct **ret_info){ struct async_struct *info; struct serial_state *sstate; sstate = rs_table + line; sstate->count++; if (sstate->info) { *ret_info = sstate->info; return 0; } info = kzalloc(sizeof(struct async_struct), GFP_KERNEL); if (!info) { sstate->count--; return -ENOMEM; }#ifdef DECLARE_WAITQUEUE init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait);#endif info->magic = SERIAL_MAGIC; info->port = sstate->port; info->flags = sstate->flags; info->xmit_fifo_size = sstate->xmit_fifo_size; info->line = line; tasklet_init(&info->tlet, do_softint, (unsigned long)info); info->state = sstate; if (sstate->info) { kfree(info); *ret_info = sstate->info; return 0; } *ret_info = sstate->info = info; return 0;}/* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its async structure into * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. */static int rs_open(struct tty_struct *tty, struct file * filp){ struct async_struct *info; int retval, line; line = tty->index; if ((line < 0) || (line >= NR_PORTS)) { return -ENODEV; } retval = get_async_struct(line, &info); if (retval) { return retval; } tty->driver_data = info; info->tty = tty; if (serial_paranoia_check(info, tty->name, "rs_open")) return -ENODEV;#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s, count = %d\n", tty->name, info->state->count);#endif info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; /* * If the port is 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);#ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS);#else return -EAGAIN;#endif } /* * Start up serial port */ retval = startup(info); if (retval) { return retval; } retval = block_til_ready(tty, filp, info); if (retval) {#ifdef SERIAL_DEBUG_OPEN printk("rs_open returning after block_til_ready with %d\n", retval);#endif return retval; }#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s successful...", tty->name);#endif return 0;}/* * /proc fs routines.... */static inline int line_info(char *buf, struct serial_state *state){ struct async_struct *info = state->info, scr_info; char stat_buf[30], control, status; int ret; unsigned long flags; ret = sprintf(buf, "%d: uart:amiga_builtin",state->line); /* * Figure out the current RS-232 lines */ if (!info) { info = &scr_info; /* This is just for serial_{in,out} */ info->magic = SERIAL_MAGIC; info->flags = state->flags; info->quot = 0; info->tty = NULL; } local_irq_save(flags); status = ciab.pra; control = info ? info->MCR : status; local_irq_restore(flags); stat_buf[0] = 0; stat_buf[1] = 0; if(!(control & SER_RTS)) strcat(stat_buf, "|RTS"); if(!(status & SER_CTS)) strcat(stat_buf, "|CTS"); if(!(control & SER_DTR)) strcat(stat_buf, "|DTR"); if(!(status & SER_DSR)) strcat(stat_buf, "|DSR"); if(!(status & SER_DCD)) strcat(stat_buf, "|CD"); if (info->quot) { ret += sprintf(buf+ret, " baud:%d", state->baud_base / info->quot); } ret += sprintf(buf+ret, " tx:%d rx:%d", state->icount.tx, state->icount.rx); if (state->icount.frame) ret += sprintf(buf+ret, " fe:%d", state->icount.frame); if (state->icount.parity) ret += sprintf(buf+ret, " pe:%d", state->icount.parity); if (state->icount.brk) ret += sprintf(buf+ret, " brk:%d", state->icount.brk); if (state->icount.overrun) ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); /* * Last thing is the RS-232 status lines */ ret += sprintf(buf+ret, " %s\n", stat_buf+1); return ret;}static int rs_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ int len = 0, l; off_t begin = 0; len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version); l = line_info(page + len, &rs_table[0]); len += l; if (len+begin > off+count) goto done; if (len+begin < off) { begin += len; len = 0; } *eof = 1;done: if (off >= len+begin) return 0; *start = page + (off-begin); return ((count < begin+len-off) ? count : begin+len-off);}/* * --------------------------------------------------------------------- * rs_init() and friends * * rs_init() is called at boot-time to initialize the serial driver. * --------------------------------------------------------------------- *//* * This routine prints out the appropriate serial driver version * number, and identifies which options were configured into this * driver. */static void show_serial_version(void){ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);}static const struct tty_operations serial_ops = { .open = rs_open, .close = rs_close, .write = rs_write, .put_char = rs_put_char, .flush_chars = rs_flush_chars, .write_room = rs_write_room, .chars_in_buffer = rs_chars_in_buffer, .flush_buffer = rs_flush_buffer, .ioctl = rs_ioctl, .throttle = rs_throttle, .unthrottle = rs_unthrottle, .set_termios = rs_set_termios, .stop = rs_stop, .start = rs_start, .hangup = rs_hangup, .break_ctl = rs_break, .send_xchar = rs_send_xchar, .wait_until_sent = rs_wait_until_sent, .read_proc = rs_read_proc, .tiocmget = rs_tiocmget, .tiocmset = rs_tiocmset,};/* * The serial driver boot-time initialization code! */static int __init rs_init(void){ unsigned long flags; struct serial_state * state; if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_SERIAL)) return -ENODEV; serial_driver = alloc_tty_driver(1); if (!serial_driver) return -ENOMEM; /* * We request SERDAT and SERPER only, because the serial registers are * too spreaded over the custom register space */ if (!request_mem_region(CUSTOM_PHYSADDR+0x30, 4, "amiserial [Paula]")) return -EBUSY; IRQ_ports = NULL; show_serial_version(); /* Initialize the tty_driver structure */ serial_driver->owner = THIS_MODULE; serial_driver->driver_name = "amiserial"; serial_driver->name = "ttyS"; serial_driver->major = TTY_MAJOR; serial_driver->minor_start = 64; serial_driver->type = TTY_DRIVER_TYPE_SERIAL; serial_driver->subtype = SERIAL_TYPE_NORMAL; serial_driver->init_termios = tty_std_termios; serial_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; serial_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(serial_driver, &serial_ops); if (tty_register_driver(serial_driver)) panic("Couldn't register serial driver\n"); state = rs_table; state->magic = SSTATE_MAGIC; state->port = (int)&custom.serdatr; /* Just to give it a value */ state->line = 0; state->custom_divisor = 0; state->close_delay = 5*HZ/10; state->closing_wait = 30*HZ; state->icount.cts = state->icount.dsr = state->icount.rng = state->icount.dcd = 0; state->icount.rx = state->icount.tx = 0; state->icount.frame = state->icount.parity = 0; state->icount.overrun = state->icount.brk = 0; printk(KERN_INFO "ttyS%d is the amiga builtin serial port\n", state->line); /* Hardware set up */ state->baud_base = amiga_colorclock; state->xmit_fifo_size = 1; local_irq_save(flags); /* set ISRs, and then disable the rx interrupts */ request_irq(IRQ_AMIGA_TBE, ser_tx_int, 0, "serial TX", state); request_irq(IRQ_AMIGA_RBF, ser_rx_int, IRQF_DISABLED, "serial RX", state); /* turn off Rx and Tx interrupts */ custom.intena = IF_RBF | IF_TBE; mb(); /* clear any pending interrupt */ custom.intreq = IF_RBF | IF_TBE; mb(); local_irq_restore(flags); /* * set the appropriate directions for the modem control flags, * and clear RTS and DTR */ ciab.ddra |= (SER_DTR | SER_RTS); /* outputs */ ciab.ddra &= ~(SER_DCD | SER_CTS | SER_DSR); /* inputs */ return 0;}static __exit void rs_exit(void) { int error; struct async_struct *info = rs_table[0].info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ tasklet_kill(&info->tlet); if ((error = tty_unregister_driver(serial_driver))) printk("SERIAL: failed to unregister serial driver (%d)\n", error); put_tty_driver(serial_driver); if (info) { rs_table[0].info = NULL; kfree(info); } release_mem_region(CUSTOM_PHYSADDR+0x30, 4);}module_init(rs_init)module_exit(rs_exit)/* * ------------------------------------------------------------ * Serial console driver * ------------------------------------------------------------ */#ifdef CONFIG_SERIAL_CONSOLEstatic void amiga_serial_putc(char c){ custom.serdat = (unsigned char)c | 0x100; while (!(custom.serdatr & 0x2000)) barrier();}/* * Print a string to the serial port trying not to disturb * any possible real use of the port... * * The console must be locked when we get here. */static void serial_console_write(struct console *co, const char *s, unsigned count){ unsigned short intena = custom.intenar; custom.intena = IF_TBE; while (count--) { if (*s == '\n') amiga_serial_putc('\r'); amiga_serial_putc(*s++); } custom.intena = IF_SETCLR | (intena & IF_TBE);}static struct tty_driver *serial_console_device(struct console *c, int *index){ *index = 0; return serial_driver;}static struct console sercons = { .name = "ttyS", .write = serial_console_write, .device = serial_console_device, .flags = CON_PRINTBUFFER, .index = -1,};/* * Register console. */static int __init amiserial_console_init(void){ register_console(&sercons); return 0;}console_initcall(amiserial_console_init);#endifMODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?