📄 tms380tr.c
字号:
return;}/* * Initializes the open parameter block. */static void tms380tr_init_opb(struct net_device *dev){ struct net_local *tp; unsigned long Addr; unsigned short RplSize = RPL_SIZE; unsigned short TplSize = TPL_SIZE; unsigned short BufferSize = BUFFER_SIZE; int i; tp = netdev_priv(dev); tp->ocpl.OPENOptions = 0; tp->ocpl.OPENOptions |= ENABLE_FULL_DUPLEX_SELECTION; tp->ocpl.FullDuplex = 0; tp->ocpl.FullDuplex |= OPEN_FULL_DUPLEX_OFF; /* * Set node address * * We go ahead and put it in the OPB even though on * most of the generic adapters this isn't required. * Its simpler this way. -- ASF */ for (i=0;i<6;i++) tp->ocpl.NodeAddr[i] = ((unsigned char *)dev->dev_addr)[i]; tp->ocpl.GroupAddr = 0; tp->ocpl.FunctAddr = 0; tp->ocpl.RxListSize = cpu_to_be16((unsigned short)RplSize); tp->ocpl.TxListSize = cpu_to_be16((unsigned short)TplSize); tp->ocpl.BufSize = cpu_to_be16((unsigned short)BufferSize); tp->ocpl.Reserved = 0; tp->ocpl.TXBufMin = TX_BUF_MIN; tp->ocpl.TXBufMax = TX_BUF_MAX; Addr = htonl(((char *)tp->ProductID - (char *)tp) + tp->dmabuffer); tp->ocpl.ProdIDAddr[0] = LOWORD(Addr); tp->ocpl.ProdIDAddr[1] = HIWORD(Addr); return;}/* * Send OPEN command to adapter */static void tms380tr_open_adapter(struct net_device *dev){ struct net_local *tp = netdev_priv(dev); if(tp->OpenCommandIssued) return; tp->OpenCommandIssued = 1; tms380tr_exec_cmd(dev, OC_OPEN); return;}/* * Clear the adapter's interrupt flag. Clear system interrupt enable * (SINTEN): disable adapter to system interrupts. */static void tms380tr_disable_interrupts(struct net_device *dev){ SIFWRITEB(0, SIFACL); return;}/* * Set the adapter's interrupt flag. Set system interrupt enable * (SINTEN): enable adapter to system interrupts. */static void tms380tr_enable_interrupts(struct net_device *dev){ SIFWRITEB(ACL_SINTEN, SIFACL); return;}/* * Put command in command queue, try to execute it. */static void tms380tr_exec_cmd(struct net_device *dev, unsigned short Command){ struct net_local *tp = netdev_priv(dev); tp->CMDqueue |= Command; tms380tr_chk_outstanding_cmds(dev); return;}static void tms380tr_timeout(struct net_device *dev){ /* * If we get here, some higher level has decided we are broken. * There should really be a "kick me" function call instead. * * Resetting the token ring adapter takes a long time so just * fake transmission time and go on trying. Our own timeout * routine is in tms380tr_timer_chk() */ dev->trans_start = jiffies; netif_wake_queue(dev);}/* * Gets skb from system, queues it and checks if it can be sent */static int tms380tr_send_packet(struct sk_buff *skb, struct net_device *dev){ struct net_local *tp = netdev_priv(dev); int err; err = tms380tr_hardware_send_packet(skb, dev); if(tp->TplFree->NextTPLPtr->BusyFlag) netif_stop_queue(dev); return (err);}/* * Move frames into adapter tx queue */static int tms380tr_hardware_send_packet(struct sk_buff *skb, struct net_device *dev){ TPL *tpl; short length; unsigned char *buf; unsigned long flags; int i; dma_addr_t dmabuf, newbuf; struct net_local *tp = netdev_priv(dev); /* Try to get a free TPL from the chain. * * NOTE: We *must* always leave one unused TPL in the chain, * because otherwise the adapter might send frames twice. */ spin_lock_irqsave(&tp->lock, flags); if(tp->TplFree->NextTPLPtr->BusyFlag) { /* No free TPL */ if (tms380tr_debug > 0) printk(KERN_DEBUG "%s: No free TPL\n", dev->name); spin_unlock_irqrestore(&tp->lock, flags); return 1; } dmabuf = 0; /* Is buffer reachable for Busmaster-DMA? */ length = skb->len; dmabuf = dma_map_single(tp->pdev, skb->data, length, DMA_TO_DEVICE); if(tp->dmalimit && (dmabuf + length > tp->dmalimit)) { /* Copy frame to local buffer */ dma_unmap_single(tp->pdev, dmabuf, length, DMA_TO_DEVICE); dmabuf = 0; i = tp->TplFree->TPLIndex; buf = tp->LocalTxBuffers[i]; memcpy(buf, skb->data, length); newbuf = ((char *)buf - (char *)tp) + tp->dmabuffer; } else { /* Send direct from skb->data */ newbuf = dmabuf; buf = skb->data; } /* Source address in packet? */ tms380tr_chk_src_addr(buf, dev->dev_addr); tp->LastSendTime = jiffies; tpl = tp->TplFree; /* Get the "free" TPL */ tpl->BusyFlag = 1; /* Mark TPL as busy */ tp->TplFree = tpl->NextTPLPtr; /* Save the skb for delayed return of skb to system */ tpl->Skb = skb; tpl->DMABuff = dmabuf; tpl->FragList[0].DataCount = cpu_to_be16((unsigned short)length); tpl->FragList[0].DataAddr = htonl(newbuf); /* Write the data length in the transmit list. */ tpl->FrameSize = cpu_to_be16((unsigned short)length); tpl->MData = buf; /* Transmit the frame and set the status values. */ tms380tr_write_tpl_status(tpl, TX_VALID | TX_START_FRAME | TX_END_FRAME | TX_PASS_SRC_ADDR | TX_FRAME_IRQ); /* Let adapter send the frame. */ tms380tr_exec_sifcmd(dev, CMD_TX_VALID); spin_unlock_irqrestore(&tp->lock, flags); return 0;}/* * Write the given value to the 'Status' field of the specified TPL. * NOTE: This function should be used whenever the status of any TPL must be * modified by the driver, because the compiler may otherwise change the * order of instructions such that writing the TPL status may be executed at * an undesireable time. When this function is used, the status is always * written when the function is called. */static void tms380tr_write_tpl_status(TPL *tpl, unsigned int Status){ tpl->Status = Status;}static void tms380tr_chk_src_addr(unsigned char *frame, unsigned char *hw_addr){ unsigned char SRBit; if((((unsigned long)frame[8]) & ~0x80) != 0) /* Compare 4 bytes */ return; if((unsigned short)frame[12] != 0) /* Compare 2 bytes */ return; SRBit = frame[8] & 0x80; memcpy(&frame[8], hw_addr, 6); frame[8] |= SRBit; return;}/* * The timer routine: Check if adapter still open and working, reopen if not. */static void tms380tr_timer_chk(unsigned long data){ struct net_device *dev = (struct net_device*)data; struct net_local *tp = netdev_priv(dev); if(tp->HaltInProgress) return; tms380tr_chk_outstanding_cmds(dev); if(time_before(tp->LastSendTime + SEND_TIMEOUT, jiffies) && (tp->TplFree != tp->TplBusy)) { /* Anything to send, but stalled too long */ tp->LastSendTime = jiffies; tms380tr_exec_cmd(dev, OC_CLOSE); /* Does reopen automatically */ } tp->timer.expires = jiffies + 2*HZ; add_timer(&tp->timer); if(tp->AdapterOpenFlag || tp->ReOpenInProgress) return; tp->ReOpenInProgress = 1; tms380tr_open_adapter(dev); return;}/* * The typical workload of the driver: Handle the network interface interrupts. */irqreturn_t tms380tr_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = dev_id; struct net_local *tp; unsigned short irq_type; int handled = 0; if(dev == NULL) { printk(KERN_INFO "%s: irq %d for unknown device.\n", dev->name, irq); return IRQ_NONE; } tp = netdev_priv(dev); irq_type = SIFREADW(SIFSTS); while(irq_type & STS_SYSTEM_IRQ) { handled = 1; irq_type &= STS_IRQ_MASK; if(!tms380tr_chk_ssb(tp, irq_type)) { printk(KERN_DEBUG "%s: DATA LATE occurred\n", dev->name); break; } switch(irq_type) { case STS_IRQ_RECEIVE_STATUS: tms380tr_reset_interrupt(dev); tms380tr_rcv_status_irq(dev); break; case STS_IRQ_TRANSMIT_STATUS: /* Check if TRANSMIT.HALT command is complete */ if(tp->ssb.Parm[0] & COMMAND_COMPLETE) { tp->TransmitCommandActive = 0; tp->TransmitHaltScheduled = 0; /* Issue a new transmit command. */ tms380tr_exec_cmd(dev, OC_TRANSMIT); } tms380tr_reset_interrupt(dev); tms380tr_tx_status_irq(dev); break; case STS_IRQ_COMMAND_STATUS: /* The SSB contains status of last command * other than receive/transmit. */ tms380tr_cmd_status_irq(dev); break; case STS_IRQ_SCB_CLEAR: /* The SCB is free for another command. */ tp->ScbInUse = 0; tms380tr_chk_outstanding_cmds(dev); break; case STS_IRQ_RING_STATUS: tms380tr_ring_status_irq(dev); break; case STS_IRQ_ADAPTER_CHECK: tms380tr_chk_irq(dev); break; case STS_IRQ_LLC_STATUS: printk(KERN_DEBUG "tms380tr: unexpected LLC status IRQ\n"); break; case STS_IRQ_TIMER: printk(KERN_DEBUG "tms380tr: unexpected Timer IRQ\n"); break; case STS_IRQ_RECEIVE_PENDING: printk(KERN_DEBUG "tms380tr: unexpected Receive Pending IRQ\n"); break; default: printk(KERN_DEBUG "Unknown Token Ring IRQ (0x%04x)\n", irq_type); break; } /* Reset system interrupt if not already done. */ if(irq_type != STS_IRQ_TRANSMIT_STATUS && irq_type != STS_IRQ_RECEIVE_STATUS) { tms380tr_reset_interrupt(dev); } irq_type = SIFREADW(SIFSTS); } return IRQ_RETVAL(handled);}/* * Reset the INTERRUPT SYSTEM bit and issue SSB CLEAR command. */static void tms380tr_reset_interrupt(struct net_device *dev){ struct net_local *tp = netdev_priv(dev); SSB *ssb = &tp->ssb; /* * [Workaround for "Data Late"] * Set all fields of the SSB to well-defined values so we can * check if the adapter has written the SSB. */ ssb->STS = (unsigned short) -1; ssb->Parm[0] = (unsigned short) -1; ssb->Parm[1] = (unsigned short) -1; ssb->Parm[2] = (unsigned short) -1; /* Free SSB by issuing SSB_CLEAR command after reading IRQ code * and clear STS_SYSTEM_IRQ bit: enable adapter for further interrupts. */ tms380tr_exec_sifcmd(dev, CMD_SSB_CLEAR | CMD_CLEAR_SYSTEM_IRQ); return;}/* * Check if the SSB has actually been written by the adapter. */static unsigned char tms380tr_chk_ssb(struct net_local *tp, unsigned short IrqType){ SSB *ssb = &tp->ssb; /* The address of the SSB. */ /* C 0 1 2 INTERRUPT CODE * - - - - -------------- * 1 1 1 1 TRANSMIT STATUS * 1 1 1 1 RECEIVE STATUS * 1 ? ? 0 COMMAND STATUS * 0 0 0 0 SCB CLEAR * 1 1 0 0 RING STATUS * 0 0 0 0 ADAPTER CHECK * * 0 = SSB field not affected by interrupt * 1 = SSB field is affected by interrupt * * C = SSB ADDRESS +0: COMMAND * 0 = SSB ADDRESS +2: STATUS 0 * 1 = SSB ADDRESS +4: STATUS 1 * 2 = SSB ADDRESS +6: STATUS 2 */ /* Check if this interrupt does use the SSB. */ if(IrqType != STS_IRQ_TRANSMIT_STATUS && IrqType != STS_IRQ_RECEIVE_STATUS && IrqType != STS_IRQ_COMMAND_STATUS && IrqType != STS_IRQ_RING_STATUS) { return (1); /* SSB not involved. */ } /* Note: All fields of the SSB have been set to all ones (-1) after it * has last been used by the software (see DriverIsr()). * * Check if the affected SSB fields are still unchanged. */ if(ssb->STS == (unsigned short) -1) return (0); /* Command field not yet available. */ if(IrqType == STS_IRQ_COMMAND_STATUS) return (1); /* Status fields not always affected. */ if(ssb->Parm[0] == (unsigned short) -1) return (0); /* Status 1 field not yet available. */ if(IrqType == STS_IRQ_RING_STATUS) return (1); /* Status 2 & 3 fields not affected. */ /* Note: At this point, the interrupt is either TRANSMIT or RECEIVE. */ if(ssb->Parm[1] == (unsigned short) -1) return (0); /* Status 2 field not yet available. */ if(ssb->Parm[2] == (unsigned short) -1) return (0); /* Status 3 field not yet available. */ return (1); /* All SSB fields have been written by the adapter. */}/* * Evaluates the command results status in the SSB status field. */static void tms380tr_cmd_status_irq(struct net_device *dev){ struct net_local *tp = netdev_priv(dev); unsigned short ssb_cmd, ssb_parm_0; unsigned short ssb_parm_1; char *open_err = "Open error -"; char *code_err = "Open code -"; /* Copy the ssb values to local variables */ ssb_cmd = tp->ssb.STS; ssb_parm_0 = tp->ssb.Parm[0]; ssb_parm_1 = tp->ssb.Parm[1]; if(ssb_cmd == OPEN) { tp->Sleeping = 0; if(!tp->ReOpenInProgress) wake_up_interruptible(&tp->wait_for_tok_int); tp->OpenCommandIssued = 0; tp->ScbInUse = 0; if((ssb_parm_0 & 0x00FF) == GOOD_COMPLETION) { /* Success, the adapter is open. */ tp->LobeWireFaultLogged = 0; tp->AdapterOpenFlag = 1; tp->AdapterVirtOpenFlag = 1; tp->TransmitCommandActive = 0; tms380tr_exec_cmd(dev, OC_TRANSMIT);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -