📄 specialix.c
字号:
tmp.xmit_fifo_size = CD186x_NFIFO; copy_to_user(retinfo, &tmp, sizeof(tmp)); 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 error; int retval; if (sx_paranoia_check(port, tty->device, "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: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); if (error) return error; put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); return 0; case TIOCSSOFTCAR: Get_user(arg, (unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCMGET: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int)); if (error) return error; return sx_get_modem_info(port, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return sx_set_modem_info(port, cmd, (unsigned int *) arg); case TIOCGSERIAL: return sx_get_serial_info(port, (struct serial_struct *) arg); case TIOCSSERIAL: return sx_set_serial_info(port, (struct serial_struct *) arg); 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->device, "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->device, "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->device, "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->device, "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 scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred. The path of * hangup processing is: * * serial interrupt routine -> (scheduler tqueue) -> * 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) return; tty_hangup(tty);}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->device, "sx_hangup")) return; bp = port_Board(port); sx_shutdown_port(bp, port); port->event = 0; port->count = 0; port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); port->tty = 0; 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->device, "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_specialix_bh(void){ run_task_queue(&tq_specialix);}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)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); }}static int sx_init_drivers(void){ int error; int i; if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) { printk(KERN_ERR "sx: Couldn't get free page.\n"); return 1; } init_bh(SPECIALIX_BH, do_specialix_bh); memset(&specialix_driver, 0, sizeof(specialix_driver)); specialix_driver.magic = TTY_DRIVER_MAGIC; specialix_driver.name = "ttyW"; specialix_driver.major = SPECIALIX_NORMAL_MAJOR; specialix_driver.num = SX_NBOARD * SX_NPORT; specialix_driver.type = TTY_DRIVER_TYPE_SERIAL; specialix_driver.subtype = SPECIALIX_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; specialix_driver.refcount = &specialix_refcount; specialix_driver.table = specialix_table; specialix_driver.termios = specialix_termios; specialix_driver.termios_locked = specialix_termios_locked; specialix_driver.open = sx_open; specialix_driver.close = sx_close; specialix_driver.write = sx_write; specialix_driver.put_char = sx_put_char; specialix_driver.flush_chars = sx_flush_chars; specialix_driver.write_room = sx_write_room; specialix_driver.chars_in_buffer = sx_chars_in_buffer; specialix_driver.flush_buffer = sx_flush_buffer; specialix_driver.ioctl = sx_ioctl; specialix_driver.throttle = sx_throttle; specialix_driver.unthrottle = sx_unthrottle; specialix_driver.set_termios = sx_set_termios; specialix_driver.stop = sx_stop; specialix_driver.start = sx_start; specialix_driver.hangup = sx_hangup; specialix_callout_driver = specialix_driver; specialix_callout_driver.name = "cuw"; specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR; specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT; if ((error = tty_register_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; } if ((error = tty_register_driver(&specialix_callout_driver))) { free_page((unsigned long)tmp_buf); tty_unregister_driver(&specialix_driver); printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout 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].callout_termios = specialix_callout_driver.init_termios; sx_port[i].normal_termios = specialix_driver.init_termios; sx_port[i].magic = SPECIALIX_MAGIC; sx_port[i].tqueue.routine = do_softint; sx_port[i].tqueue.data = &sx_port[i]; sx_port[i].tqueue_hangup.routine = do_sx_hangup; sx_port[i].tqueue_hangup.data = &sx_port[i]; sx_port[i].close_delay = 50 * HZ/100; sx_port[i].closing_wait = 3000 * HZ/100; } return 0;}static void sx_release_drivers(void){ free_page((unsigned long)tmp_buf); tty_unregister_driver(&specialix_driver); tty_unregister_driver(&specialix_callout_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 */int 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 if (pci_present()) { struct pci_dev *pdev = NULL; unsigned int tint; 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; sx_board[i].irq = pdev->irq; pci_read_config_dword(pdev, PCI_BASE_ADDRESS_2, &tint); /* Mask out the fact that it's IO-space */ sx_board[i].base = tint & PCI_BASE_ADDRESS_IO_MASK; 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;}#ifdef MODULEint iobase[SX_NBOARD] = {0,};int irq [SX_NBOARD] = {0,};/* * 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. * */int 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();} void cleanup_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 }#endif /* MODULE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -