specialix.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,265 行 · 第 1/4 页
C
2,265 行
port->MSVR |= MSVR_RTS; */ /* if (set & TIOCM_DTR) port->MSVR |= MSVR_DTR; */ if (SX_CRTSCTS(port->tty)) { if (set & TIOCM_RTS) port->MSVR |= MSVR_DTR; } else { if (set & TIOCM_DTR) port->MSVR |= MSVR_DTR; } /* if (clear & TIOCM_RTS) port->MSVR &= ~MSVR_RTS; */ /* if (clear & TIOCM_DTR) port->MSVR &= ~MSVR_DTR; */ if (SX_CRTSCTS(port->tty)) { if (clear & TIOCM_RTS) port->MSVR &= ~MSVR_DTR; } else { if (clear & TIOCM_DTR) port->MSVR &= ~MSVR_DTR; } sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_MSVR, port->MSVR); restore_flags(flags); return 0;}static inline void sx_send_break(struct specialix_port * port, unsigned long length){ struct specialix_board *bp = port_Board(port); unsigned long flags; save_flags(flags); cli(); port->break_length = SPECIALIX_TPS / HZ * length; port->COR2 |= COR2_ETC; port->IER |= IER_TXRDY; sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_COR2, port->COR2); sx_out(bp, CD186x_IER, port->IER); sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_CORCHG2); sx_wait_CCR(bp); restore_flags(flags);}static inline int sx_set_serial_info(struct specialix_port * port, struct serial_struct __user * newinfo){ struct serial_struct tmp; struct specialix_board *bp = port_Board(port); int change_speed; unsigned long flags; if (copy_from_user(&tmp, newinfo, sizeof(tmp))) return -EFAULT; #if 0 if ((tmp.irq != bp->irq) || (tmp.port != bp->base) || (tmp.type != PORT_CIRRUS) || (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) || (tmp.custom_divisor != 0) || (tmp.xmit_fifo_size != CD186x_NFIFO) || (tmp.flags & ~SPECIALIX_LEGAL_FLAGS)) return -EINVAL;#endif change_speed = ((port->flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); change_speed |= (tmp.custom_divisor != port->custom_divisor); if (!capable(CAP_SYS_ADMIN)) { if ((tmp.close_delay != port->close_delay) || (tmp.closing_wait != port->closing_wait) || ((tmp.flags & ~ASYNC_USR_MASK) != (port->flags & ~ASYNC_USR_MASK))) return -EPERM; port->flags = ((port->flags & ~ASYNC_USR_MASK) | (tmp.flags & ASYNC_USR_MASK)); port->custom_divisor = tmp.custom_divisor; } else { port->flags = ((port->flags & ~ASYNC_FLAGS) | (tmp.flags & ASYNC_FLAGS)); port->close_delay = tmp.close_delay; port->closing_wait = tmp.closing_wait; port->custom_divisor = tmp.custom_divisor; } if (change_speed) { save_flags(flags); cli(); sx_change_speed(bp, port); restore_flags(flags); } return 0;}static inline int sx_get_serial_info(struct specialix_port * port, struct serial_struct __user *retinfo){ struct serial_struct tmp; struct specialix_board *bp = port_Board(port); memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_CIRRUS; tmp.line = port - sx_port; tmp.port = bp->base; tmp.irq = bp->irq; tmp.flags = port->flags; tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC; tmp.close_delay = port->close_delay * HZ/100; tmp.closing_wait = port->closing_wait * HZ/100; tmp.custom_divisor = port->custom_divisor; tmp.xmit_fifo_size = CD186x_NFIFO; if (copy_to_user(retinfo, &tmp, sizeof(tmp))) return -EFAULT; return 0;}static int sx_ioctl(struct tty_struct * tty, struct file * filp, unsigned int cmd, unsigned long arg){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; int retval; void __user *argp = (void __user *)arg; if (sx_paranoia_check(port, tty->name, "sx_ioctl")) return -ENODEV; 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) sx_send_break(port, 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); sx_send_break(port, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)argp)) return -EFAULT; return 0; case TIOCSSOFTCAR: if (get_user(arg, (unsigned long __user *) argp)) return -EFAULT; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCGSERIAL: return sx_get_serial_info(port, argp); case TIOCSSERIAL: return sx_set_serial_info(port, argp); default: return -ENOIOCTLCMD; } return 0;}static void sx_throttle(struct tty_struct * tty){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; unsigned long flags; if (sx_paranoia_check(port, tty->name, "sx_throttle")) return; bp = port_Board(port); save_flags(flags); cli(); /* Use DTR instead of RTS ! */ if (SX_CRTSCTS (tty)) port->MSVR &= ~MSVR_DTR; else { /* Auch!!! I think the system shouldn't call this then. */ /* Or maybe we're supposed (allowed?) to do our side of hw handshake anyway, even when hardware handshake is off. When you see this in your logs, please report.... */ printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n", port_No (port)); } sx_out(bp, CD186x_CAR, port_No(port)); if (I_IXOFF(tty)) { sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_SSCH2); sx_wait_CCR(bp); } sx_out(bp, CD186x_MSVR, port->MSVR); restore_flags(flags);}static void sx_unthrottle(struct tty_struct * tty){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; unsigned long flags; if (sx_paranoia_check(port, tty->name, "sx_unthrottle")) return; bp = port_Board(port); save_flags(flags); cli(); /* XXXX Use DTR INSTEAD???? */ if (SX_CRTSCTS(tty)) { port->MSVR |= MSVR_DTR; } /* Else clause: see remark in "sx_throttle"... */ sx_out(bp, CD186x_CAR, port_No(port)); if (I_IXOFF(tty)) { sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_SSCH1); sx_wait_CCR(bp); } sx_out(bp, CD186x_MSVR, port->MSVR); restore_flags(flags);}static void sx_stop(struct tty_struct * tty){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; unsigned long flags; if (sx_paranoia_check(port, tty->name, "sx_stop")) return; bp = port_Board(port); save_flags(flags); cli(); port->IER &= ~IER_TXRDY; sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_IER, port->IER); restore_flags(flags);}static void sx_start(struct tty_struct * tty){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; unsigned long flags; if (sx_paranoia_check(port, tty->name, "sx_start")) return; bp = port_Board(port); save_flags(flags); cli(); if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) { port->IER |= IER_TXRDY; sx_out(bp, CD186x_CAR, port_No(port)); sx_out(bp, CD186x_IER, port->IER); } restore_flags(flags);}/* * This routine is called from the work-queue when the interrupt * routine has signalled that a hangup has occurred. The path of * hangup processing is: * * serial interrupt routine -> (workqueue) -> * do_sx_hangup() -> tty->hangup() -> sx_hangup() * */static void do_sx_hangup(void *private_){ struct specialix_port *port = (struct specialix_port *) private_; struct tty_struct *tty; tty = port->tty; if (tty) tty_hangup(tty); /* FIXME: module removal race here */}static void sx_hangup(struct tty_struct * tty){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; struct specialix_board *bp; if (sx_paranoia_check(port, tty->name, "sx_hangup")) return; bp = port_Board(port); sx_shutdown_port(bp, port); port->event = 0; port->count = 0; port->flags &= ~ASYNC_NORMAL_ACTIVE; port->tty = NULL; wake_up_interruptible(&port->open_wait);}static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios){ struct specialix_port *port = (struct specialix_port *)tty->driver_data; unsigned long flags; if (sx_paranoia_check(port, tty->name, "sx_set_termios")) return; if (tty->termios->c_cflag == old_termios->c_cflag && tty->termios->c_iflag == old_termios->c_iflag) return; save_flags(flags); cli(); sx_change_speed(port_Board(port), port); restore_flags(flags); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; sx_start(tty); }}static void do_softint(void *private_){ struct specialix_port *port = (struct specialix_port *) private_; struct tty_struct *tty; if(!(tty = port->tty)) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) tty_wakeup(tty);}static struct tty_operations sx_ops = { .open = sx_open, .close = sx_close, .write = sx_write, .put_char = sx_put_char, .flush_chars = sx_flush_chars, .write_room = sx_write_room, .chars_in_buffer = sx_chars_in_buffer, .flush_buffer = sx_flush_buffer, .ioctl = sx_ioctl, .throttle = sx_throttle, .unthrottle = sx_unthrottle, .set_termios = sx_set_termios, .stop = sx_stop, .start = sx_start, .hangup = sx_hangup, .tiocmget = sx_tiocmget, .tiocmset = sx_tiocmset,};static int sx_init_drivers(void){ int error; int i; specialix_driver = alloc_tty_driver(SX_NBOARD * SX_NPORT); if (!specialix_driver) { printk(KERN_ERR "sx: Couldn't allocate tty_driver.\n"); return 1; } if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) { printk(KERN_ERR "sx: Couldn't get free page.\n"); put_tty_driver(specialix_driver); return 1; } specialix_driver->owner = THIS_MODULE; specialix_driver->name = "ttyW"; specialix_driver->major = SPECIALIX_NORMAL_MAJOR; specialix_driver->type = TTY_DRIVER_TYPE_SERIAL; specialix_driver->subtype = SERIAL_TYPE_NORMAL; specialix_driver->init_termios = tty_std_termios; specialix_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; specialix_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(specialix_driver, &sx_ops); if ((error = tty_register_driver(specialix_driver))) { put_tty_driver(specialix_driver); free_page((unsigned long)tmp_buf); printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n", error); return 1; } memset(sx_port, 0, sizeof(sx_port)); for (i = 0; i < SX_NPORT * SX_NBOARD; i++) { sx_port[i].magic = SPECIALIX_MAGIC; INIT_WORK(&sx_port[i].tqueue, do_softint, &sx_port[i]); INIT_WORK(&sx_port[i].tqueue_hangup, do_sx_hangup, &sx_port[i]); sx_port[i].close_delay = 50 * HZ/100; sx_port[i].closing_wait = 3000 * HZ/100; init_waitqueue_head(&sx_port[i].open_wait); init_waitqueue_head(&sx_port[i].close_wait); } return 0;}static void sx_release_drivers(void){ free_page((unsigned long)tmp_buf); tty_unregister_driver(specialix_driver); put_tty_driver(specialix_driver);}#ifndef MODULE/* * Called at boot time. * * You can specify IO base for up to SX_NBOARD cards, * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt. * Note that there will be no probing at default * addresses in this case. * */ void specialix_setup(char *str, int * ints){ int i; for (i=0;i<SX_NBOARD;i++) { sx_board[i].base = 0; } for (i = 1; i <= ints[0]; i++) { if (i&1) sx_board[i/2].base = ints[i]; else sx_board[i/2 -1].irq = ints[i]; }}#endif/* * This routine must be called by kernel at boot time */static int __init specialix_init(void){ int i; int found = 0; printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997/1998.\n"); printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");#ifdef CONFIG_SPECIALIX_RTSCTS printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");#else printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");#endif if (sx_init_drivers()) return -EIO; for (i = 0; i < SX_NBOARD; i++) if (sx_board[i].base && !sx_probe(&sx_board[i])) found++;#ifdef CONFIG_PCI { struct pci_dev *pdev = NULL; i=0; while (i < SX_NBOARD) { if (sx_board[i].flags & SX_BOARD_PRESENT) { i++; continue; } pdev = pci_find_device (PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8, pdev); if (!pdev) break; if (pci_enable_device(pdev)) continue; sx_board[i].irq = pdev->irq; sx_board[i].base = pci_resource_start (pdev, 2); sx_board[i].flags |= SX_BOARD_IS_PCI; if (!sx_probe(&sx_board[i])) found ++; } }#endif if (!found) { sx_release_drivers(); printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n"); return -EIO; } return 0;}int iobase[SX_NBOARD] = {0,};int irq [SX_NBOARD] = {0,};MODULE_PARM(iobase,"1-" __MODULE_STRING(SX_NBOARD) "i");MODULE_PARM(irq,"1-" __MODULE_STRING(SX_NBOARD) "i");/* * You can setup up to 4 boards. * by specifying "iobase=0xXXX,0xXXX ..." as insmod parameter. * You should specify the IRQs too in that case "irq=....,...". * * More than 4 boards in one computer is not possible, as the card can * only use 4 different interrupts. * */static int __init specialix_init_module(void){ int i; if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) { for(i = 0; i < SX_NBOARD; i++) { sx_board[i].base = iobase[i]; sx_board[i].irq = irq[i]; } } return specialix_init();} static void __exit specialix_exit_module(void){ int i; sx_release_drivers(); for (i = 0; i < SX_NBOARD; i++) if (sx_board[i].flags & SX_BOARD_PRESENT) sx_release_io_range(&sx_board[i]);#ifdef SPECIALIX_TIMER del_timer (&missed_irq_timer);#endif }module_init(specialix_init_module);module_exit(specialix_exit_module);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?