📄 rtl_3c905cx_drv.c
字号:
static int rtl_3COM905C_open (struct rtl_file *filp){ rtl_3COM905C_release (filp); if(rt_3c905c_opened == 0x00){ if((rt_3c905c_dev = rtl_3COM905C_init_device())!=NULL){ sem_init(&rt_3c905x_sem, 0, 0); rtl_3COM905C_start_up_device(rt_3c905c_dev); rt_3c905c_opened = 0x01; return COM3_905C_MAJOR; }else{ rtl_printf("ERROR: Couldn't initialize device 3Com905C-X\n"); return -1; } }else{ rtl_printf("Device 3Com905C-X is already opened\n"); return -1; }}/***************************************************************************************//* This function is used to initialise the pci and the buffering subsystem. *//***************************************************************************************/struct pci_dev *rtl_3COM905C_init_device(void){ struct pci_dev *dev; /* First of all, we must get a pointer to the pci_dev structure */ if((dev = rt_pci_find_device(COM3_VENDOR_ID, COM3_DEVICE_ID, NULL))== NULL) return NULL; rt_3c905x_policy.initialize_rx_buffer(&rt_3c905x_rx_buffer); /* Let's enable the device */ if (rt_pci_enable_device(dev)){ rtl_printf("PCI ERROR: Can't enable device 3Com905C-X\n"); return NULL; } return dev;}/***************************************************************************************//* This function is called when the driver module is inserted. It registers the device *//* , letting other modules to use it by means of the calls read, write, close, open *//* and ioctl. *//***************************************************************************************/int init_module(void){ printk("\n\n\nRT-Linux driver for the Ethernet Card 3Com905c-x being loaded\n\n\n"); if (rtl_register_rtldev (COM3_905C_MAJOR, COM3_905C_NAME, &rtl_3COM905C_fops)) { printk ("RTLinux /dev/%s: unable to get RTLinux major %d\n", COM3_905C_NAME, COM3_905C_MAJOR); return -EIO; }else{ printk("Registered device 3Com905C-X: /dev/%s major number %d\n",COM3_905C_NAME, COM3_905C_MAJOR); rt_3c905c_registered = 0x01; return 0; }}/***************************************************************************************//* This function is called when removing the driver module. It makes sure that there *//* are no pending executions of the interrupt handler. It releases the ethernet card *//* and frees all resources. The driver is unregistered. *//***************************************************************************************/void cleanup_module(void){ int inside = 1; rtl_irqstate_t state; if((rt_3c905c_opened == 0x01) || rt_3c905c_inside_the_interrupt_handler){ rt_3c905c_trying_to_close = 1; /* Since inside the interrupt handler there's a call to the scheduler, there may */ /* be an execution of the interrupt handler that hasn't been completely executed. */ /* The card cannot be released in that case, so we must be sure that there is no */ /* interrupt handler execution pending. Otherwise, that may crash the system. */ while(inside){ rtl_no_interrupts(state); if(rt_3c905c_inside_the_interrupt_handler){ rtl_restore_interrupts(state); usleep(10); }else{ rtl_hard_disable_irq(rt_3c905c_vp.pdev->irq); rtl_restore_interrupts(state); inside = 0; } } rtl_3COM905C_issue_and_wait(UpStall); rtl_3COM905C_issue_and_wait(DownStall); } if(rt_3c905c_registered == 0x01){ printk("Unregistering device /dev/%s\n",COM3_905C_NAME); rtl_unregister_rtldev(COM3_905C_MAJOR,COM3_905C_NAME); } printk("\n\n\nRT-Linux driver for the Ethernet Card 3Com905c-x being removed\n\n\n");}/***************************************************************************************//* This is the interrupt handler. It is executed when the ethernet card raises an *//* interrupt and interrupts are enabled. In the initialisation phase we've forced the *//* card to only generate the UpComplete interrupt, that is, when a full incoming *//* packet has been processed. *//***************************************************************************************/unsigned int boomerang_interrupt(unsigned int irq , struct pt_regs *regs){ long ioaddr = rt_3c905c_vp.ioaddr; int status = inw(ioaddr + EL3_STATUS); int work_done = max_interrupt_work; struct pci_dev *dev = rt_3c905c_dev; rt_3c905c_inside_the_interrupt_handler = 1; rt_3c905c_interrupted++; if(rt_3c905c_writting & rt_3c905c_interrupted) rtl_printf("I've been interrupted %d times\n",rt_3c905c_interrupted); if (status & UpComplete) { rt_3c905c_vp.rx_packets++; rtl_3COM905C_issue_and_wait(UpStall); while(rt_3c905c_vp.rx_ring[rt_3c905c_vp.cur_rx % RX_RING_SIZE].status & 0x00008000){ boomerang_rx(dev); outw(AckIntr | UpComplete, ioaddr + EL3_CMD); if (--work_done < 0) break; } outw(UpUnstall, ioaddr + EL3_CMD); } if (status & DownComplete){ rtl_printf("Se acaba de enviar el paquete\n"); outw(AckIntr | DownComplete, ioaddr + EL3_CMD); } /* Check for all uncommon interrupts at once. */ if (status & (HostError | RxEarly | StatsFull | IntReq)){ rtl_printf("VORTEX_ERROR\n"); vortex_error(dev, status); } /* Acknowledge the IRQ. */ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); if (rt_3c905c_vp.cb_fn_base){ writel(0x8000, rt_3c905c_vp.cb_fn_base + 4); } /* We must be sure that we're out of the interrupt handler before cleanup_modules */ /* is executed. If cleanup_modules is being executed, we don't have to enable the */ /* irq. If enabled, then the system could crash. */ if(!rt_3c905c_trying_to_close) rtl_hard_enable_irq(rt_3c905c_vp.pdev->irq); rt_3c905c_interrupted--; return (rt_3c905c_inside_the_interrupt_handler = 0);}/***************************************************************************************//* This is the function called when an incoming packet has succesfuly arrived, so we *//* have to copy it into internal buffers. *//***************************************************************************************/static int boomerang_rx(struct pci_dev *dev){ int entry = rt_3c905c_vp.cur_rx % RX_RING_SIZE; int rx_status; int rx_work_limit = rt_3c905c_vp.dirty_rx + RX_RING_SIZE - rt_3c905c_vp.cur_rx; int i,j; unsigned char *buffer; int temp = rt_3c905c_vp.rx_ring[entry].status & 0x1fff; unsigned char mine = 0x01, multicast_packet= 0x01, arp_request_for_me = 0x01 ; buffer =rt_3c905c_vp.rx_skbuff[entry]; /* A NIC receives all the packets in the LAN so we will receive an interrupt for each one of those. */ /* As we only want those packets sent to us we must filter them. So, we will only receive packets */ /* directly sent us (i.e. destination address is ours) and those ARP frames which ask for our MAC. */ /* An ARP improvement consist on receive all ARP request packets in the LAN, so, at least, we could */ /* know the pair IP and MAC address of those computers performing the request. As most of the */ /* frames in a LAN are ARP request frames the overhead produced by receiving all of them would be */ /* considerable, so we won't bother with this improvement. */ //Is this frame for us?? for(i=0; i<6; i++){ if(buffer[i] == rt_3c905c_vp.dev_addr[i]) continue; else{ mine = 0x00; break; } } if(mine == 0x01) goto accept_frame; // Is an ARP frame??? if((buffer[12]==0x08) && (buffer[13]==0x06)){ // It asks for my IP?? for(j=0; j<rt_3c905c_n_filters; j++){ for(i=0; i<4;i++){ if(buffer[38+i]==rt_3c905c_ip_addr[j][i]) continue; else{ arp_request_for_me = 0x00; break; } } } }else arp_request_for_me = 0x00; // Is it a multicast frame?? for(i=0; i<6; i++){ if(buffer[i] == 0xff) continue; else{ multicast_packet = 0x00; break; } } accept_frame: if((mine == 0x01) || ((multicast_packet==0x01) && (arp_request_for_me==0x01))){ rt_3c905c_vp.rx_frames_for_us++; if(rt_3c905x_policy.add_frame_to_buffer((void *) &rt_3c905x_rx_buffer,buffer,temp)== 0){ sem_post(&rt_3c905x_sem); rtl_schedule(); } } while ((rx_status = le32_to_cpu(rt_3c905c_vp.rx_ring[entry].status)) & RxDComplete){ if (--rx_work_limit < 0) break; if (!(rx_status & RxDError)) /* Error, update stats. */ rt_3c905c_vp.stats.rx_packets++; } entry = (++rt_3c905c_vp.cur_rx) % RX_RING_SIZE; /* Refill the Rx ring buffers. */ for (; rt_3c905c_vp.cur_rx - rt_3c905c_vp.dirty_rx > 0; rt_3c905c_vp.dirty_rx++) { entry = rt_3c905c_vp.dirty_rx % RX_RING_SIZE; rt_3c905c_vp.rx_ring[entry].status = 0; /* Clear complete bit. */ } return 0;}/***************************************************************************************//* This function does internal initialisations of the card. It writes into internal *//* registers and checks the card capabilities. *//***************************************************************************************/int rtl_3COM905C_start_up_device(struct pci_dev *dev){ struct vortex_chip_info * const vci = &vortex_info; unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */ char *print_name; int retval; long ioaddr; int i,step; print_name = dev ? dev->slot_name : "3c59x"; ioaddr = rt_pci_resource_start(dev, 0); rt_3c905c_vp.drv_flags = vci->drv_flags; rt_3c905c_vp.has_nway = (vci->drv_flags & HAS_NWAY) ? 1 : 0; rt_3c905c_vp.io_size = vci->io_size; rt_3c905c_vp.card_idx = 0; rt_3c905c_vp.ioaddr = ioaddr; rt_3c905c_vp.media_override = 7; rt_3c905c_vp.mtu = mtu; print_name = dev ? dev->slot_name : "3c59x"; /* PCI-only startup logic */ if (dev) { if (request_region(ioaddr, vci->io_size, print_name) != NULL){ rt_3c905c_vp.must_free_region = 1; } /* enable bus-mastering if necessary */ if (vci->flags & PCI_USES_MASTER) pci_set_master (dev); rt_3c905c_vp.pdev = dev; /* Makes sure rings are at least 16 byte aligned. */ rt_3c905c_vp.rx_ring = pci_alloc_consistent(dev, sizeof(struct boom_rx_desc) * RX_RING_SIZE, &rt_3c905c_vp.rx_ring_dma); rt_3c905c_vp.tx_ring = pci_alloc_consistent(dev, sizeof(struct boom_tx_desc) * TX_RING_SIZE, &rt_3c905c_vp.tx_ring_dma); retval = -ENOMEM; if ((rt_3c905c_vp.rx_ring == 0) || (rt_3c905c_vp.tx_ring == 0)) goto free_region; EL3WINDOW(0); { int base; if (vci->drv_flags & EEPROM_8BIT) base = 0x230; else if (vci->drv_flags & EEPROM_OFFSET) base = EEPROM_Read + 0x30; else base = EEPROM_Read; for (i = 0; i < 0x40; i++) { int timer; /* This means that we want to read EepromCommand Register and disable writting */ /* Issuing ReadRegister & WriteDisable */ outw(base + i, ioaddr + Wn0EepromCmd); for (timer = 10; timer >= 0; timer--) { /* The read data is available through the EepromData register 162us after */ /* the ReadRegister command has been issued */ rtl_delay(162000); /* Bit 15th (eepromBusy) of EepromCommand register is a read-only bit asserted */ /* during the execution of EEProm commands. Further commans should not be issued */ /* to the EepromCommand register, nor should data be read from the EepromData */ /* register while this bit is true */ if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0) break; } /* Save the contents of the 3C90xC NIC's EEPROM */ eeprom[i] = inw(ioaddr + Wn0EepromData); } }//EL3WINDOW(0) configuration finished /* EEPROM can be checksummed in order to assure that reading was OK */ for (i = 0; i < 0x18; i++) checksum ^= eeprom[i]; checksum = (checksum ^ (checksum >> 8)) & 0xff; if (checksum != 0x00) { /* Grrr, needless incompatible change 3Com. */ while (i < 0x21) checksum ^= eeprom[i++]; checksum = (checksum ^ (checksum >> 8)) & 0xff; } if ((checksum != 0x00) && !(vci->drv_flags & IS_TORNADO)) printk(" ***INVALID CHECKSUM %4.4x*** ", checksum); /* Save HW address into dev_addr (MAC address in format 00:04:75:bd:ea:e7) */ for (i = 0; i < 3; i++) ((u16 *)rt_3c905c_vp.dev_addr)[i] = htons(eeprom[i + 10]); /* This writes into the StationAddress register the NIC's HW address in order */ /* to define the individual destination address that the NIC responds to when */ /* receiving packets */ EL3WINDOW(2); for (i = 0; i < 6; i++) outb(rt_3c905c_vp.dev_addr[i], ioaddr + i); EL3WINDOW(4); step = (inb(ioaddr + Wn4_NetDiag) & 0x1e) >> 1; if (dev && vci->drv_flags & HAS_CB_FNS) { unsigned long fn_st_addr; /* Cardbus function status space */ unsigned short n; fn_st_addr = pci_resource_start (dev, 2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -