📄 vacserial.c
字号:
* Following two functions were proposed by Pavel Osipenko * to make VAC/VIC behaviour more regular. */static void intr_begin(struct async_struct* info) { serial_outw(info, VAC_UART_INT_MASK, 0);}static void intr_end(struct async_struct* info){ vac_outw(VAC_INT_CTRL_UART_DISABLE(info), VAC_INT_CTRL); vac_outw(VAC_INT_CTRL_UART_ENABLE, VAC_INT_CTRL); serial_outw(info, VAC_UART_INT_MASK, info->IER);}/* * This is the serial driver's generic interrupt routine */static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){ int status; struct async_struct * info; int pass_counter = 0; struct async_struct *end_mark = 0;#ifdef SERIAL_DEBUG_INTR baget_printk("rs_interrupt(%d)...", irq);#endif info = IRQ_ports[irq]; if (!info) return; do { intr_begin(info); /* Mark we begin port handling */ if (!info->tty || (serial_inw (info, VAC_UART_INT_STATUS) & VAC_UART_STATUS_INTS) == 0) { if (!end_mark) end_mark = info; goto next; } end_mark = 0; info->last_active = jiffies; status = serial_inw(info, VAC_UART_INT_STATUS);#ifdef SERIAL_DEBUG_INTR baget_printk("status = %x...", status);#endif if (status & VAC_UART_STATUS_RX_READY) { receive_chars(info, &status); } check_modem_status(info); if (status & VAC_UART_STATUS_TX_EMPTY) transmit_chars(info, 0); next: intr_end(info); /* Mark this port handled */ info = info->next_port; if (!info) { info = IRQ_ports[irq]; if (pass_counter++ > RS_ISR_PASS_LIMIT) { break; /* Prevent infinite loops */ } continue; } } while (end_mark != info);#ifdef SERIAL_DEBUG_INTR baget_printk("end.\n");#endif}#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ *//* The original driver was simplified here: two functions were joined to reduce code */#define rs_interrupt_single rs_interrupt/* * ------------------------------------------------------------------- * Here ends 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 void do_serial_bh(void){ run_task_queue(&tq_serial);}static void do_softint(void *private_){ struct async_struct *info = (struct 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); }}/* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to * figure out the appropriate timeout for an interrupt chain, routines * to initialize and startup a serial port, and routines to shutdown a * serial port. Useful stuff like that. * --------------------------------------------------------------- *//* * This routine figures out the correct timeout for a particular IRQ. * It uses the smallest timeout of all of the serial ports in a * particular interrupt chain. Now only used for IRQ 0.... */static void figure_IRQ_timeout(int irq){ struct async_struct *info; int timeout = 60*HZ; /* 60 seconds === a long time :-) */ info = IRQ_ports[irq]; if (!info) { IRQ_timeout[irq] = 60*HZ; return; } while (info) { if (info->timeout < timeout) timeout = info->timeout; info = info->next_port; } if (!irq) timeout = timeout / 2; IRQ_timeout[irq] = timeout ? timeout : 1;}static int startup(struct async_struct * info){ unsigned long flags; int retval=0; void (*handler)(int, void *, struct pt_regs *); struct serial_state *state= info->state; unsigned long page; page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; save_flags(flags); cli(); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (!state->port || !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 baget_printk("starting up ttys%d (irq %d)...", info->line, state->irq);#endif if (uart_config[info->state->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(info, VAC_UART_MODE, 0); serial_outp(info, VAC_UART_INT_MASK, 0); } /* * Allocate the IRQ if necessary */ if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) {#ifdef CONFIG_SERIAL_SHARE_IRQ free_irq(state->irq, NULL); handler = rs_interrupt;#else retval = -EBUSY; goto errout;#endif /* CONFIG_SERIAL_SHARE_IRQ */ } else handler = rs_interrupt_single; retval = request_irq(state->irq, handler, IRQ_T(state), "serial", NULL); if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto errout; } } /* * Insert serial port into IRQ chain. */ info->prev_port = 0; info->next_port = IRQ_ports[state->irq]; if (info->next_port) info->next_port->prev_port = info; IRQ_ports[state->irq] = info; figure_IRQ_timeout(state->irq); /* * Clear the interrupt registers. */ /* (void) serial_inw(info, VAC_UART_INT_STATUS); */ /* (see above) */ (void) serial_inw(info, VAC_UART_RX); /* * Now, initialize the UART */ serial_outp(info, VAC_UART_MODE, VAC_UART_MODE_INITIAL); /*reset DLAB*/ /* * Finally, enable interrupts */ info->IER = VAC_UART_INT_RX_BREAK_CHANGE | VAC_UART_INT_RX_ERRS | \ VAC_UART_INT_RX_READY; serial_outp(info, VAC_UART_INT_MASK, info->IER); /*enable interrupts*/ /* * And clear the interrupt registers again for luck. */ (void)serial_inp(info, VAC_UART_INT_STATUS); (void)serial_inp(info, VAC_UART_RX); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * Set up serial timers... */ mod_timer(&vacs_timer, jiffies + 2*HZ/100); /* * and set the speed of the serial port */ change_speed(info); 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 void shutdown(struct async_struct * info){ unsigned long flags; struct serial_state *state; int retval; if (!(info->flags & ASYNC_INITIALIZED)) return; state = info->state;#ifdef SERIAL_DEBUG_OPEN baget_printk("Shutting down serial port %d (irq %d)....", 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); /* * First unlink the serial port from the IRQ chain... */ if (info->next_port) info->next_port->prev_port = info->prev_port; if (info->prev_port) info->prev_port->next_port = info->next_port; else IRQ_ports[state->irq] = info->next_port; figure_IRQ_timeout(state->irq); /* * Free the IRQ, if necessary */ if (state->irq && (!IRQ_ports[state->irq] || !IRQ_ports[state->irq]->next_port)) { if (IRQ_ports[state->irq]) { free_irq(state->irq, NULL); retval = request_irq(state->irq, rs_interrupt_single, IRQ_T(state), "serial", NULL); if (retval) printk("serial shutdown: request_irq: error %d" " Couldn't reacquire IRQ.\n", retval); } else free_irq(state->irq, NULL); } if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } info->IER = 0; serial_outp(info, VAC_UART_INT_MASK, 0x00); /* disable all intrs */ /* disable break condition */ serial_out(info, VAC_UART_MODE, serial_inp(info, VAC_UART_MODE) & \ ~VAC_UART_MODE_SEND_BREAK); if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}/* * When we set line mode, we call this function * for Baget-specific adjustments. */static inline unsigned short vac_uart_mode_fixup (unsigned short cval){#ifdef QUAD_UART_SPEED /* * When we are using 4-x advantage in speed: * * Disadvantage : can't support 75, 150 bauds * Advantage : can support 19200, 38400 bauds */ char speed = 7 & (cval >> 10); cval &= ~(7 << 10); cval |= VAC_UART_MODE_BAUD(speed-2);#endif /* * In general, we have Tx and Rx ON all time * and use int mask flag for their disabling. */ cval |= VAC_UART_MODE_RX_ENABLE; cval |= VAC_UART_MODE_TX_ENABLE; cval |= VAC_UART_MODE_CHAR_RX_ENABLE; cval |= VAC_UART_MODE_CHAR_TX_ENABLE; /* Low 4 bits are not used in UART */ cval &= ~0xf; return cval; }/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(struct async_struct *info){ unsigned short port; int quot = 0, baud_base, baud; unsigned cflag, cval; int bits; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; /* byte size and parity */ switch (cflag & CSIZE) { case CS7: cval = 0x0; bits = 9; break; case CS8: cval = VAC_UART_MODE_8BIT_CHAR; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ case CS5: case CS6: default: cval = 0x0; bits = 9; break; } cval &= ~VAC_UART_MODE_PARITY_ENABLE; if (cflag & PARENB) { cval |= VAC_UART_MODE_PARITY_ENABLE; bits++; } if (cflag & PARODD) cval |= VAC_UART_MODE_PARITY_ODD; /* Determine divisor based on baud rate */ baud = tty_get_baud_rate(info->tty); if (!baud) baud = 9600; /* B0 transition handled in rs_set_termios */ baud_base = info->state->baud_base; if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->state->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2*baud_base / 269); else if (baud) quot = baud_base / baud; } /* If the quotient is ever 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 */ serial_out(info, VAC_UART_INT_MASK, info->IER); /* * Set up parity check flag */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = VAC_UART_STATUS_RX_ERR_OVERRUN | \ VAC_UART_STATUS_TX_EMPTY | VAC_UART_STATUS_RX_READY; if (I_INPCK(info->tty)) info->read_status_mask |= VAC_UART_STATUS_RX_ERR_FRAME | \ VAC_UART_STATUS_RX_ERR_PARITY; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= VAC_UART_STATUS_RX_BREAK_CHANGE; /* * Characters to ignore */ info->ignore_status_mask = 0; if (I_IGNPAR(info->tty)) info->ignore_status_mask |= VAC_UART_STATUS_RX_ERR_PARITY | \ VAC_UART_STATUS_RX_ERR_FRAME; if (I_IGNBRK(info->tty)) { info->ignore_status_mask |= VAC_UART_STATUS_RX_BREAK_CHANGE; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ if (I_IGNPAR(info->tty)) info->ignore_status_mask |= \ VAC_UART_STATUS_RX_ERR_OVERRUN; } /* * !!! ignore all characters if CREAD is not set */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -