📄 mxser.c
字号:
ret = 0; return (ret);}static int mxser_chars_in_buffer(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; return (info->xmit_cnt);}static void mxser_flush_buffer(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; save_flags(flags); cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty);}static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ unsigned long flags; struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; int retval; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ unsigned long templ; if (PORTNO(tty) == MXSER_PORTS) return (mxser_ioctl_special(cmd, arg)); if ((cmd != TIOCGSERIAL) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return (-EIO); } switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return (retval); tty_wait_until_sent(tty, 0); if (!arg) mxser_send_break(info, HZ / 4); /* 1/4 second */ return (0); case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return (retval); tty_wait_until_sent(tty, 0); mxser_send_break(info, arg ? arg * (HZ / 10) : HZ / 4); return (0); case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); case TIOCSSOFTCAR: if(get_user(templ, (unsigned long *) arg)) return -EFAULT; arg = templ; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return (0); case TIOCMGET: return (mxser_get_modem_info(info, (unsigned int *) arg)); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return (mxser_set_modem_info(info, cmd, (unsigned int *) arg)); case TIOCGSERIAL: return (mxser_get_serial_info(info, (struct serial_struct *) arg)); case TIOCSSERIAL: return (mxser_set_serial_info(info, (struct serial_struct *) arg)); case TIOCSERGETLSR: /* Get line status register */ return (mxser_get_lsr_info(info, (unsigned int *) arg)); /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: save_flags(flags); cli(); cprev = info->icount; /* note the counters on entry */ restore_flags(flags); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return (-ERESTARTSYS); save_flags(flags); cli(); cnow = info->icount; /* atomic copy */ restore_flags(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return (-EIO); /* no change => error */ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { return (0); } cprev = cnow; } /* NOTREACHED */ /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ case TIOCGICOUNT: save_flags(flags); cli(); cnow = info->icount; restore_flags(flags); p_cuser = (struct serial_icounter_struct *) arg; if(put_user(cnow.cts, &p_cuser->cts)) return -EFAULT; if(put_user(cnow.dsr, &p_cuser->dsr)) return -EFAULT; if(put_user(cnow.rng, &p_cuser->rng)) return -EFAULT; return put_user(cnow.dcd, &p_cuser->dcd); case MOXA_HighSpeedOn: return put_user(info->baud_base != 115200 ? 1 : 0, (int *) arg); default: return (-ENOIOCTLCMD); } return (0);}static int mxser_ioctl_special(unsigned int cmd, unsigned long arg){ int i, result, status; switch (cmd) { case MOXA_GET_CONF: if(copy_to_user((struct mxser_hwconf *) arg, mxsercfg, sizeof(struct mxser_hwconf) * 4)) return -EFAULT; return 0; case MOXA_GET_MAJOR: if(copy_to_user((int *) arg, &ttymajor, sizeof(int))) return -EFAULT; return 0; case MOXA_GET_CUMAJOR: if(copy_to_user((int *) arg, &calloutmajor, sizeof(int))) return -EFAULT; return 0; case MOXA_CHKPORTENABLE: result = 0; for (i = 0; i < MXSER_PORTS; i++) { if (mxvar_table[i].base) result |= (1 << i); } return put_user(result, (unsigned long *) arg); case MOXA_GETDATACOUNT: if(copy_to_user((struct mxser_log *) arg, &mxvar_log, sizeof(mxvar_log))) return -EFAULT; return (0); case MOXA_GETMSTATUS: for (i = 0; i < MXSER_PORTS; i++) { GMStatus[i].ri = 0; if (!mxvar_table[i].base) { GMStatus[i].dcd = 0; GMStatus[i].dsr = 0; GMStatus[i].cts = 0; continue; } if (!mxvar_table[i].tty || !mxvar_table[i].tty->termios) GMStatus[i].cflag = mxvar_table[i].normal_termios.c_cflag; else GMStatus[i].cflag = mxvar_table[i].tty->termios->c_cflag; status = inb(mxvar_table[i].base + UART_MSR); if (status & 0x80 /*UART_MSR_DCD */ ) GMStatus[i].dcd = 1; else GMStatus[i].dcd = 0; if (status & 0x20 /*UART_MSR_DSR */ ) GMStatus[i].dsr = 1; else GMStatus[i].dsr = 0; if (status & 0x10 /*UART_MSR_CTS */ ) GMStatus[i].cts = 1; else GMStatus[i].cts = 0; } if(copy_to_user((struct mxser_mstatus *) arg, GMStatus, sizeof(struct mxser_mstatus) * MXSER_PORTS)) return -EFAULT; return 0; default: return (-ENOIOCTLCMD); } return (0);}/* * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. */static void mxser_throttle(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) { info->x_char = STOP_CHAR(tty); save_flags(flags); cli(); outb(info->IER, 0); info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); /* force Tx interrupt */ restore_flags(flags); } if (info->tty->termios->c_cflag & CRTSCTS) { info->MCR &= ~UART_MCR_RTS; save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); }}static void mxser_unthrottle(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else { info->x_char = START_CHAR(tty); save_flags(flags); cli(); outb(info->IER, 0); info->IER |= UART_IER_THRI; /* force Tx interrupt */ outb(info->IER, info->base + UART_IER); restore_flags(flags); } } if (info->tty->termios->c_cflag & CRTSCTS) { info->MCR |= UART_MCR_RTS; save_flags(flags); cli(); outb(info->MCR, info->base + UART_MCR); restore_flags(flags); }}static void mxser_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data;/* 8-2-99 by William if ( (tty->termios->c_cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag)) ) return; mxser_change_speed(info, old_termios); if ( (old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS) ) { tty->hw_stopped = 0; mxser_start(tty); } */ if ((tty->termios->c_cflag != old_termios->c_cflag) || (RELEVANT_IFLAG(tty->termios->c_iflag) != RELEVANT_IFLAG(old_termios->c_iflag))) { mxser_change_speed(info, old_termios); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; mxser_start(tty); } }/* Handle sw stopped */ if ((old_termios->c_iflag & IXON) && !(tty->termios->c_iflag & IXON)) { tty->stopped = 0; mxser_start(tty); }}/* * mxser_stop() and mxser_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. */static void mxser_stop(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; save_flags(flags); cli(); if (info->IER & UART_IER_THRI) { info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); } restore_flags(flags);}static void mxser_start(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; save_flags(flags); cli(); if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); } restore_flags(flags);}/* * This routine is called by tty_hangup() when a hangup is signaled. */void mxser_hangup(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; mxser_flush_buffer(tty); mxser_shutdown(info); info->event = 0; info->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); info->tty = 0; wake_up_interruptible(&info->open_wait);}/* * This is the serial driver's generic interrupt routine */static void mxser_interrupt(int irq, void *dev_id, struct pt_regs *regs){ int status, i; struct mxser_struct *info; struct mxser_struct *port; int max, irqbits, bits, msr; int pass_counter = 0; port = 0; for (i = 0; i < MXSER_BOARDS; i++) { if (dev_id == &(mxvar_table[i * MXSER_PORTS_PER_BOARD])) { port = dev_id; break; } } if (i == MXSER_BOARDS) return; if (port == 0) return; max = mxser_numports[mxsercfg[i].board_type - 1]; while (1) { irqbits = inb(port->vector) & port->vectormask; if (irqbits == port->vectormask) break; for (i = 0, bits = 1; i < max; i++, irqbits |= bits, bits <<= 1) { if (irqbits == port->vectormask) break; if (bits & irqbits) continue; info = port + i; if (!info->tty || (inb(info->base + UART_IIR) & UART_IIR_NO_INT)) continue; status = inb(info->base + UART_LSR) & info->read_status_mask; if (status & UART_LSR_DR) mxser_receive_chars(info, &status); msr = inb(info->base + UART_MSR); if (msr & UART_MSR_ANY_DELTA) mxser_check_modem_status(info, msr); if (status & UART_LSR_THRE) {/* 8-2-99 by William if ( info->x_char || (info->xmit_cnt > 0) ) */ mxser_transmit_chars(info); } } if (pass_counter++ > MXSER_ISR_PASS_LIMIT) {#if 0 printk("MOXA Smartio/Indusrtio family driver interrupt loop break\n");#endif break; /* Prevent infinite loops */ } }}static inline void mxser_receive_chars(struct mxser_struct *info, int *status){ struct tty_struct *tty = info->tty; unsigned char ch; int ignored = 0; int cnt = 0; do { ch = inb(info->base + UART_RX); if (*status & info->ignore_status_mask) { if (++ignored > 100) break; } else { if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; tty->flip.count++; if (*status & UART_LSR_SPECIAL) { if (*status & UART_LSR_BI) { *tty->flip.flag_buf_ptr++ = TTY_BREAK; if (info->flags & ASYNC_SAK) do_SAK(tty); } else if (*status & UART_LSR_PE) { *tty->flip.flag_buf_ptr++ = TTY_PARITY; } else if (*status & UART_LSR_FE) { *tty->flip.flag_buf_ptr++ = TTY_FRAME; } else if (*status & UART_LSR_OE) { *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; } else *tty->flip.flag_buf_ptr++ = 0; } else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; cnt++; } *status = inb(info->base + UART_LSR) & info->read_status_mask; } while (*status & UART_LSR_DR); mxvar_log.rxcnt[info->port] += cnt; queue_task(&tty->flip.tqueue, &tq_timer);}static inline void mxser_transmit_chars(struct mxser_struct *info){ int count, cnt; if (info->x_char) { outb(info->x_char, info->base + UART_TX); info->x_char = 0; mxvar_log.txcnt[info->port]++; return; } if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) { info->IER &= ~UART_IER_THRI; outb(info->IER, info->base + UART_IER); return; } cnt = info->xmit_cnt; count = info->xmit_fifo_size; do { outb(info->xmit_buf[info->xmit_tail++], info->base + UART_TX); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE - 1); if (--info->xmit_cnt <= 0) break; } while (--count > 0); mxvar_log.txcnt[info->port] += (cnt - info->xmit_cnt); if (info->xmit_cnt < WAKEUP_CHARS) { set_bit(MXSER_EVENT_TXLOW, &info->event); schedule_task(&info->tqueue); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -