isicom.c
来自「linux 内核源代码」· C语言 代码 · 共 1,898 行 · 第 1/4 页
C
1,898 行
outsw(base, port->xmit_buf+port->xmit_tail,word_count); port->xmit_tail = (port->xmit_tail + (word_count << 1)) & (SERIAL_XMIT_SIZE - 1); txcount -= (word_count << 1); port->xmit_cnt -= (word_count << 1); if (cnt & 0x0001) { residue = YES; wrd = port->xmit_buf[port->xmit_tail]; port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; txcount--; } } InterruptTheCard(base); if (port->xmit_cnt <= 0) port->status &= ~ISI_TXOK; if (port->xmit_cnt <= WAKEUP_CHARS) tty_wakeup(tty); }unlock: spin_unlock_irqrestore(&isi_card[card].card_lock, flags); /* schedule another tx for hopefully in about 10ms */sched_again: mod_timer(&tx, jiffies + msecs_to_jiffies(10));}/* * Main interrupt handler routine */static irqreturn_t isicom_interrupt(int irq, void *dev_id){ struct isi_board *card = dev_id; struct isi_port *port; struct tty_struct *tty; unsigned long base; u16 header, word_count, count, channel; short byte_count; unsigned char *rp; if (!card || !(card->status & FIRMWARE_LOADED)) return IRQ_NONE; base = card->base; /* did the card interrupt us? */ if (!(inw(base + 0x0e) & 0x02)) return IRQ_NONE; spin_lock(&card->card_lock); /* * disable any interrupts from the PCI card and lower the * interrupt line */ outw(0x8000, base+0x04); ClearInterrupt(base); inw(base); /* get the dummy word out */ header = inw(base); channel = (header & 0x7800) >> card->shift_count; byte_count = header & 0xff; if (channel + 1 > card->port_count) { printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%lx): " "%d(channel) > port_count.\n", base, channel+1); outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED; } port = card->ports + channel; if (!(port->flags & ASYNC_INITIALIZED)) { outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED; } tty = port->tty; if (tty == NULL) { word_count = byte_count >> 1; while(byte_count > 1) { inw(base); byte_count -= 2; } if (byte_count & 0x01) inw(base); outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED; } if (header & 0x8000) { /* Status Packet */ header = inw(base); switch(header & 0xff) { case 0: /* Change in EIA signals */ if (port->flags & ASYNC_CHECK_CD) { if (port->status & ISI_DCD) { if (!(header & ISI_DCD)) { /* Carrier has been lost */ pr_dbg("interrupt: DCD->low.\n" ); port->status &= ~ISI_DCD; tty_hangup(tty); } } else if (header & ISI_DCD) { /* Carrier has been detected */ pr_dbg("interrupt: DCD->high.\n"); port->status |= ISI_DCD; wake_up_interruptible(&port->open_wait); } } else { if (header & ISI_DCD) port->status |= ISI_DCD; else port->status &= ~ISI_DCD; } if (port->flags & ASYNC_CTS_FLOW) { if (port->tty->hw_stopped) { if (header & ISI_CTS) { port->tty->hw_stopped = 0; /* start tx ing */ port->status |= (ISI_TXOK | ISI_CTS); tty_wakeup(tty); } } else if (!(header & ISI_CTS)) { port->tty->hw_stopped = 1; /* stop tx ing */ port->status &= ~(ISI_TXOK | ISI_CTS); } } else { if (header & ISI_CTS) port->status |= ISI_CTS; else port->status &= ~ISI_CTS; } if (header & ISI_DSR) port->status |= ISI_DSR; else port->status &= ~ISI_DSR; if (header & ISI_RI) port->status |= ISI_RI; else port->status &= ~ISI_RI; break; case 1: /* Received Break !!! */ tty_insert_flip_char(tty, 0, TTY_BREAK); if (port->flags & ASYNC_SAK) do_SAK(tty); tty_flip_buffer_push(tty); break; case 2: /* Statistics */ pr_dbg("isicom_interrupt: stats!!!.\n"); break; default: pr_dbg("Intr: Unknown code in status packet.\n"); break; } } else { /* Data Packet */ count = tty_prepare_flip_string(tty, &rp, byte_count & ~1); pr_dbg("Intr: Can rx %d of %d bytes.\n", count, byte_count); word_count = count >> 1; insw(base, rp, word_count); byte_count -= (word_count << 1); if (count & 0x0001) { tty_insert_flip_char(tty, inw(base) & 0xff, TTY_NORMAL); byte_count -= 2; } if (byte_count > 0) { pr_dbg("Intr(0x%lx:%d): Flip buffer overflow! dropping " "bytes...\n", base, channel + 1); while(byte_count > 0) { /* drain out unread xtra data */ inw(base); byte_count -= 2; } } tty_flip_buffer_push(tty); } outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); return IRQ_HANDLED;}static void isicom_config_port(struct isi_port *port){ struct isi_board *card = port->card; struct tty_struct *tty; unsigned long baud; unsigned long base = card->base; u16 channel_setup, channel = port->channel, shift_count = card->shift_count; unsigned char flow_ctrl; if (!(tty = port->tty) || !tty->termios) return; baud = C_BAUD(tty); if (baud & CBAUDEX) { baud &= ~CBAUDEX; /* if CBAUDEX bit is on and the baud is set to either 50 or 75 * then the card is programmed for 57.6Kbps or 115Kbps * respectively. */ /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ if (baud < 1 || baud > 4) port->tty->termios->c_cflag &= ~CBAUDEX; else baud += 15; } if (baud == 15) { /* the ASYNC_SPD_HI and ASYNC_SPD_VHI options are set * by the set_serial_info ioctl ... this is done by * the 'setserial' utility. */ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud++; /* 57.6 Kbps */ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud +=2; /* 115 Kbps */ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) baud += 3; /* 230 kbps*/ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) baud += 4; /* 460 kbps*/ } if (linuxb_to_isib[baud] == -1) { /* hang up */ drop_dtr(port); return; } else raise_dtr(port); if (WaitTillCardIsFree(base) == 0) { outw(0x8000 | (channel << shift_count) |0x03, base); outw(linuxb_to_isib[baud] << 8 | 0x03, base); channel_setup = 0; switch(C_CSIZE(tty)) { case CS5: channel_setup |= ISICOM_CS5; break; case CS6: channel_setup |= ISICOM_CS6; break; case CS7: channel_setup |= ISICOM_CS7; break; case CS8: channel_setup |= ISICOM_CS8; break; } if (C_CSTOPB(tty)) channel_setup |= ISICOM_2SB; if (C_PARENB(tty)) { channel_setup |= ISICOM_EVPAR; if (C_PARODD(tty)) channel_setup |= ISICOM_ODPAR; } outw(channel_setup, base); InterruptTheCard(base); } if (C_CLOCAL(tty)) port->flags &= ~ASYNC_CHECK_CD; else port->flags |= ASYNC_CHECK_CD; /* flow control settings ...*/ flow_ctrl = 0; port->flags &= ~ASYNC_CTS_FLOW; if (C_CRTSCTS(tty)) { port->flags |= ASYNC_CTS_FLOW; flow_ctrl |= ISICOM_CTSRTS; } if (I_IXON(tty)) flow_ctrl |= ISICOM_RESPOND_XONXOFF; if (I_IXOFF(tty)) flow_ctrl |= ISICOM_INITIATE_XONXOFF; if (WaitTillCardIsFree(base) == 0) { outw(0x8000 | (channel << shift_count) |0x04, base); outw(flow_ctrl << 8 | 0x05, base); outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base); InterruptTheCard(base); } /* rx enabled -> enable port for rx on the card */ if (C_CREAD(tty)) { card->port_status |= (1 << channel); outw(card->port_status, base + 0x02); }}/* open et all */static inline void isicom_setup_board(struct isi_board *bp){ int channel; struct isi_port *port; unsigned long flags; spin_lock_irqsave(&bp->card_lock, flags); if (bp->status & BOARD_ACTIVE) { spin_unlock_irqrestore(&bp->card_lock, flags); return; } port = bp->ports; bp->status |= BOARD_ACTIVE; for (channel = 0; channel < bp->port_count; channel++, port++) drop_dtr_rts(port); spin_unlock_irqrestore(&bp->card_lock, flags);}static int isicom_setup_port(struct isi_port *port){ struct isi_board *card = port->card; unsigned long flags; if (port->flags & ASYNC_INITIALIZED) { return 0; } if (!port->xmit_buf) { unsigned long page; if (!(page = get_zeroed_page(GFP_KERNEL))) return -ENOMEM; if (port->xmit_buf) { free_page(page); return -ERESTARTSYS; } port->xmit_buf = (unsigned char *) page; } spin_lock_irqsave(&card->card_lock, flags); if (port->tty) clear_bit(TTY_IO_ERROR, &port->tty->flags); if (port->count == 1) card->count++; port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; /* discard any residual data */ if (WaitTillCardIsFree(card->base) == 0) { outw(0x8000 | (port->channel << card->shift_count) | 0x02, card->base); outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base); InterruptTheCard(card->base); } isicom_config_port(port); port->flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); return 0;}static int block_til_ready(struct tty_struct *tty, struct file *filp, struct isi_port *port){ struct isi_board *card = port->card; int do_clocal = 0, retval; unsigned long flags; DECLARE_WAITQUEUE(wait, current); /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { pr_dbg("block_til_ready: close in progress.\n"); interruptible_sleep_on(&port->close_wait); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; } /* if non-blocking mode is set ... */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { pr_dbg("block_til_ready: non-block mode.\n"); port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (C_CLOCAL(tty)) do_clocal = 1; /* block waiting for DCD to be asserted, and while callout dev is busy */ retval = 0; add_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&card->card_lock, flags); if (!tty_hung_up_p(filp)) port->count--; port->blocked_open++; spin_unlock_irqrestore(&card->card_lock, flags); while (1) { raise_dtr_rts(port); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(port->flags & ASYNC_CLOSING) && (do_clocal || (port->status & ISI_DCD))) { break; } if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&card->card_lock, flags); if (!tty_hung_up_p(filp)) port->count++; port->blocked_open--; spin_unlock_irqrestore(&card->card_lock, flags); if (retval) return retval; port->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static int isicom_open(struct tty_struct *tty, struct file *filp){ struct isi_port *port; struct isi_board *card; unsigned int board; int error, line; line = tty->index; if (line < 0 || line > PORT_COUNT-1) return -ENODEV; board = BOARD(line); card = &isi_card[board]; if (!(card->status & FIRMWARE_LOADED)) return -ENODEV; /* open on a port greater than the port count for the card !!! */ if (line > ((board * 16) + card->port_count - 1)) return -ENODEV; port = &isi_ports[line]; if (isicom_paranoia_check(port, tty->name, "isicom_open")) return -ENODEV; isicom_setup_board(card); port->count++; tty->driver_data = port;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?