📄 pt.c
字号:
/* We have a valid frame */ if (lp->dmachan) { pkt_len = lp->rcvbuf->cnt = bytecount - 2 +1; /* Get buffer for next frame */ cur_buf = lp->rcvbuf; switchbuffers(lp); setup_rx_dma(lp); } else { pkt_len = lp->rcvbuf->cnt -= 2; /* Toss 2 CRC bytes */ pkt_len += 1; /* make room for KISS control byte */ } /* Malloc up new buffer */ sksize = pkt_len; skb = dev_alloc_skb(sksize); if (skb == NULL) { printk(KERN_ERR "PT: %s: Memory squeeze, dropping packet.\n", dev->name); lp->stats.rx_dropped++; restore_flags(flags); return; } skb->dev = dev; /* KISS kludge = prefix with a 0 byte */ cfix=skb_put(skb,pkt_len); *cfix++=0; /* skb->data points to the start of sk_buff area */ if (lp->dmachan) memcpy(cfix, (char*)cur_buf->data, pkt_len - 1); else memcpy(cfix, lp->rcvbuf->data, pkt_len - 1); skb->protocol = ntohs(ETH_P_AX25); skb->mac.raw=skb->data; lp->stats.rx_bytes+=skb->len; netif_rx(skb); lp->stats.rx_packets++; if (!lp->dmachan) { /* packet queued - wind back buffer for next frame */ lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; } } /* good frame */ } /* check active Rx */ /* Clear error status */ lp->rstate = ACTIVE; /* Reset error latch */ } /* end EOF check */ wrtscc(lp->cardbase, cmd, R0, ERR_RES); restore_flags(flags);} /* pt_rxisr() *//* * This handles the two timer interrupts. * This is a real bugger, cause you have to rip it out of the pi's * external status code. They use the CTS line or something. */static void pt_tmrisr(struct pt_local *lp){ unsigned long flags;#ifdef PT_DEBUG printk(KERN_DEBUG "PT: pt_tmrisr(): tstate = %d (%d).\n", lp->tstate, lp->base & CHANA);#endif save_flags(flags); cli(); switch (lp->tstate) { /* Most of this stuff is in pt_exisr() */ case FLAGOUT: case ST_TXDELAY: case DEFER:/* case ACTIVE: case UNDERRUN:*/ pt_exisr(lp); break; default: if (lp->base & CHANA) printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel A\n", lp->tstate); else printk(KERN_ERR "PT: pt_tmrisr(): Invalid tstate %d for Channel B\n", lp->tstate); break; } /* end switch */ restore_flags(flags);} /* pt_tmrisr() *//* * This routine is called by the kernel when there is an interrupt for the * PT. */static void pt_interrupt(int irq, void *dev_id, struct pt_regs *regs){ /* It's a tad dodgy here, but we assume pt0a until proven otherwise */ struct device *dev = &pt0a; struct pt_local *lp = dev->priv; unsigned char intreg; unsigned char st; register int cbase = dev->base_addr & 0x3f0; unsigned long flags; /* Read the PT's interrupt register, this is not the SCC one! */ intreg = inb_p(cbase + INT_REG); while(( intreg & 0x07) != 0x07) { /* Read interrupt register pending from Channel A */ while ((st = rdscc(cbase, cbase + CHANA + CTL, R3)) != 0) { /* Read interrupt vector from R2, channel B */#ifdef PT_DEBUG printk(KERN_DEBUG "PT: pt_interrupt(): R3 = %#3x", st);#endif/* st = rdscc(lp->cardbase, cbase + CHANB + CTL, R2) & 0x0e;*/#ifdef PT_DEBUG printk(KERN_DEBUG "PI: R2 = %#3x.\n", st);#endif if (st & CHARxIP) { /* Channel A Rx */ lp = (struct pt_local*)pt0a.priv; pt_rxisr(&pt0a); } else if (st & CHATxIP) { /* Channel A Tx */ lp = (struct pt_local*)pt0a.priv; pt_txisr(lp); } else if (st & CHAEXT) { /* Channel A External Status */ lp = (struct pt_local*)pt0a.priv; pt_exisr(lp); } else if (st & CHBRxIP) { /* Channel B Rx */ lp= (struct pt_local*)pt0b.priv; pt_rxisr(&pt0b); } else if (st & CHBTxIP) { /* Channel B Tx */ lp = (struct pt_local*)pt0b.priv; pt_txisr(lp); } else if (st & CHBEXT) { /* Channel B External Status */ lp = (struct pt_local*)pt0b.priv; pt_exisr(lp); } /* Reset highest interrupt under service */ save_flags(flags); cli(); wrtscc(lp->cardbase, lp->base + CTL, R0, RES_H_IUS); restore_flags(flags); } /* end of SCC ints */ if (!(intreg & PT_TMR1_MSK)) { /* Clear timer 1 */ inb_p(cbase + TMR1CLR); pt_tmrisr( (struct pt_local*)pt0a.priv); } if (!(intreg & PT_TMR2_MSK)) { /* Clear timer 2 */ inb_p(cbase + TMR2CLR); pt_tmrisr( (struct pt_local*)pt0b.priv); } /* Get the next PT interrupt vector */ intreg = inb_p(cbase + INT_REG); } /* while (intreg) */} /* pt_interrupt() */static void pt_exisr(struct pt_local *lp){ unsigned long flags; int cmd = lp->base + CTL; unsigned char st; char c; int length; save_flags(flags); cli(); /* Get external status */ st = rdscc(lp->cardbase, cmd, R0);#ifdef PT_DEBUG printk(KERN_DEBUG "PT: exisr(): R0 = %#3x tstate = %d (%d).\n", st, lp->tstate, lp->base & CHANA);#endif /* Reset external status latch */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((lp->rstate >= ACTIVE) && (st & BRK_ABRT) && lp->dmachan) { setup_rx_dma(lp); lp->rstate = ACTIVE; } switch (lp->tstate) { case ACTIVE: /* Unexpected underrun */#ifdef PT_DEBUG printk(KERN_DEBUG "PT: exisr(): unexpected underrun detected.\n");#endif kfree_skb(lp->sndbuf); lp->sndbuf = NULL; if (!lp->dmachan) { wrtscc(lp->cardbase, cmd, R0, SEND_ABORT); lp->stats.tx_errors++; lp->stats.tx_fifo_errors++; } lp->tstate = FLAGOUT; tdelay(lp, lp->squeldelay); restore_flags(flags); return; case UNDERRUN: lp->tstate = CRCOUT; restore_flags(flags); return; case FLAGOUT: /* squeldelay has timed out */ /* Find a frame for transmission */ if ((lp->sndbuf = skb_dequeue(&lp->sndq)) == NULL) { /* Nothing to send - return to Rx mode */ pt_rts(lp, OFF); lp->tstate = IDLE; restore_flags(flags); return; } if (!lp->dmachan) { lp->txptr = lp->sndbuf->data; lp->txptr++; /* Ignore KISS control byte */ lp->txcnt = (int) lp->sndbuf->len - 1; } /* Fall through if we have a packet */ case ST_TXDELAY: if (lp->dmachan) { /* Disable DMA chan */ disable_dma(lp->dmachan); /* Set up for TX dma */ wrtscc(lp->cardbase, cmd, R1, WT_FN_RDYFN | EXT_INT_ENAB); length = lp->sndbuf->len - 1; memcpy(lp->txdmabuf, &lp->sndbuf->data[1], length); /* Setup DMA controller for Tx */ setup_tx_dma(lp, length); enable_dma(lp->dmachan); /* Reset CRC, Txint pending */ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC | RES_Tx_P); /* Allow underrun only */ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* Enable TX DMA */ wrtscc(lp->cardbase, cmd, R1, WT_RDY_ENAB | WT_FN_RDYFN | EXT_INT_ENAB); /* Send CRC on underrun */ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); lp->tstate = ACTIVE; break; } /* Get first char to send */ lp->txcnt--; c = *lp->txptr++; /* Reset CRC for next frame */ wrtscc(lp->cardbase, cmd, R0, RES_Tx_CRC); /* send abort on underrun */ if (lp->nrzi) { wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZI | ABUNDER); } else { wrtscc(lp->cardbase, cmd, R10, CRCPS | NRZ | ABUNDER); } /* send first char */ wrtscc(lp->cardbase, cmd, R8, c); /* Reset end of message latch */ wrtscc(lp->cardbase, cmd, R0, RES_EOM_L); /* stuff an extra one in *//* while ((rdscc(lp->cardbase, cmd, R0) & Tx_BUF_EMP) && lp->txcnt) { lp->txcnt--; c = *lp->txptr++; wrtscc(lp->cardbase, cmd, R8, c); }*/ /* select Tx interrupts to enable */ /* Allow underrun int only */ wrtscc(lp->cardbase, cmd, R15, TxUIE); /* Reset external interrupts */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); /* Tx and Rx ints enabled */ wrtscc(lp->cardbase, cmd, R1, TxINT_ENAB | EXT_INT_ENAB); lp->tstate = ACTIVE; restore_flags(flags); return; /* slotime has timed out */ case DEFER: /* Check DCD - debounce it * see Intel Microcommunications Handbook, p2-308 */ wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); wrtscc(lp->cardbase, cmd, R0, RES_EXT_INT); if ((rdscc(lp->cardbase, cmd, R0) & DCD) != 0) { lp->tstate = DEFER; tdelay(lp, 100); /* DEFER until DCD transition or timeout */ wrtscc(lp->cardbase, cmd, R15, DCDIE); restore_flags(flags); return; } if (random() > lp->persist) { lp->tstate = DEFER; tdelay(lp, lp->slotime); restore_flags(flags); return; } if (lp->dmachan) wrtscc(lp->cardbase, cmd, R5, TxCRC_ENAB | RTS | Tx8); pt_rts(lp, ON); /* Tx on */ lp->tstate = ST_TXDELAY; tdelay(lp, lp->txdelay); restore_flags(flags); return; /* Only for int driven parts */ if (lp->dmachan) { restore_flags(flags); return; } } /* end switch */ /* * Rx mode only * This triggers when hunt mode is entered, & since an ABORT * automatically enters hunt mode, we use that to clean up * any waiting garbage */ if ((lp->rstate == ACTIVE) && (st & BRK_ABRT) ) {#ifdef PT_DEBUG printk(KERN_DEBUG "PT: exisr(): abort detected.\n");#endif /* read and dump all of SCC Rx FIFO */ (void) rdscc(lp->cardbase, cmd, R8); (void) rdscc(lp->cardbase, cmd, R8); (void) rdscc(lp->cardbase, cmd, R8); lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; /* Re-sync the SCC */ wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); } /* Check for DCD transitions */ if ( (st & DCD) != (lp->saved_RR0 & DCD)) {#ifdef PT_DEBUG printk(KERN_DEBUG "PT: pt_exisr(): DCD is now %s.\n", (st & DCD)? "ON" : "OFF" );#endif if (st & DCD) { /* Check that we don't already have some data */ if (lp->rcvbuf->cnt > 0) {#ifdef PT_DEBUG printk(KERN_DEBUG "PT: pt_exisr() dumping %u bytes from buffer.\n", lp->rcvbuf->cnt);#endif /* wind back buffers */ lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; } } else { /* DCD off */ /* read and dump al SCC FIFO */ (void)rdscc(lp->cardbase, cmd, R8); (void)rdscc(lp->cardbase, cmd, R8); (void)rdscc(lp->cardbase, cmd, R8); /* wind back buffers */ lp->rcp = lp->rcvbuf->data; lp->rcvbuf->cnt = 0; /* Re-sync the SCC */ wrtscc(lp->cardbase, cmd, R3, RxENABLE | ENT_HM | AUTO_ENAB | Rx8); } } /* Update the saved version of register RR) */ lp->saved_RR0 = st &~ ZCOUNT; restore_flags(flags);} /* pt_exisr() */#ifdef MODULEEXPORT_NO_SYMBOLS;MODULE_AUTHOR("Craig Small VK2XLZ <vk2xlz@vk2xlz.ampr.org>");MODULE_DESCRIPTION("AX.25 driver for the Gracillis PacketTwin HDLC card");int init_module(void){ return pt_init();}void cleanup_module(void){ free_irq(pt0a.irq, &pt0a); /* IRQs and IO Ports are shared */ release_region(pt0a.base_addr & 0x3f0, PT_TOTAL_SIZE); kfree(pt0a.priv); pt0a.priv = NULL; unregister_netdev(&pt0a); kfree(pt0b.priv); pt0b.priv = NULL; unregister_netdev(&pt0b);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -