/* card->lock HAS to be held */ static inline void raise_rts(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0a04, base); InterruptTheCard(base); port->status |= ISI_RTS; }
/* card->lock HAS to be held */ static void drop_dtr_rts(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (WaitTillCardIsFree(base)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0c04, base); InterruptTheCard(base); port->status &= ~(ISI_RTS | ISI_DTR); }
static inline void kill_queue(struct isi_port *port, short queue) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (!lock_card(card)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw((queue << 8) | 0x06, base); InterruptTheCard(base); unlock_card(card); }
static void isicom_dtr_rts(struct tty_port *port, int on) { struct isi_port *ip = container_of(port, struct isi_port, port); struct isi_board *card = ip->card; unsigned long base = card->base; u16 channel = ip->channel; if (!lock_card(card)) return; if (on) { outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0f04, base); InterruptTheCard(base); ip->status |= (ISI_DTR | ISI_RTS); } else { outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0C04, base); InterruptTheCard(base); ip->status &= ~(ISI_DTR | ISI_RTS); } unlock_card(card); }
/* card->lock MUST NOT be held */ static inline void raise_dtr_rts(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (!lock_card(card)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0f04, base); InterruptTheCard(base); port->status |= (ISI_DTR | ISI_RTS); unlock_card(card); }
static inline void drop_dtr(struct isi_port *port) { struct isi_board *card = port->card; unsigned long base = card->base; u16 channel = port->channel; if (!lock_card(card)) return; outw(0x8000 | (channel << card->shift_count) | 0x02, base); outw(0x0404, base); InterruptTheCard(base); port->status &= ~ISI_DTR; unlock_card(card); }
static void isicom_tx(unsigned long _data) { unsigned long flags, base; unsigned int retries; short count = (BOARD_COUNT-1), card; short txcount, wrd, residue, word_count, cnt; struct isi_port *port; struct tty_struct *tty; /* find next active board */ card = (prev_card + 1) & 0x0003; while(count-- > 0) { if (isi_card[card].status & BOARD_ACTIVE) break; card = (card + 1) & 0x0003; } if (!(isi_card[card].status & BOARD_ACTIVE)) goto sched_again; prev_card = card; count = isi_card[card].port_count; port = isi_card[card].ports; base = isi_card[card].base; spin_lock_irqsave(&isi_card[card].card_lock, flags); for (retries = 0; retries < 100; retries++) { if (inw(base + 0xe) & 0x1) break; udelay(2); } if (retries >= 100) goto unlock; for (;count > 0;count--, port++) { /* port not active or tx disabled to force flow control */ if (!(port->flags & ASYNC_INITIALIZED) || !(port->status & ISI_TXOK)) continue; tty = port->tty; if (tty == NULL) continue; txcount = min_t(short, TX_SIZE, port->xmit_cnt); if (txcount <= 0 || tty->stopped || tty->hw_stopped) continue; if (!(inw(base + 0x02) & (1 << port->channel))) continue; pr_dbg("txing %d bytes, port%d.\n", txcount, port->channel + 1); outw((port->channel << isi_card[card].shift_count) | txcount, base); residue = NO; wrd = 0; while (1) { cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - port->xmit_tail)); if (residue == YES) { residue = NO; if (cnt > 0) { wrd |= (port->xmit_buf[port->xmit_tail] << 8); port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; txcount--; cnt--; outw(wrd, base); } else { outw(wrd, base); break; } } if (cnt <= 0) break; word_count = cnt >> 1; 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)); }
static void isicom_tx(unsigned long _data) { short count = (BOARD_COUNT-1), card, base; short txcount, wrd, residue, word_count, cnt; struct isi_port *port; struct tty_struct *tty; /* find next active board */ card = (prev_card + 1) & 0x0003; while(count-- > 0) { if (isi_card[card].status & BOARD_ACTIVE) break; card = (card + 1) & 0x0003; } if (!(isi_card[card].status & BOARD_ACTIVE)) goto sched_again; prev_card = card; count = isi_card[card].port_count; port = isi_card[card].ports; base = isi_card[card].base; for (;count > 0;count--, port++) { if (!lock_card_at_interrupt(&isi_card[card])) continue; /* port not active or tx disabled to force flow control */ if (!(port->flags & ASYNC_INITIALIZED) || !(port->status & ISI_TXOK)) unlock_card(&isi_card[card]); continue; tty = port->tty; if (tty == NULL) { unlock_card(&isi_card[card]); continue; } txcount = min_t(short, TX_SIZE, port->xmit_cnt); if (txcount <= 0 || tty->stopped || tty->hw_stopped) { unlock_card(&isi_card[card]); continue; } if (!(inw(base + 0x02) & (1 << port->channel))) { unlock_card(&isi_card[card]); continue; } pr_dbg("txing %d bytes, port%d.\n", txcount, port->channel + 1); outw((port->channel << isi_card[card].shift_count) | txcount, base); residue = NO; wrd = 0; while (1) { cnt = min_t(int, txcount, (SERIAL_XMIT_SIZE - port->xmit_tail)); if (residue == YES) { residue = NO; if (cnt > 0) { wrd |= (port->xmit_buf[port->xmit_tail] << 8); port->xmit_tail = (port->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); port->xmit_cnt--; txcount--; cnt--; outw(wrd, base); } else { outw(wrd, base); break; } } if (cnt <= 0) break; word_count = cnt >> 1; 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) schedule_work(&port->bh_tqueue); unlock_card(&isi_card[card]); } /* schedule another tx for hopefully in about 10ms */ sched_again: if (!re_schedule) { re_schedule = 2; return; } init_timer(&tx); tx.expires = jiffies + HZ/100; tx.data = 0; tx.function = isicom_tx; add_timer(&tx); return; }