📄 dmascc.c
字号:
} else { inb_p(io + TWIN_CLR_TMR2); tm_isr(&info->dev[1]); } } /* No interrupts pending from the PackeTwin */ return; }}static inline void z8530_isr(struct scc_info *info){ int is, a_cmd; a_cmd = info->scc_base + SCCA_CMD; while ((is = read_scc(a_cmd, R3))) { if (is & CHARxIP) { rx_isr(&info->dev[0]); } else if (is & CHATxIP) { tx_isr(&info->dev[0]); } else if (is & CHAEXT) { es_isr(&info->dev[0]); } else if (is & CHBRxIP) { rx_isr(&info->dev[1]); } else if (is & CHBTxIP) { tx_isr(&info->dev[1]); } else { es_isr(&info->dev[1]); } } /* Ok, no interrupts pending from this 8530. The INT line should be inactive now. */}static void rx_isr(struct device *dev){ struct scc_priv *priv = dev->priv; int cmd = priv->cmd; if (dev->dma) { /* Check special condition and perform error reset. See 2.4.7.5. */ special_condition(dev, read_scc(cmd, R1)); write_scc(cmd, R0, ERR_RES); } else { /* Check special condition for each character. Error reset not necessary. Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */ int rc; while (read_scc(cmd, R0) & Rx_CH_AV) { rc = read_scc(cmd, R1); if (priv->rx_ptr < BUF_SIZE) priv->rx_buf[priv->rx_head][priv->rx_ptr++] = read_scc(cmd, R8); else { priv->rx_over = 2; read_scc(cmd, R8); } special_condition(dev, rc); } }}static void special_condition(struct device *dev, int rc){ struct scc_priv *priv = dev->priv; int cb, cmd = priv->cmd; unsigned long flags; /* See Figure 2-15. Only overrun and EOF need to be checked. */ if (rc & Rx_OVR) { /* Receiver overrun */ priv->rx_over = 1; if (!dev->dma) write_scc(cmd, R0, ERR_RES); } else if (rc & END_FR) { /* End of frame. Get byte count */ if (dev->dma) { flags=claim_dma_lock(); disable_dma(dev->dma); clear_dma_ff(dev->dma); cb = BUF_SIZE - get_dma_residue(dev->dma) - 2; release_dma_lock(flags); } else { cb = priv->rx_ptr - 2; } if (priv->rx_over) { /* We had an overrun */ priv->stats.rx_errors++; if (priv->rx_over == 2) priv->stats.rx_length_errors++; else priv->stats.rx_fifo_errors++; priv->rx_over = 0; } else if (rc & CRC_ERR) { /* Count invalid CRC only if packet length >= minimum */ if (cb >= 8) { priv->stats.rx_errors++; priv->stats.rx_crc_errors++; } } else { if (cb >= 8) { /* Put good frame in FIFO */ priv->rx_len[priv->rx_head] = cb; priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF; priv->rx_count++; if (priv->rx_count == NUM_RX_BUF) { /* Disable receiver if FIFO full */ write_scc(cmd, R3, Rx8); priv->stats.rx_errors++; priv->stats.rx_over_errors++; } /* Mark bottom half handler */ queue_task(&priv->rx_task, &tq_immediate); mark_bh(IMMEDIATE_BH); } } /* Get ready for new frame */ if (dev->dma) { flags=claim_dma_lock(); set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); set_dma_count(dev->dma, BUF_SIZE); enable_dma(dev->dma); release_dma_lock(flags); } else { priv->rx_ptr = 0; } }}static void rx_bh(void *arg){ struct device *dev = arg; struct scc_priv *priv = dev->priv; struct scc_info *info = priv->info; int cmd = priv->cmd; int i = priv->rx_tail; int cb; unsigned long flags; struct sk_buff *skb; unsigned char *data; save_flags(flags); cli(); while (priv->rx_count) { restore_flags(flags); cb = priv->rx_len[i]; /* Allocate buffer */ skb = dev_alloc_skb(cb+1); if (skb == NULL) { /* Drop packet */ priv->stats.rx_dropped++; } else { /* Fill buffer */ data = skb_put(skb, cb+1); data[0] = 0; memcpy(&data[1], priv->rx_buf[i], cb); skb->dev = dev; skb->protocol = ntohs(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); priv->stats.rx_packets++; } save_flags(flags); cli(); /* Enable receiver if RX buffers have been unavailable */ if ((priv->rx_count == NUM_RX_BUF) && (priv->status & DCD)) { if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK); write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB); if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK); } /* Move tail */ priv->rx_tail = i = (i + 1) % NUM_RX_BUF; priv->rx_count--; } restore_flags(flags);}static void tx_isr(struct device *dev){ struct scc_priv *priv = dev->priv; int cmd = priv->cmd; int i = priv->tx_tail, p = priv->tx_ptr; /* Suspend TX interrupts if we don't want to send anything. See Figure 2-22. */ if (p == priv->tx_len[i]) { write_scc(cmd, R0, RES_Tx_P); return; } /* Write characters */ while ((read_scc(cmd, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) { write_scc(cmd, R8, priv->tx_buf[i][p++]); } priv->tx_ptr = p;}static void es_isr(struct device *dev){ struct scc_priv *priv = dev->priv; struct scc_info *info = priv->info; int i, cmd = priv->cmd; int st, dst, res; unsigned long flags; /* Read status and reset interrupt bit */ st = read_scc(cmd, R0); write_scc(cmd, R0, RES_EXT_INT); dst = priv->status ^ st; priv->status = st; /* Since the EOM latch is reset automatically, we assume that it has been zero if and only if we are in the TX_ACTIVE state. Otherwise we follow 2.4.9.6. */ /* Transmit underrun */ if ((priv->tx_state == TX_ACTIVE) && (st & TxEOM)) { /* Get remaining bytes */ i = priv->tx_tail; if (dev->dma) { flags=claim_dma_lock(); disable_dma(dev->dma); clear_dma_ff(dev->dma); res = get_dma_residue(dev->dma); release_dma_lock(flags); } else { res = priv->tx_len[i] - priv->tx_ptr; if (res) write_scc(cmd, R0, RES_Tx_P); priv->tx_ptr = 0; } /* Remove frame from FIFO */ priv->tx_tail = (i + 1) % NUM_TX_BUF; priv->tx_count--; dev->tbusy = 0; /* Check if another frame is available and we are allowed to transmit */ if (priv->tx_count && (jiffies - priv->tx_start) < priv->param.txtime) { if (dev->dma) { flags=claim_dma_lock(); set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]); set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]); enable_dma(dev->dma); release_dma_lock(flags); } else { /* If we have an ESCC, we are allowed to write data bytes immediately. Otherwise we have to wait for the next TX interrupt. See Figure 2-22. */ if (info->chip == Z85230) { tx_isr(dev); } } } else { /* No frame available. Disable interrupts. */ priv->tx_state = TX_SQDELAY; delay(dev, priv->param.sqdelay); write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE); write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN); } /* Update packet statistics */ if (res) { priv->stats.tx_errors++; priv->stats.tx_fifo_errors++; } else { priv->stats.tx_packets++; } /* Inform upper layers */ mark_bh(NET_BH); } /* DCD transition */ if ((priv->tx_state < TX_TXDELAY) && (dst & DCD)) { /* Transmitter state change */ priv->tx_state = TX_OFF; /* Enable or disable receiver */ if (st & DCD) { if (dev->dma) { /* Program DMA controller */ flags=claim_dma_lock(); disable_dma(dev->dma); clear_dma_ff(dev->dma); set_dma_mode(dev->dma, DMA_MODE_READ); set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); set_dma_count(dev->dma, BUF_SIZE); enable_dma(dev->dma); release_dma_lock(flags); /* Configure PackeTwin DMA */ if (info->type == TYPE_TWIN) { outb_p((dev->dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3, dev->base_addr + TWIN_DMA_CFG); } /* Sp. cond. intr. only, ext int enable */ write_scc(cmd, R1, EXT_INT_ENAB | INT_ERR_Rx | WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB); } else { /* Intr. on all Rx characters and Sp. cond., ext int enable */ write_scc(cmd, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT | WT_FN_RDYFN); } if (priv->rx_count < NUM_RX_BUF) { /* Enable receiver */ write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB); } } else { /* Disable DMA */ if (dev->dma) { flags=claim_dma_lock(); disable_dma(dev->dma); release_dma_lock(flags); } /* Disable receiver */ write_scc(cmd, R3, Rx8); /* DMA disable, RX int disable, Ext int enable */ write_scc(cmd, R1, EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN); /* Transmitter state change */ if (random() > priv->param.persist) delay(dev, priv->param.slottime); else { if (priv->tx_count) { priv->tx_state = TX_TXDELAY; write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); priv->tx_start = jiffies; delay(dev, priv->param.txdelay); } else { priv->tx_state = TX_IDLE; } } } } /* CTS transition */ if ((info->type <= TYPE_PI2) && (dst & CTS) && (~st & CTS)) { /* Timer has expired */ tm_isr(dev); } /* /SYNC/HUNT transition */ if ((dst & SYNC_HUNT) && (~st & SYNC_HUNT)) { /* Reset current frame and clear RX FIFO */ while (read_scc(cmd, R0) & Rx_CH_AV) read_scc(cmd, R8); priv->rx_over = 0; if (dev->dma) { flags=claim_dma_lock(); disable_dma(dev->dma); clear_dma_ff(dev->dma); set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]); set_dma_count(dev->dma, BUF_SIZE); enable_dma(dev->dma); release_dma_lock(flags); } else { priv->rx_ptr = 0; } }}static void tm_isr(struct device *dev){ struct scc_priv *priv = dev->priv; struct scc_info *info = priv->info; int cmd = priv->cmd; unsigned long flags; switch (priv->tx_state) { case TX_OFF: if (~priv->status & DCD) { if (random() > priv->param.persist) delay(dev, priv->param.slottime); else { if (priv->tx_count) { priv->tx_state = TX_TXDELAY; write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8); priv->tx_start = jiffies; delay(dev, priv->param.txdelay); } else { priv->tx_state = TX_IDLE; } } } break; case TX_TXDELAY: priv->tx_state = TX_ACTIVE; if (dev->dma) { /* Program DMA controller */ flags=claim_dma_lock(); disable_dma(dev->dma); clear_dma_ff(dev->dma); set_dma_mode(dev->dma, DMA_MODE_WRITE); set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]); set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]); enable_dma(dev->dma); release_dma_lock(flags); /* Configure PackeTwin DMA */ if (info->type == TYPE_TWIN) { outb_p((dev->dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3, dev->base_addr + TWIN_DMA_CFG); } /* Enable interrupts and DMA. On the PackeTwin, the DTR//REQ pin is used for TX DMA requests, but we enable the WAIT/DMA request pin, anyway */ write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE); write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB); } else { write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE); write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB); tx_isr(dev); } if (info->chip == Z8530) write_scc(cmd, R0, RES_EOM_L); break; case TX_SQDELAY: /* Disable transmitter */ write_scc(cmd, R5, TxCRC_ENAB | Tx8); /* Transmitter state change: Switch to TX_OFF and wait at least 1 slottime. */ priv->tx_state = TX_OFF; if (~priv->status & DCD) delay(dev, priv->param.waittime); }}static inline void delay(struct device *dev, int t){ struct scc_priv *priv = dev->priv; int tmr = priv->tmr; outb_p(t & 0xFF, tmr); outb_p((t >> 8) & 0xFF, tmr);}static inline unsigned char random(void){ /* See "Numerical Recipes in C", second edition, p. 284 */ rand = rand * 1664525L + 1013904223L; return (unsigned char) (rand >> 24);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -