📄 isicom.c
字号:
static void isicom_bottomhalf(void * data){ struct isi_port * port = (struct isi_port *) data; struct tty_struct * tty = port->tty; if (!tty) return; if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait);} /* main interrupt handler routine */ static void isicom_interrupt(int irq, void * dev_id, struct pt_regs * regs){ struct isi_board * card; struct isi_port * port; struct tty_struct * tty; unsigned short base, header, word_count, count; unsigned char channel; short byte_count; card = irq_to_board[irq]; if (!card || !(card->status & FIRMWARE_LOADED)) { printk(KERN_DEBUG "ISICOM: interrupt: not handling irq%d!.\n", irq); return; } base = card->base; inw(base); /* get the dummy word out */ header = inw(base); channel = (header & 0x7800) >> card->shift_count; byte_count = header & 0xff;#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM:Intr:(0x%x:%d).\n", base, channel+1);#endif if ((channel+1) > card->port_count) { printk(KERN_WARNING "ISICOM: isicom_interrupt(0x%x): %d(channel) > port_count.\n", base, channel+1); ClearInterrupt(base); return; } port = card->ports + channel; if (!(port->flags & ASYNC_INITIALIZED)) { ClearInterrupt(base); return; } tty = port->tty; 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 */#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: interrupt: DCD->low.\n");#endif port->status &= ~ISI_DCD; if (!((port->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_CALLOUT_NOHUP))) queue_task(&port->hangup_tq, &tq_scheduler); } } else { if (header & ISI_DCD) { /* Carrier has been detected */#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: interrupt: DCD->high.\n");#endif 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); schedule_bh(port); } } 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 !!! */ if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; *tty->flip.flag_buf_ptr++ = TTY_BREAK; /* dunno if this is right */ *tty->flip.char_buf_ptr++ = 0; tty->flip.count++; if (port->flags & ASYNC_SAK) do_SAK(tty); queue_task(&tty->flip.tqueue, &tq_timer); break; case 2: /* Statistics */ printk(KERN_DEBUG "ISICOM: isicom_interrupt: stats!!!.\n"); break; default: printk(KERN_WARNING "ISICOM: Intr: Unknown code in status packet.\n"); break; } } else { /* Data Packet */ count = MIN(byte_count, (TTY_FLIPBUF_SIZE - tty->flip.count));#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: Intr: Can rx %d of %d bytes.\n", count, byte_count);#endif word_count = count >> 1; insw(base, tty->flip.char_buf_ptr, word_count); tty->flip.char_buf_ptr += (word_count << 1); byte_count -= (word_count << 1); if (count & 0x0001) { *tty->flip.char_buf_ptr++ = (char)(inw(base) & 0xff); byte_count -= 2; } memset(tty->flip.flag_buf_ptr, 0, count); tty->flip.flag_buf_ptr += count; tty->flip.count += count; if (byte_count > 0) { printk(KERN_DEBUG "ISICOM: Intr(0x%x:%d): Flip buffer overflow! dropping bytes...\n", base, channel+1); while(byte_count > 0) { /* drain out unread xtra data */ inw(base); byte_count -= 2; } } queue_task(&tty->flip.tqueue, &tq_timer); } ClearInterrupt(base); return;} /* called with interrupts disabled */ static void isicom_config_port(struct isi_port * port){ struct isi_board * card = port->card; struct tty_struct * tty; unsigned long baud; unsigned short channel_setup, wait, base = card->base; unsigned short 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. */ if (baud < 1 || baud > 2) 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 (linuxb_to_isib[baud] == -1) { /* hang up */ drop_dtr(port); return; } else raise_dtr(port); wait = 100; while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0)); if (!wait) { printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at channel setup.\n"); return; } 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; wait = 100; while (((inw(base + 0x0e) & 0x0001) == 0) && (wait-- > 0)); if (!wait) { printk(KERN_WARNING "ISICOM: Card found busy in isicom_config_port at flow setup.\n"); return; } 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 */ extern inline void isicom_setup_board(struct isi_board * bp){ int channel; struct isi_port * port; unsigned long flags; if (bp->status & BOARD_ACTIVE) return; port = bp->ports;#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts start, port_count %d...\n", bp->port_count);#endif for(channel = 0; channel < bp->port_count; channel++, port++) { save_flags(flags); cli(); drop_dtr_rts(port); restore_flags(flags); }#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: setup_board: drop_dtr_rts stop...\n"); #endif bp->status |= BOARD_ACTIVE; MOD_INC_USE_COUNT; return;} 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_free_page(GFP_KERNEL))) return -ENOMEM; if (port->xmit_buf) { free_page(page); return -ERESTARTSYS; } port->xmit_buf = (unsigned char *) page; } save_flags(flags); cli(); 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 */ kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX); isicom_config_port(port); port->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0; } static int block_til_ready(struct tty_struct * tty, struct file * filp, struct isi_port * port) { int do_clocal = 0, retval; struct wait_queue wait = { current, NULL }; /* block if port is in the process of being closed */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: block_til_ready: close in progress.\n");#endif interruptible_sleep_on(&port->close_wait); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; } /* trying to open a callout device... check for constraints */ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: bl_ti_rdy: callout open.\n"); #endif if (port->flags & ASYNC_NORMAL_ACTIVE) return -EBUSY; if ((port->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_SESSION_LOCKOUT) && (port->session != current->session)) return -EBUSY; if ((port->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_PGRP_LOCKOUT) && (port->pgrp != current->pgrp)) return -EBUSY; port->flags |= ASYNC_CALLOUT_ACTIVE; cli(); raise_dtr_rts(port); sti(); return 0; } /* if non-blocking mode is set ... */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) {#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: block_til_ready: non-block mode.\n");#endif if (port->flags & ASYNC_CALLOUT_ACTIVE) return -EBUSY; port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (port->flags & ASYNC_CALLOUT_ACTIVE) { if (port->normal_termios.c_cflag & CLOCAL) do_clocal = 1; } else { if (C_CLOCAL(tty)) do_clocal = 1; }#ifdef ISICOM_DEBUG if (do_clocal) printk(KERN_DEBUG "ISICOM: block_til_ready: CLOCAL set.\n");#endif /* block waiting for DCD to be asserted, and while callout dev is busy */ retval = 0; add_wait_queue(&port->open_wait, &wait); cli(); if (!tty_hung_up_p(filp)) port->count--; sti(); port->blocked_open++;#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: block_til_ready: waiting for DCD...\n");#endif while (1) { cli(); if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) raise_dtr_rts(port); sti(); 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;#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: block_til_ready: tty_hung_up_p || not init.\n"); #endif break; } if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && !(port->flags & ASYNC_CLOSING) && (do_clocal || (port->status & ISI_DCD))) {#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: block_til_ready: do_clocal || DCD.\n"); #endif break; } if (signal_pending(current)) {#ifdef ISICOM_DEBUG printk(KERN_DEBUG "ISICOM: block_til_ready: sig blocked.\n");#endif retval = -ERESTARTSYS; break; } schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) port->count++; port->blocked_open--; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -