📄 su.c
字号:
if (--info->xmit_cnt <= 0) break; } while (--count > 0); if (info->xmit_cnt < WAKEUP_CHARS) su_sched_event(info, RS_EVENT_WRITE_WAKEUP);#ifdef SERIAL_DEBUG_INTR printk("T%d...", info->xmit_cnt);#endif if (intr_done) *intr_done = 0; if (info->xmit_cnt <= 0) { info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); }}static voidcheck_modem_status(struct su_struct *info){ int status; struct async_icount *icount; status = serial_in(info, UART_MSR); if (status & UART_MSR_ANY_DELTA) { icount = &info->icount; /* update input line counters */ if (status & UART_MSR_TERI) icount->rng++; if (status & UART_MSR_DDSR) icount->dsr++; if (status & UART_MSR_DDCD) { icount->dcd++;#ifdef CONFIG_HARD_PPS if ((info->flags & ASYNC_HARDPPS_CD) && (status & UART_MSR_DCD)) hardpps();#endif } if (status & UART_MSR_DCTS) icount->cts++; wake_up_interruptible(&info->delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off");#endif if (status & UART_MSR_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 & UART_MSR_CTS) {#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start...");#endif info->tty->hw_stopped = 0; info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); su_sched_event(info, RS_EVENT_WRITE_WAKEUP); return; } } else { if (!(status & UART_MSR_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 kbd/mouse serial driver's interrupt routine */static voidsu_kbd_ms_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct su_struct *info = (struct su_struct *)dev_id; unsigned char status;#ifdef SERIAL_DEBUG_INTR printk("su_kbd_ms_interrupt(%s)...", __irq_itoa(irq));#endif if (!info) return; if (serial_in(info, UART_IIR) & UART_IIR_NO_INT) return; status = serial_inp(info, UART_LSR);#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if ((status & UART_LSR_DR) || (status & UART_LSR_BI)) receive_kbd_ms_chars(info, regs, (status & UART_LSR_BI) != 0);#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}/* * This is the serial driver's generic interrupt routine */static voidsu_serial_interrupt(int irq, void *dev_id, struct pt_regs * regs){ int status; struct su_struct *info; int pass_counter = 0;#ifdef SERIAL_DEBUG_INTR printk("su_serial_interrupt(%s)...", __irq_itoa(irq));#endif info = (struct su_struct *)dev_id; if (!info || !info->tty) {#ifdef SERIAL_DEBUG_INTR printk("strain\n");#endif return; } do { status = serial_inp(info, UART_LSR);#ifdef SERIAL_DEBUG_INTR printk("status = %x...", status);#endif if (status & UART_LSR_DR) receive_serial_chars(info, &status, regs); check_modem_status(info); if (status & UART_LSR_THRE) transmit_chars(info, 0); if (pass_counter++ > RS_ISR_PASS_LIMIT) {#ifdef SERIAL_DEBUG_INTR printk("rs loop break");#endif break; /* Prevent infinite loops */ } } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); info->last_active = jiffies;#ifdef SERIAL_DEBUG_INTR printk("end.\n");#endif}/* * ------------------------------------------------------------------- * 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 * su_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 su_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 su_struct *info = (struct su_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. * --------------------------------------------------------------- */static intstartup(struct su_struct *info){ unsigned long flags; int retval=0; unsigned long page; save_flags(flags); if (info->tty) { page = get_free_page(GFP_KERNEL); if (!page) return -ENOMEM; cli(); if (info->flags & ASYNC_INITIALIZED) { free_page(page); goto errout; } if (info->port == 0 || info->type == PORT_UNKNOWN) { 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; } cli();#ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d (irq %s)...", info->line, __irq_itoa(info->irq));#endif if (uart_config[info->type].flags & UART_STARTECH) { /* Wake up UART */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); serial_outp(info, UART_IER, 0); serial_outp(info, UART_EFR, 0); serial_outp(info, UART_LCR, 0); } if (info->type == PORT_16750) { /* Wake up UART */ serial_outp(info, UART_IER, 0); } /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ if (uart_config[info->type].flags & UART_CLEAR_FIFO) serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); /* * At this point there's no way the LSR could still be 0xFF; * if it is, then bail out, because there's likely no UART * here. */ if (serial_inp(info, UART_LSR) == 0xff) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); } else retval = -ENODEV; goto errout; } /* * Allocate the IRQ if necessary */ if (info->port_type != SU_PORT_PORT) { retval = request_irq(info->irq, su_kbd_ms_interrupt, SA_SHIRQ, info->name, info); } else { retval = request_irq(info->irq, su_serial_interrupt, SA_SHIRQ, info->name, info); } if (retval) { if (capable(CAP_SYS_ADMIN)) { if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); retval = 0; } goto errout; } /* * Clear the interrupt registers. */ (void) serial_inp(info, UART_RX); (void) serial_inp(info, UART_IIR); (void) serial_inp(info, UART_MSR); /* * Now, initialize the UART */ serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */ info->MCR = 0; if (info->tty && info->tty->termios->c_cflag & CBAUD) info->MCR = UART_MCR_DTR | UART_MCR_RTS; if (info->irq != 0) info->MCR |= UART_MCR_OUT2; serial_outp(info, UART_MCR, info->MCR); /* * Finally, enable interrupts */ info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; serial_outp(info, UART_IER, info->IER); /* enable interrupts */ /* * And clear the interrupt registers again for luck. */ (void)serial_inp(info, UART_LSR); (void)serial_inp(info, UART_RX); (void)serial_inp(info, UART_IIR); (void)serial_inp(info, UART_MSR); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * 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 su_struct *info){ unsigned long flags; if (!(info->flags & ASYNC_INITIALIZED)) return; 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); /* * Free the IRQ, if necessary */ free_irq(info->irq, info); if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ info->MCR &= ~UART_MCR_OUT2; /* disable break condition */ serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); serial_outp(info, UART_MCR, info->MCR); /* disable FIFO's */ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); /* read data port to reset things */ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); if (uart_config[info->type].flags & UART_STARTECH) { /* Arrange to enter sleep mode */ serial_outp(info, UART_LCR, 0xBF); serial_outp(info, UART_EFR, UART_EFR_ECB); serial_outp(info, UART_IER, UART_IERX_SLEEP); serial_outp(info, UART_LCR, 0); } if (info->type == PORT_16750) { /* Arrange to enter sleep mode */ serial_outp(info, UART_IER, UART_IERX_SLEEP); } info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);}static intsu_get_baud_rate(struct su_struct *info){ static struct tty_struct c_tty; static struct termios c_termios; if (info->tty) return tty_get_baud_rate(info->tty); memset(&c_tty, 0, sizeof(c_tty)); memset(&c_termios, 0, sizeof(c_termios)); c_tty.termios = &c_termios; c_termios.c_cflag = info->cflag; return tty_get_baud_rate(&c_tty);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static voidchange_speed(struct su_struct *info, struct termios *old_termios){ int quot = 0, baud; unsigned int cval, fcr = 0; int bits; unsigned long flags; if (info->port_type == SU_PORT_PORT) { if (!info->tty || !info->tty->termios) return; if (!info->port) return; info->cflag = info->tty->termios->c_cflag; } /* byte size and parity */ switch (info->cflag & CSIZE) { case CS5: cval = 0x00; bits = 7; break; case CS6: cval = 0x01; bits = 8; break; case CS7: cval = 0x02; bits = 9; break; case CS8: cval = 0x03; bits = 10; break; /* Never happens, but GCC is too dumb to figure it out */ default: cval = 0x00; bits = 7; break; } if (info->cflag & CSTOPB) { cval |= 0x04; bits++; } if (info->cflag & PARENB) { cval |= UART_LCR_PARITY; bits++; } if (!(info->cflag & PARODD)) cval |= UART_LCR_EPAR;#ifdef CMSPAR if (info->cflag & CMSPAR) cval |= UART_LCR_SPAR;#endif /* Determine divisor based on baud rate */ baud = su_get_baud_rate(info); if (baud == 38400 && ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) quot = info->custom_divisor; else { if (baud == 134) /* Special case since 134 is really 134.5 */ quot = (2 * info->baud_base / 269);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -