📄 dev_am79c971.c
字号:
if (op_type == MTS_WRITE) { d->rap = *data & 0xFF; } else { *data = d->rap; } break; case 0x10: /* RDP (Register Data Port) */ am79c971_rdp_access(cpu,d,op_type,data); break; case 0x1c: /* BDP (BCR Data Port) */ am79c971_bdp_access(cpu,d,op_type,data); break; } return NULL;}/* Read a RX descriptor */static int rxdesc_read(struct am79c971_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd){ m_uint32_t buf[4]; m_uint8_t sw_style; /* Get the software style */ sw_style = d->bcr[20]; /* Read the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,&buf,rxd_addr,sizeof(struct rx_desc)); switch(sw_style) { case 2: rxd->rmd[0] = vmtoh32(buf[0]); /* rb addr */ rxd->rmd[1] = vmtoh32(buf[1]); /* own flag, ... */ rxd->rmd[2] = vmtoh32(buf[2]); /* rfrtag, mcnt, ... */ rxd->rmd[3] = vmtoh32(buf[3]); /* user */ break; case 3: rxd->rmd[0] = vmtoh32(buf[2]); /* rb addr */ rxd->rmd[1] = vmtoh32(buf[1]); /* own flag, ... */ rxd->rmd[2] = vmtoh32(buf[0]); /* rfrtag, mcnt, ... */ rxd->rmd[3] = vmtoh32(buf[3]); /* user */ break; default: AM79C971_LOG(d,"invalid software style %u!\n",sw_style); return(-1); } return(0);}/* Set the address of the next RX descriptor */static inline void rxdesc_set_next(struct am79c971_data *d){ d->rx_pos++; if (d->rx_pos == d->rx_len) d->rx_pos = 0;}/* Compute the address of the current RX descriptor */static inline m_uint32_t rxdesc_get_current(struct am79c971_data *d){ return(d->rx_start + (d->rx_pos * sizeof(struct rx_desc)));}/* Put a packet in buffer of a descriptor */static void rxdesc_put_pkt(struct am79c971_data *d,struct rx_desc *rxd, u_char **pkt,ssize_t *pkt_len){ ssize_t len,cp_len; /* Compute the data length to copy */ len = ~((rxd->rmd[1] & AM79C971_RMD1_LEN) - 1); len &= AM79C971_RMD1_LEN; cp_len = m_min(len,*pkt_len); /* Copy packet data to the VM physical RAM */#if DEBUG_RECEIVE AM79C971_LOG(d,"am79c971_handle_rxring: storing %u bytes at 0x%8.8x\n", cp_len, rxd->rmd[0]);#endif physmem_copy_to_vm(d->vm,*pkt,rxd->rmd[0],cp_len); *pkt += cp_len; *pkt_len -= cp_len;}/* * Put a packet in the RX ring. */static int am79c971_receive_pkt(struct am79c971_data *d, u_char *pkt,ssize_t pkt_len){ m_uint32_t rx_start,rx_current,rx_next,rxdn_rmd1; struct rx_desc rxd0,rxdn,*rxdc; ssize_t tot_len = pkt_len; u_char *pkt_ptr = pkt; m_uint8_t sw_style; int i; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,AM79C971_MAX_PKT_SIZE); /* Copy the current rxring descriptor */ rx_start = rx_current = rxdesc_get_current(d); rxdesc_read(d,rx_start,&rxd0); /* We must have the first descriptor... */ if (!(rxd0.rmd[1] & AM79C971_RMD1_OWN)) return(FALSE); for(i=0,rxdc=&rxd0;;i++) {#if DEBUG_RECEIVE AM79C971_LOG(d,"am79c971_handle_rxring: i=%d, addr=0x%8.8x: " "rmd[0]=0x%x, rmd[1]=0x%x, rmd[2]=0x%x, rmd[3]=0x%x\n", i,rx_current, rxdc->rmd[0],rxdc->rmd[1],rxdc->rmd[2],rxdc->rmd[3]);#endif /* Put data into the descriptor buffer */ rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Go to the next descriptor */ rxdesc_set_next(d); /* If this is not the first descriptor, clear the OWN bit */ if (i != 0) rxdc->rmd[1] &= ~AM79C971_RMD1_OWN; /* If we have finished, mark the descriptor as end of packet */ if (tot_len == 0) { rxdc->rmd[1] |= AM79C971_RMD1_ENP; physmem_copy_u32_to_vm(d->vm,rx_current+4,rxdc->rmd[1]); /* Get the software style */ sw_style = d->bcr[20]; /* Update the message byte count field */ rxdc->rmd[2] &= ~AM79C971_RMD2_LEN; rxdc->rmd[2] |= pkt_len + 4; switch(sw_style) { case 2: physmem_copy_u32_to_vm(d->vm,rx_current+8,rxdc->rmd[2]); break; case 3: physmem_copy_u32_to_vm(d->vm,rx_current,rxdc->rmd[2]); break; default: AM79C971_LOG(d,"invalid software style %u!\n",sw_style); } break; } /* Try to acquire the next descriptor */ rx_next = rxdesc_get_current(d); rxdn_rmd1 = physmem_copy_u32_from_vm(d->vm,rx_next+4); if (!(rxdn_rmd1 & AM79C971_RMD1_OWN)) { rxdc->rmd[1] |= AM79C971_RMD1_ERR | AM79C971_RMD1_BUFF; rxdc->rmd[1] |= AM79C971_RMD1_ENP; physmem_copy_u32_to_vm(d->vm,rx_current+4,rxdc->rmd[1]); break; } /* Update rmd1 to store change of OWN bit */ physmem_copy_u32_to_vm(d->vm,rx_current+4,rxdc->rmd[1]); /* Read the next descriptor from VM physical RAM */ rxdesc_read(d,rx_next,&rxdn); rxdc = &rxdn; rx_current = rx_next; } /* Update the first RX descriptor */ rxd0.rmd[1] &= ~AM79C971_RMD1_OWN; rxd0.rmd[1] |= AM79C971_RMD1_STP; physmem_copy_u32_to_vm(d->vm,rx_start+4,rxd0.rmd[1]); d->csr[0] |= AM79C971_CSR0_RINT; am79c971_update_intr_flag(d); am79c971_trigger_irq(d); return(TRUE);}/* Handle the RX ring */static int am79c971_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct am79c971_data *d){ n_eth_hdr_t *hdr; /* * Don't start receive if the RX ring address has not been set * and if RX ON is not set. */ if ((d->rx_start == 0) || !(d->csr[0] & AM79C971_CSR0_TXON)) return(FALSE);#if DEBUG_RECEIVE AM79C971_LOG(d,"receiving a packet of %d bytes\n",pkt_len); mem_dump(log_file,pkt,pkt_len);#endif /* * Receive only multicast/broadcast trafic + unicast traffic * for this virtual machine. */ hdr = (n_eth_hdr_t *)pkt; if (am79c971_handle_mac_addr(d,pkt)) am79c971_receive_pkt(d,pkt,pkt_len); return(TRUE);}/* Read a TX descriptor */static int txdesc_read(struct am79c971_data *d,m_uint32_t txd_addr, struct tx_desc *txd){ m_uint32_t buf[4]; m_uint8_t sw_style; /* Get the software style */ sw_style = d->bcr[20]; /* Read the descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,&buf,txd_addr,sizeof(struct tx_desc)); switch(sw_style) { case 2: txd->tmd[0] = vmtoh32(buf[0]); /* tb addr */ txd->tmd[1] = vmtoh32(buf[1]); /* own flag, ... */ txd->tmd[2] = vmtoh32(buf[2]); /* buff, uflo, ... */ txd->tmd[3] = vmtoh32(buf[3]); /* user */ break; case 3: txd->tmd[0] = vmtoh32(buf[2]); /* tb addr */ txd->tmd[1] = vmtoh32(buf[1]); /* own flag, ... */ txd->tmd[2] = vmtoh32(buf[0]); /* buff, uflo, ... */ txd->tmd[3] = vmtoh32(buf[3]); /* user */ break; default: AM79C971_LOG(d,"invalid software style %u!\n",sw_style); return(-1); } return(0);}/* Set the address of the next TX descriptor */static inline void txdesc_set_next(struct am79c971_data *d){ d->tx_pos++; if (d->tx_pos == d->tx_len) d->tx_pos = 0;}/* Compute the address of the current TX descriptor */static inline m_uint32_t txdesc_get_current(struct am79c971_data *d){ return(d->tx_start + (d->tx_pos * sizeof(struct tx_desc)));}/* Handle the TX ring (single packet) */static int am79c971_handle_txring_single(struct am79c971_data *d){ u_char pkt[AM79C971_MAX_PKT_SIZE],*pkt_ptr; struct tx_desc txd0,ctxd,ntxd,*ptxd; m_uint32_t tx_start,tx_current; m_uint32_t clen,tot_len; if ((d->tx_start == 0) || !(d->csr[0] & AM79C971_CSR0_TXON)) return(FALSE); /* Copy the current txring descriptor */ tx_start = tx_current = txdesc_get_current(d); ptxd = &txd0; txdesc_read(d,tx_start,ptxd); /* If we don't own the first descriptor, we cannot transmit */ if (!(ptxd->tmd[1] & AM79C971_TMD1_OWN)) return(FALSE); #if DEBUG_TRANSMIT AM79C971_LOG(d,"am79c971_handle_txring: 1st desc: " "tmd[0]=0x%x, tmd[1]=0x%x, tmd[2]=0x%x, tmd[3]=0x%x\n", ptxd->tmd[0],ptxd->tmd[1],ptxd->tmd[2],ptxd->tmd[3]);#endif /* Empty packet for now */ pkt_ptr = pkt; tot_len = 0; for(;;) {#if DEBUG_TRANSMIT AM79C971_LOG(d,"am79c971_handle_txring: loop: " "tmd[0]=0x%x, tmd[1]=0x%x, tmd[2]=0x%x, tmd[3]=0x%x\n", ptxd->tmd[0],ptxd->tmd[1],ptxd->tmd[2],ptxd->tmd[3]);#endif /* Copy packet data */ clen = ~((ptxd->tmd[1] & AM79C971_TMD1_LEN) - 1); clen &= AM79C971_TMD1_LEN; physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tmd[0],clen); pkt_ptr += clen; tot_len += clen; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->tmd[1] & AM79C971_TMD1_STP)) { ptxd->tmd[1] &= ~AM79C971_TMD1_OWN; physmem_copy_u32_to_vm(d->vm,tx_current+4,ptxd->tmd[1]); } /* Set the next descriptor */ txdesc_set_next(d); /* Stop now if end of packet has been reached */ if (ptxd->tmd[1] & AM79C971_TMD1_ENP) break; /* Read the next descriptor and try to acquire it */ tx_current = txdesc_get_current(d); txdesc_read(d,tx_current,&ntxd); if (!(ntxd.tmd[1] & AM79C971_TMD1_OWN)) { AM79C971_LOG(d,"am79c971_handle_txring: UNDERFLOW!\n"); return(FALSE); } memcpy(&ctxd,&ntxd,sizeof(struct tx_desc)); ptxd = &ctxd; } if (tot_len != 0) {#if DEBUG_TRANSMIT AM79C971_LOG(d,"sending packet of %u bytes\n",tot_len); mem_dump(log_file,pkt,tot_len);#endif /* send it on wire */ netio_send(d->nio,pkt,tot_len); } /* Clear the OWN flag of the first descriptor */ txd0.tmd[1] &= ~AM79C971_TMD1_OWN; physmem_copy_u32_to_vm(d->vm,tx_start+4,txd0.tmd[1]); /* Generate TX interrupt */ d->csr[0] |= AM79C971_CSR0_TINT; am79c971_update_intr_flag(d); am79c971_trigger_irq(d); return(TRUE);}/* Handle the TX ring */static int am79c971_handle_txring(struct am79c971_data *d){ int i; for(i=0;i<AM79C971_TXRING_PASS_COUNT;i++) if (!am79c971_handle_txring_single(d)) break; return(TRUE);}/* * pci_am79c971_read() * * Read a PCI register. */static m_uint32_t pci_am79c971_read(cpu_mips_t *cpu,struct pci_device *dev, int reg){ struct am79c971_data *d = dev->priv_data;#if DEBUG_PCI_REGS AM79C971_LOG(d,"read PCI register 0x%x\n",reg);#endif switch (reg) { case 0x00: return((AM79C971_PCI_PRODUCT_ID << 16) | AM79C971_PCI_VENDOR_ID); case 0x08: return(0x02000002); case PCI_REG_BAR1: return(d->dev->phys_addr); default: return(0); }}/* * pci_am79c971_write() * * Write a PCI register. */static void pci_am79c971_write(cpu_mips_t *cpu,struct pci_device *dev, int reg,m_uint32_t value){ struct am79c971_data *d = dev->priv_data;#if DEBUG_PCI_REGS AM79C971_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value);#endif switch(reg) { case PCI_REG_BAR1: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); AM79C971_LOG(d,"registers are mapped at 0x%x\n",value); break; }}/* * dev_am79c971_init() * * Generic AMD Am79c971 initialization code. */struct am79c971_data *dev_am79c971_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq){ struct am79c971_data *d; struct pci_device *pci_dev; struct vdevice *dev; /* Allocate the private data structure for AM79C971 */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (AM79C971): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); memcpy(d->mii_regs[0],mii_reg_values,sizeof(mii_reg_values)); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, AM79C971_PCI_VENDOR_ID,AM79C971_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_am79c971_read,pci_am79c971_write); if (!pci_dev) { fprintf(stderr,"%s (AM79C971): unable to create PCI device.\n",name); goto err_pci_dev; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (AM79C971): unable to create device.\n",name); goto err_dev; } d->name = name; d->vm = vm; d->type = interface_type; d->pci_dev = pci_dev; d->dev = dev; dev->phys_addr = 0; dev->phys_len = 0x4000; dev->handler = dev_am79c971_access; dev->priv_data = d; return(d); err_dev: pci_dev_remove(pci_dev); err_pci_dev: free(d); return NULL;}/* Remove an AMD Am79c971 device */void dev_am79c971_remove(struct am79c971_data *d){ if (d != NULL) { pci_dev_remove(d->pci_dev); vm_unbind_device(d->vm,d->dev); cpu_group_rebuild_mts(d->vm->cpu_group); free(d->dev); free(d); }}/* Bind a NIO to an AMD Am79c971 device */int dev_am79c971_set_nio(struct am79c971_data *d,netio_desc_t *nio){ /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)am79c971_handle_txring,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)am79c971_handle_rxring,d,NULL); return(0);}/* Unbind a NIO from an AMD Am79c971 device */void dev_am79c971_unset_nio(struct am79c971_data *d){ if (d->nio != NULL) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -