📄 serial_netarm.c
字号:
tty->flip.flag_buf_ptr++; tty->flip.char_buf_ptr++; fifo_char++ ; icount->rx++; }#if defined(CONFIG_SERIAL_NETARM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ignore_char:#endif /* read status anew */ *status = regs->status_a ; } while ((*status & NETARM_SER_STATA_RX_RDY) && (tty->flip.count < TTY_FLIPBUF_SIZE)); tty_flip_buffer_push(tty);}static _INLINE_ voidcheck_modem_status(struct netarm_async_struct *info){ int status; struct async_icount *icount; status = info->SCSRA; if (status & (NETARM_SER_STATA_RI|NETARM_SER_STATA_DSR |NETARM_SER_STATA_DCD|NETARM_SER_STATA_CTS)) { icount = &info->state->icount; /* update input line counters */ if (status & NETARM_SER_STATA_RI) icount->rng++; if (status & NETARM_SER_STATA_DSR) icount->dsr++; if (status & NETARM_SER_STATA_DCD) icount->dcd++; if (status & NETARM_SER_STATA_CTS) icount->cts++; wake_up_interruptible(&info->delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (status & NETARM_SER_STATA_DCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & NETARM_SER_STATA_DCD) ? "on" : "off");#endif if (status & NETARM_SER_STATA_DCD) wake_up_interruptible(&info->open_wait); else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_CALLOUT_NOHUP))) {#ifdef SERIAL_DEBUG_OPEN printk("doing serial hangup...");#endif if (info->tty) tty_hangup(info->tty); } } if (info->flags & ASYNC_CTS_FLOW) { if (info->tty->hw_stopped) { if (status & NETARM_SER_STATA_CTS) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start...");#endif info->tty->hw_stopped = 0; /* FIXME: do we need rs_sched_event() here? info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);*/ return; } } else { if (!(status & NETARM_SER_STATA_CTS)) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop...");#endif info->tty->hw_stopped = 1; /* info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER);*/ } } }}/* * ------------------------------------------------------------------- * This is the serial driver's interrupt routine for a single port * ------------------------------------------------------------------- */static _INLINE_ voidnas_rx_interrupt(struct netarm_async_struct *info, struct pt_regs *regs){ int status; int pass_counter = 0; if (!info || !info->tty) return; do { status = info->SCSRA;#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if (status & (NETARM_SER_STATA_RX_RDY |NETARM_SER_STATA_RX_HALF |NETARM_SER_STATA_RX_FULL)) receive_chars(info, &status, regs);/* FIXME: do we need check_modem_status() if we don't want to transmit anything? check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0);*/ if (pass_counter++ > RS_ISR_PASS_LIMIT) {#if 0 printk("rs_single loop break.\n");#endif break; }#ifdef SERIAL_DEBUG_INTR printk("STAT_A = %x...", info->registers->status_a);#endif } while (NETARM_SER_STATA_RX_RDY & (info->SCSRA = info->registers->status_a)); info->last_active = jiffies;#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}static voidnas_rx_interrupt_1(int irq, void *dev_id, struct pt_regs *regs){#if (defined(SERIAL_DEBUG_INTR) && defined(NAS_DEBUG_VERBOSE)) printk(" nas_rx_interrupt_1: int!\n");#endif nas_rx_interrupt(&NAS_ports[0], regs) ;}static voidnas_rx_interrupt_2(int irq, void *dev_id, struct pt_regs *regs){#if (defined(SERIAL_DEBUG_INTR) && defined(NAS_DEBUG_VERBOSE)) printk(" nas_rx_interrupt_2: int!\n");#endif nas_rx_interrupt(&NAS_ports[1], regs) ;}/* * ------------------------------------------------------------------- * Here end the serial interrupt routines * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static voiddo_serial_bh(void){ run_task_queue(&tq_serial);}static voiddo_softint(void *private_){ struct netarm_async_struct *info = (struct netarm_async_struct *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);#ifdef SERIAL_HAVE_POLL_WAIT wake_up_interruptible(&tty->poll_wait);#endif }}/* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines * to initialize and startup a serial port, and routines to shutdown a * serial port. Useful stuff like that. * --------------------------------------------------------------- */static int nas_validate_baud(int baud){ if (baud >= MIN_BAUD_RATE && baud <= MAX_BAUD_RATE) return 0; return 1;}/* * This is used to figure out the divisor speeds and the timeouts */static int nas_baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0 };static int nas_n_baud_table = sizeof(nas_baud_table)/sizeof(int);/* nas_tty_get_baud_rate is borrowed from 2.2.12 tty_io.c *//* since the netarm serial driver is based upon the 2.2.x source *//* rather than the 2.0.X. The reasons for this are purely historical */intnas_tty_get_baud_rate(struct tty_struct *tty){ unsigned int cflag, i; cflag = tty->termios->c_cflag; i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i+15 >= nas_n_baud_table) tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } return nas_baud_table[i];}static intstartup(struct netarm_async_struct * info){ unsigned long flags; int baud, retval=0; struct serial_state *state = info->state; unsigned long page; volatile netarm_serial_channel_t *regs; /* check the baud rate */ baud = nas_tty_get_baud_rate(info->tty); if (nas_validate_baud(baud) != 0) { printk("nas_startup_port: invalid baud rate from tty struct: %d\n", baud); return -EINVAL; } info->baud = baud; page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; save_flags(flags); cli(); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); free_page(page); goto errout; } if (info->xmit.buf) free_page(page); else info->xmit.buf = (unsigned char *) page;#ifdef SERIAL_DEBUG_OPEN printk("Starting up ttyS%d (irq %d) at %d baud...\n", info->line, state->irq, info->baud);#endif /* Wake up and initialize SER module */ regs = info->registers; regs->rx_match = 0; regs->rx_buf_timer = 0; regs->bitrate = NETARM_SER_BR_X16(baud); regs->rx_char_timer = NETARM_SER_RXGAP(baud); regs->ctrl_b = NETARM_SER_CTLB_RCGT_EN | NETARM_SER_CTLB_UART_MODE;#ifdef POLLED_SERIAL regs->ctrl_a |= NETARM_SER_CTLA_ENABLE ;#else /* enable interrupts */ regs->ctrl_a |= NETARM_SER_CTLA_ENABLE | NETARM_SER_CTLA_IE_RX_RDY | NETARM_SER_CTLA_IE_RX_FULL ;#endif /* * Set up the tty->alt_speed kludge */ if (info->tty) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) info->tty->alt_speed = 57600; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) info->tty->alt_speed = 115200; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) info->tty->alt_speed = 230400; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) info->tty->alt_speed = 460800; } /* * and set the speed of the serial port */ change_speed(info, 0); info->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0;errout: restore_flags(flags); return retval;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static voidshutdown(struct netarm_async_struct * info){ unsigned long flags; struct serial_state *state; volatile netarm_serial_channel_t *regs; if (!(info->flags & ASYNC_INITIALIZED)) return; state = info->state; regs = info->registers;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)...\n", info->line, state->irq);#endif save_flags(flags); cli(); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ wake_up_interruptible(&info->delta_msr_wait); if (info->xmit.buf) { unsigned long pg = (unsigned long) info->xmit.buf; info->xmit.buf = 0; free_page(pg); } /* turn off the port */ regs->ctrl_a &= ~NETARM_SER_CTLA_ENABLE ; if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}/* * This routine is called to set the specified baud rate for a serial port. */static voidchange_speed(struct netarm_async_struct *info, struct termios *old_termios){ volatile netarm_serial_channel_t *regs; int quot = 0, baud_base, baud; unsigned cflag, cval; int bits; unsigned long flags; regs = info->registers; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!CONFIGURED_NAS_PORT(info)) return; /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = NETARM_SER_CTLA_5BITS; bits = 7; break; case CS6: cval = NETARM_SER_CTLA_6BITS; bits = 8; break; case CS7: cval = NETARM_SER_CTLA_7BITS; bits = 9; break; case CS8: cval = NETARM_SER_CTLA_8BITS; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ default: cval = NETARM_SER_CTLA_5BITS; bits = 7; break; } if (cflag & CSTOPB) { cval |= NETARM_SER_CTLA_2STOP; bits++; } if (cflag & PARENB) { cval |= NETARM_SER_CTLA_P_ODD; bits++; if (!(cflag & PARODD)) cval |= NETARM_SER_CTLA_P_EVEN; } /* Determine divisor based on baud rate */ baud = nas_tty_get_baud_rate(info->tty); if (!baud) baud = 9600;#ifdef SERIAL_DEBUG_OPEN printk("change_speed: cflag %d, baud rate %d\n", cflag, baud);#endif baud_base = info->state->baud_base; quot = baud_base / baud; /* As a last resort, if the quotient is zero, default to 9600 bps */ if (!quot) quot = baud_base / 9600; info->quot = quot; info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); info->timeout += HZ/50; /* Add .02 seconds of slop */ /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) cval |= NETARM_SER_CTLA_CTSTX | NETARM_SER_CTLA_RTSRX; /* * Set up parity check flag */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) save_flags(flags); cli(); regs = info->registers; regs->rx_match = 0; regs->rx_buf_timer = 0; regs->bitrate = NETARM_SER_BR_X16(baud); regs->rx_char_timer = NETARM_SER_RXGAP(baud); regs->ctrl_b = NETARM_SER_CTLB_RCGT_EN | NETARM_SER_CTLB_UART_MODE;#ifdef POLLED_SERIAL regs->ctrl_a = NETARM_SER_CTLA_ENABLE | cval;#else /* enable interrupts */ regs->ctrl_a = NETARM_SER_CTLA_ENABLE | NETARM_SER_CTLA_IE_RX_RDY | NETARM_SER_CTLA_IE_RX_FULL | cval;#endif#ifdef SERIAL_DEBUG_OPEN printk("change_speed: bitrate %x, rxgap %x\n", regs->bitrate, regs->rx_char_timer );#endif restore_flags(flags);}/* Output a single character to the device */static voidrs_put_char(struct tty_struct *tty, unsigned char ch){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; volatile netarm_serial_channel_t *regs; unsigned char *fifo;#ifdef NAS_DEBUG_VERBOSE printk(" rs_put_char called\n");#endif regs = info->registers; fifo = (unsigned char *)&(regs->fifo); /* write the character (polled) */ NAS_TX_WAIT_RDY(regs); *fifo = ch;}/* not needed in polled output: */static voidrs_flush_chars(struct tty_struct *tty){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; serial_paranoia_check(info, tty->device, "rs_flush_chars");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -