mxser.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,381 行 · 第 1/4 页
C
2,381 行
continue; } hwconf.pdev = NULL; if (mxser_initbrd(m, &hwconf) < 0) continue; mxser_getcfg(m, &hwconf); m++; } /* start finding PCI board here */#ifdef CONFIG_PCI { struct pci_dev *pdev = NULL; int n = (sizeof(mxser_pcibrds) / sizeof(mxser_pcibrds[0])) - 1; for (b = 0; b < n; b++) { while ((pdev = pci_find_device(mxser_pcibrds[b].vendor, mxser_pcibrds[b].device, pdev))) { if (pci_enable_device(pdev)) continue; hwconf.pdev = pdev; printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n", mxser_brdname[mxser_pcibrds[b].driver_data], pdev->bus->number, PCI_SLOT(pdev->devfn)); if (m >= MXSER_BOARDS) { printk("Too many Smartio family boards found (maximum %d),board not configured\n", MXSER_BOARDS); } else { retval = mxser_get_PCI_conf(pdev, mxser_pcibrds[b].driver_data, &hwconf); if (retval < 0) { if (retval == MXSER_ERR_IRQ) printk("Invalid interrupt number,board not configured\n"); else if (retval == MXSER_ERR_IRQ_CONFLIT) printk("Invalid interrupt number,board not configured\n"); else if (retval == MXSER_ERR_VECTOR) printk("Invalid interrupt vector,board not configured\n"); else if (retval == MXSER_ERR_IOADDR) printk("Invalid I/O address,board not configured\n"); continue; } if (mxser_initbrd(m, &hwconf) < 0) continue; mxser_getcfg(m, &hwconf); m++; } } } }#endif for (i = m; i < MXSER_BOARDS; i++) { mxsercfg[i].board_type = -1; } if (!tty_register_driver(mxvar_sdriver)) return 0; put_tty_driver(mxvar_sdriver); printk("Couldn't install MOXA Smartio family driver !\n"); for (i = 0; i < MXSER_BOARDS; i++) { if (mxsercfg[i].board_type == -1) continue; free_irq(mxsercfg[i].irq, &mxvar_table[i * MXSER_PORTS_PER_BOARD]); } return -1;}static void mxser_do_softint(void *private_){ struct mxser_struct *info = (struct mxser_struct *) private_; struct tty_struct *tty; tty = info->tty; if (tty) { if (test_and_clear_bit(MXSER_EVENT_TXLOW, &info->event)) { tty_wakeup(tty); wake_up_interruptible(&tty->write_wait); } if (test_and_clear_bit(MXSER_EVENT_HANGUP, &info->event)) { tty_hangup(tty); /* FIXME: module removal race here - AKPM */ } }}/* * 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 mxser_open(struct tty_struct *tty, struct file *filp){ struct mxser_struct *info; int retval, line; unsigned long page; line = PORTNO(tty); if (line == MXSER_PORTS) return (0); if ((line < 0) || (line > MXSER_PORTS)) return (-ENODEV); info = mxvar_table + line; if (!info->base) return (-ENODEV); info->count++; tty->driver_data = info; info->tty = tty; if (!mxvar_tmp_buf) { page = get_zeroed_page(GFP_KERNEL); if (!page) return (-ENOMEM); if (mxvar_tmp_buf) free_page(page); else mxvar_tmp_buf = (unsigned char *) page; } /* * Start up serial port */ retval = mxser_startup(info); if (retval) return (retval); return mxser_block_til_ready(tty, filp, info);}/* * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. */static void mxser_close(struct tty_struct *tty, struct file *filp){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; unsigned long timeout; if (PORTNO(tty) == MXSER_PORTS) return; if (!info) return; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { restore_flags(flags); return; } if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk("mxser_close: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { printk("mxser_close: bad serial port count for ttys%d: %d\n", info->port, info->count); info->count = 0; } if (info->count) { restore_flags(flags); return; } info->flags |= ASYNC_CLOSING; info->cflag = tty->termios->c_cflag; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ info->IER &= ~UART_IER_RLSI; /* by William info->read_status_mask &= ~UART_LSR_DR; */ if (info->flags & ASYNC_INITIALIZED) { outb(info->IER, info->base + UART_IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; while (!(inb(info->base + UART_LSR) & UART_LSR_TEMT)) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(5); if (time_after(jiffies, timeout)) break; } } mxser_shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = NULL; if (info->blocked_open) { if (info->close_delay) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); restore_flags(flags);}static int mxser_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count){ int c, total = 0; struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; if (!tty || !info->xmit_buf || !mxvar_tmp_buf) return (0); save_flags(flags); if (from_user) { down(&mxvar_tmp_buf_sem); while (1) { c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; c -= copy_from_user(mxvar_tmp_buf, buf, c); if (!c) { if (!total) total = -EFAULT; break; } cli(); c = min_t(int, c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, mxvar_tmp_buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; total += c; } up(&mxvar_tmp_buf_sem); } else { while (1) { cli(); c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) { restore_flags(flags); break; } memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE - 1); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; total += c; } } cli(); if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); } restore_flags(flags); return (total);}static void mxser_put_char(struct tty_struct *tty, unsigned char ch){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; if (!tty || !info->xmit_buf) return; save_flags(flags); cli(); if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { restore_flags(flags); return; } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE - 1; info->xmit_cnt++; /********************************************** why ??? *********** if ( !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI) ) { info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); } *****************************************************************/ restore_flags(flags);}static void mxser_flush_chars(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; unsigned long flags; if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; save_flags(flags); cli(); info->IER |= UART_IER_THRI; outb(info->IER, info->base + UART_IER); restore_flags(flags);}static int mxser_write_room(struct tty_struct *tty){ struct mxser_struct *info = (struct mxser_struct *) tty->driver_data; int ret; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) 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); tty_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 __user *p_cuser; unsigned long templ; void __user *argp = (void __user *)arg; 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 __user *)argp); case TIOCSSOFTCAR: if(get_user(templ, (unsigned long __user *) arg)) return -EFAULT; arg = templ; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return (0); case TIOCGSERIAL: return mxser_get_serial_info(info, argp); case TIOCSSERIAL: return mxser_set_serial_info(info, argp); case TIOCSERGETLSR: /* Get line status register */ return mxser_get_lsr_info(info, argp); /* * 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 = argp; 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 __user *)argp); default: return (-ENOIOCTLCMD); } return (0);}static int mxser_ioctl_special(unsigned int cmd, unsigned long arg){ int i, result, status; void __user *argp = (void __user *)arg; switch (cmd) { case MOXA_GET_CONF: if(copy_to_user(argp, mxsercfg, sizeof(struct mxser_hwconf) * 4)) return -EFAULT; return 0; case MOXA_GET_MAJOR: if(copy_to_user(argp, &ttymajor, sizeof(int))) return -EFAULT; return 0; case MOXA_GET_CUMAJOR: result = 0; if(copy_to_user(argp, &result, 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 __user *) argp); case MOXA_GETDATACOUNT: if (copy_to_user(argp, &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].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(argp, 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;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?