📄 amiserial.c
字号:
* 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);#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", state->line, state->count);#endif 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)) rtsdtr_ctrl(SER_DTR|SER_RTS); restore_flags(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_CALLOUT_ACTIVE) && !(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(); } 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 = kmalloc(sizeof(struct async_struct), GFP_KERNEL); if (!info) { sstate->count--; return -ENOMEM; } memset(info, 0, sizeof(struct async_struct));#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; info->tqueue.routine = do_softint; info->tqueue.data = 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; unsigned long page; MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_PORTS)) { MOD_DEC_USE_COUNT; return -ENODEV; } retval = get_async_struct(line, &info); if (retval) { MOD_DEC_USE_COUNT; return retval; } tty->driver_data = info; info->tty = tty; if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV;#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->state->count);#endif info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (!tmp_buf) { page = get_free_page(GFP_KERNEL); if (!page) { return -ENOMEM; } if (tmp_buf) free_page(page); else tmp_buf = (unsigned char *) page; } /* * 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; } 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; change_speed(info, 0); } info->session = current->session; info->pgrp = current->pgrp;#ifdef SERIAL_DEBUG_OPEN printk("rs_open ttys%d successful...", info->line);#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 = 0; } save_flags(flags); cli(); status = ciab.pra; control = info ? info->MCR : status; restore_flags(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;}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 _INLINE_ void show_serial_version(void){ printk(KERN_INFO "%s version %s\n", serial_name, serial_version);}int register_serial(struct serial_struct *req);void unregister_serial(int line);/* * 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; /* * 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; init_bh(SERIAL_BH, do_serial_bh); IRQ_ports = NULL; show_serial_version(); /* Initialize the tty_driver structure */ memset(&serial_driver, 0, sizeof(struct tty_driver)); serial_driver.magic = TTY_DRIVER_MAGIC; serial_driver.driver_name = "amiserial"; serial_driver.name = "ttyS"; serial_driver.major = TTY_MAJOR; serial_driver.minor_start = 64; serial_driver.num = 1; 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; serial_driver.refcount = &serial_refcount; serial_driver.table = serial_table; serial_driver.termios = serial_termios; serial_driver.termios_locked = serial_termios_locked; serial_driver.open = rs_open; serial_driver.close = rs_close; serial_driver.write = rs_write; serial_driver.put_char = rs_put_char; serial_driver.flush_chars = rs_flush_chars; serial_driver.write_room = rs_write_room; serial_driver.chars_in_buffer = rs_chars_in_buffer; serial_driver.flush_buffer = rs_flush_buffer; serial_driver.ioctl = rs_ioctl; serial_driver.throttle = rs_throttle; serial_driver.unthrottle = rs_unthrottle; serial_driver.set_termios = rs_set_termios; serial_driver.stop = rs_stop; serial_driver.start = rs_start; serial_driver.hangup = rs_hangup; serial_driver.break_ctl = rs_break; serial_driver.send_xchar = rs_send_xchar; serial_driver.wait_until_sent = rs_wait_until_sent; serial_driver.read_proc = rs_read_proc; /* * The callout device is just like normal device except for * major number and the subtype code. */ callout_driver = serial_driver; callout_driver.name = "cua"; callout_driver.major = TTYAUX_MAJOR; callout_driver.subtype = SERIAL_TYPE_CALLOUT; callout_driver.read_proc = 0; callout_driver.proc_entry = 0; if (tty_register_driver(&serial_driver)) panic("Couldn't register serial driver\n"); if (tty_register_driver(&callout_driver)) panic("Couldn't register callout 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->callout_termios = callout_driver.init_termios; state->normal_termios = serial_driver.init_termios; 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; /* if(state->port && check_region(state->port,REGION_LENGTH(state))) continue; */ printk(KERN_INFO "ttyS%02d is the amiga builtin serial port\n", state->line); /* Hardware set up */ state->baud_base = amiga_colorclock; state->xmit_fifo_size = 1; save_flags (flags); cli(); /* 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, SA_INTERRUPT, "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(); restore_flags (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) { unsigned long flags; int e1, e2; struct async_struct *info; /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ save_flags(flags); cli(); remove_bh(SERIAL_BH); if ((e1 = tty_unregister_driver(&serial_driver))) printk("SERIAL: failed to unregister serial driver (%d)\n", e1); if ((e2 = tty_unregister_driver(&callout_driver))) printk("SERIAL: failed to unregister callout driver (%d)\n", e2); restore_flags(flags); info = rs_table[0].info; if (info) { rs_table[0].info = NULL; kfree(info); } if (tmp_buf) { free_page((unsigned long) tmp_buf); tmp_buf = NULL; } 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_lock must be held 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);}/* * Receive character from the serial port */static int serial_console_wait_key(struct console *co){ unsigned short intena = custom.intenar; int ch; custom.intena = IF_RBF; while (!(custom.intreqr & IF_RBF)) barrier(); ch = custom.serdatr & 0xff; custom.intreq = IF_RBF; custom.intena = IF_SETCLR | (intena & IF_RBF); return ch;}static kdev_t serial_console_device(struct console *c){ return MKDEV(TTY_MAJOR, 64);}static struct console sercons = { "ttyS", serial_console_write, NULL, serial_console_device, serial_console_wait_key, NULL, NULL, CON_PRINTBUFFER, -1, 0, NULL};/* * Register console. */void __init serial_console_init(void){ register_console(&sercons);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -