📄 dev_i8255x.c
字号:
if (action.ctrl & CB_CTRL_I) d->scb_stat_ack |= SCB_STAT_CX; /* Return to idle state ? */ if (action.ctrl & CB_CTRL_EL) { d->cu_state = CU_STATE_IDLE; d->scb_stat_ack |= SCB_STAT_CNA; break; } else { /* Enter suspended state ? */ if (action.ctrl & CB_CTRL_S) { d->cu_state = CU_STATE_SUSPEND; d->scb_stat_ack |= SCB_STAT_CNA; break; } } /* Go to next descriptor */ d->cu_offset = action.link_offset; } /* Update interrupt status */ dev_i8255x_update_irq_status(d);}/* Resume a Command Block List */static int dev_i8255x_cu_resume(struct i8255x_data *d){ struct i8255x_cu_action action; m_uint32_t cu_addr; /* If we are in idle state, ignore the command */ if (d->cu_state == CU_STATE_IDLE) return(FALSE); cu_addr = d->cu_base + d->cu_offset; /* Check if the previous block has still the S bit set */ dev_i8255x_fetch_cb(d,cu_addr,&action); if (action.ctrl & CB_CTRL_S) return(FALSE); d->cu_offset = action.link_offset; d->cu_state = CU_STATE_LPQ_ACT; dev_i8255x_process_cbl(d); return(TRUE);}/* Dump Statistical counters */static void dev_i8255x_dump_stat_cnt(struct i8255x_data *d){ m_uint32_t counters[I8255X_STAT_CNT_SIZE]; memcpy(counters,d->stat_counters,sizeof(counters)); mem_bswap32(counters,sizeof(counters)); physmem_copy_to_vm(d->vm,counters,d->stat_cnt_addr,sizeof(counters));}/* Process a CU command */static void dev_i8255x_process_cu_cmd(struct i8255x_data *d,u_int cuc){ switch(cuc) { /* No Operation */ case CU_CMD_NOP: break; /* Start */ case CU_CMD_START: d->cu_offset = d->scb_gptr; d->cu_state = CU_STATE_LPQ_ACT; dev_i8255x_process_cbl(d); break; /* Resume */ case CU_CMD_RESUME: dev_i8255x_cu_resume(d); break; /* Load CU base */ case CU_CMD_LOAD_CU_BASE: d->cu_base = d->scb_gptr; break; /* Load Dump Counters Address */ case CU_CMD_LOAD_DUMP_CNT: d->stat_cnt_addr = d->scb_gptr; break; /* Dump Statistical Counters */ case CU_CMD_DUMP_STAT_CNT: dev_i8255x_dump_stat_cnt(d); break; /* Dump Statistical Counters and reset them */ case CU_CMD_DUMP_RST_STAT_CNT: dev_i8255x_dump_stat_cnt(d); memset(d->stat_counters,0,sizeof(d->stat_counters)); break; default: EEPRO_LOG(d,"unsupported CU command 0x%2.2x\n",cuc); }}/* Fetch an RxFD (RX Frame Descriptor) */static void dev_i8255x_fetch_rxfd(struct i8255x_data *d,m_uint32_t addr, struct i8255x_rxfd *rxfd){ physmem_copy_from_vm(d->vm,rxfd,addr,sizeof(*rxfd)); rxfd->ctrl = vmtoh32(rxfd->ctrl); rxfd->link_offset = vmtoh32(rxfd->link_offset); rxfd->rxbd_addr = vmtoh32(rxfd->rxbd_addr); rxfd->rxbd_size = vmtoh32(rxfd->rxbd_size);}/* Fetch an RxBD (Rx Buffer Descriptor) */static void dev_i8255x_fetch_rxbd(struct i8255x_data *d,m_uint32_t addr, struct i8255x_rxbd *rxbd){ physmem_copy_from_vm(d->vm,rxbd,addr,sizeof(*rxbd)); rxbd->ctrl = vmtoh32(rxbd->ctrl); rxbd->rxbd_next = vmtoh32(rxbd->rxbd_next); rxbd->buf_addr = vmtoh32(rxbd->buf_addr); rxbd->buf_size = vmtoh32(rxbd->buf_size);}/* Store a packet */static int dev_i8255x_store_rx_pkt(struct i8255x_data *d, m_uint8_t *pkt,ssize_t pkt_len){ m_uint32_t rxfd_addr,rxbd_addr; m_uint32_t rxfd_next,rxbd_next; m_uint32_t clen,buf_size,norm_len; struct i8255x_rxfd rxfd; struct i8255x_rxbd rxbd; m_uint8_t *pkt_ptr; ssize_t tot_len; /* Fetch the RX Frame descriptor */ rxfd_addr = d->ru_base + d->ru_offset; dev_i8255x_fetch_rxfd(d,rxfd_addr,&rxfd); /* === Simplified mode === */ if (!(rxfd.ctrl & RXFD_CTRL_SF)) { /* Copy the packet data directly into the frame descriptor */ norm_len = normalize_size(pkt_len,4,0); mem_bswap32(pkt,norm_len); physmem_copy_to_vm(d->vm,pkt,rxfd_addr+0x10,norm_len); /* Update the RxFD and generate the appropriate interrupt */ goto update_rxfd; } /* === Flexible mode === */ rxbd_addr = d->ru_base + rxfd.rxbd_addr; pkt_ptr = pkt; tot_len = pkt_len; do { /* Fetch the RX buffer */ dev_i8255x_fetch_rxbd(d,rxbd_addr,&rxbd); rxbd_next = rxbd.rxbd_next; /* Get the current buffer size */ buf_size = rxbd.buf_size & RXFD_SIZE_MASK; clen = m_min(tot_len,buf_size); /* Copy the data into the buffer */ norm_len = normalize_size(clen,4,0); mem_bswap32(pkt_ptr,norm_len); physmem_copy_to_vm(d->vm,pkt_ptr,rxbd.buf_addr,norm_len); pkt_ptr += clen; tot_len -= clen; /* Update RX buffer info */ if (!tot_len) { rxbd.ctrl |= RXBD_CTRL_EOF; clen += 4; /* Add CRC */ } rxbd.ctrl |= RXBD_CTRL_F | clen; physmem_copy_u32_to_vm(d->vm,rxbd_addr+0x00,rxbd.ctrl); }while(tot_len > 0); /* Set the next available RxBD in next RxFD */ rxbd_next = d->ru_base + rxbd.rxbd_next; rxfd_next = d->ru_base + rxfd.link_offset; physmem_copy_u32_to_vm(d->vm,rxfd_next+0x08,rxbd_next); /* Update the RxFD */ update_rxfd: rxfd.ctrl |= RXFD_CTRL_C | RXFD_CTRL_OK; rxfd.rxbd_size &= ~0xFFFF; rxfd.rxbd_size |= RXFD_EOF | (pkt_len + 4); physmem_copy_u32_to_vm(d->vm,rxfd_addr+0x00,rxfd.ctrl); physmem_copy_u32_to_vm(d->vm,rxfd_addr+0x0c,rxfd.rxbd_size); d->stat_counters[STAT_CNT_RX_GOOD]++; /* A frame has been received: generate an IRQ */ d->scb_stat_ack |= SCB_STAT_FR; if (rxfd.ctrl & RXFD_CTRL_EL) { d->ru_state = RU_STATE_NO_RES; d->scb_stat_ack |= SCB_STAT_RNR; } else { if (rxfd.ctrl & RXFD_CTRL_S) { d->ru_state = RU_STATE_SUSPEND; d->scb_stat_ack |= SCB_STAT_RNR; } else { d->ru_offset = rxfd.link_offset; } } dev_i8255x_update_irq_status(d); return(TRUE);}/* Resume reception */static int dev_i8255x_ru_resume(struct i8255x_data *d){ struct i8255x_rxfd rxfd; m_uint32_t rxfd_addr; /* If we are not in ready state, ignore the command */ if (d->ru_state != RU_STATE_READY) return(FALSE); /* Fetch the RX Frame descriptor */ rxfd_addr = d->ru_base + d->ru_offset; dev_i8255x_fetch_rxfd(d,rxfd_addr,&rxfd); /* Check if the previous frame descriptor has still the S bit set */ if (rxfd.ctrl & RXFD_CTRL_S) return(FALSE); d->ru_offset = rxfd.link_offset; d->ru_state = RU_STATE_READY; return(TRUE);}/* Process a RU command */static void dev_i8255x_process_ru_cmd(struct i8255x_data *d,u_int ruc){ switch(ruc) { /* No Operation */ case RU_CMD_NOP: break; /* Start */ case RU_CMD_START: d->ru_offset = d->scb_gptr; d->ru_state = RU_STATE_READY; break; /* Resume */ case RU_CMD_RESUME: dev_i8255x_ru_resume(d); break; /* Load RU base */ case RU_CMD_LOAD_RU_BASE: d->ru_base = d->scb_gptr; break; default: EEPRO_LOG(d,"unsupported RU command 0x%2.2x\n",ruc); }}/* * dev_i8255x_access() */void *dev_i8255x_access(cpu_gen_t *cpu,struct vdevice *dev, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data){ struct i8255x_data *d = dev->priv_data; u_int cuc,ruc,mii_op; if (op_type == MTS_READ) *data = 0x0;#if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " "val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); }#endif EEPRO_LOCK(d); switch(offset) { /* SCB Command Word (interrupt control byte) */ case 0x00: if (op_type == MTS_WRITE) d->scb_ic = *data; break; /* SCB Command Word (command byte) */ case 0x01: if (op_type == MTS_WRITE) { cuc = (*data & SCB_CMD_CUC_MASK) >> SCB_CMD_CUC_SHIFT; ruc = (*data & SCB_CMD_RUC_MASK) >> SCB_CMD_RUC_SHIFT; /* Process CU and RU commands */ dev_i8255x_process_cu_cmd(d,cuc); dev_i8255x_process_ru_cmd(d,ruc); } break; /* SCB Status Word */ case 0x02: if (op_type == MTS_READ) { *data = d->scb_stat_ack << 8; } else { d->scb_stat_ack &= ~(*data >> 8); dev_i8255x_update_irq_status(d); } break; /* SCB General Pointer */ case 0x04: if (op_type == MTS_WRITE) d->scb_gptr = *data; else *data = d->scb_gptr; break; /* MDI control register */ case 0x10: if (op_type == MTS_READ) { mii_op = (d->mii_ctrl & I8255X_MDI_OP_MASK) >> I8255X_MDI_OP_SHIFT; if (mii_op == MII_OPCODE_READ) { d->mii_ctrl &= ~I8255X_MDI_DATA_MASK; d->mii_ctrl |= mii_reg_read(d); } *data = d->mii_ctrl | I8255X_MDI_R; } else { d->mii_ctrl = *data; mii_op = (d->mii_ctrl & I8255X_MDI_OP_MASK) >> I8255X_MDI_OP_SHIFT; if (mii_op == MII_OPCODE_WRITE) mii_reg_write(d); } break;#if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read access to unknown offset=0x%x, " "pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write access to unknown offset=0x%x, pc=0x%llx, " "val=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),*data,op_size); }#endif } EEPRO_UNLOCK(d); return NULL;}/* Handle the RX ring */static int dev_i8255x_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct i8255x_data *d){ int res = FALSE; EEPRO_LOCK(d); if (d->ru_state == RU_STATE_READY) res = dev_i8255x_store_rx_pkt(d,pkt,pkt_len); EEPRO_UNLOCK(d); return(res);}/* * pci_i8255x_read() * * Read a PCI register. */static m_uint32_t pci_i8255x_read(cpu_gen_t *cpu,struct pci_device *dev, int reg){ struct i8255x_data *d = dev->priv_data;#if DEBUG_PCI_REGS EEPRO_LOG(d,"read PCI register 0x%x\n",reg);#endif switch (reg) { case 0x00: return((I8255X_PCI_PRODUCT_ID << 16) | I8255X_PCI_VENDOR_ID); case PCI_REG_BAR0: return(d->dev->phys_addr); case 0x0c: return(0x4000); default: return(0); }}/* * pci_i8255x_write() * * Write a PCI register. */static void pci_i8255x_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value){ struct i8255x_data *d = dev->priv_data;#if DEBUG_PCI_REGS EEPRO_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value);#endif switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); EEPRO_LOG(d,"registers are mapped at 0x%x\n",value); break; }}/* * dev_i8255x_init() */struct i8255x_data *dev_i8255x_init(vm_instance_t *vm,char *name,int interface_type, struct pci_bus *pci_bus,int pci_device,int irq){ struct i8255x_data *d; struct pci_device *pci_dev; struct vdevice *dev; /* Allocate the private data structure for I8255X */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (i8255x): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, I8255X_PCI_VENDOR_ID,I8255X_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_i8255x_read,pci_i8255x_write); if (!pci_dev) { fprintf(stderr,"%s (i8255x): unable to create PCI device.\n",name); goto err_pci_dev; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (i8255x): unable to create device.\n",name); goto err_dev; } d->name = name; d->vm = vm; d->pci_dev = pci_dev; d->dev = dev; dev->phys_addr = 0; dev->phys_len = 0x10000; dev->handler = dev_i8255x_access; dev->priv_data = d; return(d); err_dev: pci_dev_remove(pci_dev); err_pci_dev: free(d); return NULL;}/* Remove an Intel i8255x device */void dev_i8255x_remove(struct i8255x_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 Intel i8255x device */int dev_i8255x_set_nio(struct i8255x_data *d,netio_desc_t *nio){ /* check that a NIO is not already bound */ if (d->nio != NULL) return(-1); d->nio = nio; netio_rxl_add(nio,(netio_rx_handler_t)dev_i8255x_handle_rxring,d,NULL); return(0);}/* Unbind a NIO from an Intel i8255x device */void dev_i8255x_unset_nio(struct i8255x_data *d){ if (d->nio != NULL) { netio_rxl_remove(d->nio); d->nio = NULL; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -