/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * Serial Interfaces (Mueslix). * * Note: "debug serial mueslix" gives more technical info. * * Chip mode: Cisco models 36xx and 72xx don't seem to use the same microcode, * so there are code variants to make things work properly. * * Chip mode 0 => 3600 * Chip mode 1 => 7200 * * 2 points noticed until now: * - RX/TX ring wrapping checks are done differently, * - TX packet sizes are not specified in the same way. * * Test methodology: * - Connect two virtual routers together ; * - Do pings by sending 10 packets by 10 packets. If this stops working, * count the number of transmitted packets and check with RX/TX rings * sizes. This is problably a ring wrapping problem. * - Do multiple pings with various sizes (padding checks); * - Check if CDP is working, with various hostname sizes. Since CDP * contains a checksum, it is a good way to determine if packets are * sent/received correctly. * - Do a Telnet from both virtual router to the other one, and do a * "sh run". */ #include #include #include #include #include #include #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "net.h" #include "net_io.h" #include "ptask.h" #include "dev_mueslix.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_PCI_REGS 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 /* Mueslix PCI vendor/product codes */ #define MUESLIX_PCI_VENDOR_ID 0x1137 #define MUESLIX_PCI_PRODUCT_ID 0x0001 /* Number of channels (4 interfaces) */ #define MUESLIX_NR_CHANNELS 4 #define MUESLIX_CHANNEL_LEN 0x100 /* RX/TX status for a channel */ #define MUESLIX_CHANNEL_STATUS_RX 0x01 #define MUESLIX_CHANNEL_STATUS_TX 0x02 /* RX/TX enable masks (XXX check if bit position is correct) */ #define MUESLIX_TX_ENABLE 0x01 #define MUESLIX_RX_ENABLE 0x02 /* RX/TX IRQ masks */ #define MUESLIX_TX_IRQ 0x01 #define MUESLIX_RX_IRQ 0x10 /* Addresses of ports */ #define MUESLIX_CHANNEL0_OFFSET 0x100 #define MUESLIX_CHANNEL1_OFFSET 0x200 #define MUESLIX_CHANNEL2_OFFSET 0x300 #define MUESLIX_CHANNEL3_OFFSET 0x400 /* TPU Registers */ #define MUESLIX_TPU_CMD_OFFSET 0x2c24 #define MUESLIX_TPU_CMD_RSP_OFFSET 0x2c2c /* General and channels registers */ #define MUESLIX_GEN_CHAN_LEN 0x500 /* TPU microcode */ #define MUESLIX_UCODE_OFFSET 0x2000 #define MUESLIX_UCODE_LEN 0x800 /* TPU Xmem and YMem */ #define MUESLIX_XMEM_OFFSET 0x2a00 #define MUESLIX_YMEM_OFFSET 0x2b00 #define MUESLIX_XYMEM_LEN 0x100 /* Maximum packet size */ #define MUESLIX_MAX_PKT_SIZE 18000 /* Send up to 16 packets in a TX ring scan pass */ #define MUESLIX_TXRING_PASS_COUNT 16 /* RX descriptors */ #define MUESLIX_RXDESC_OWN 0x80000000 /* Ownership */ #define MUESLIX_RXDESC_FS 0x40000000 /* First Segment */ #define MUESLIX_RXDESC_LS 0x20000000 /* Last Segment */ #define MUESLIX_RXDESC_OVERRUN 0x10000000 /* Overrun */ #define MUESLIX_RXDESC_IGNORED 0x08000000 /* Ignored */ #define MUESLIX_RXDESC_ABORT 0x04000000 /* Abort */ #define MUESLIX_RXDESC_CRC 0x02000000 /* CRC error */ #define MUESLIX_RXDESC_LEN_MASK 0xffff /* TX descriptors */ #define MUESLIX_TXDESC_OWN 0x80000000 /* Ownership */ #define MUESLIX_TXDESC_FS 0x40000000 /* First Segment */ #define MUESLIX_TXDESC_LS 0x20000000 /* Last Segment */ #define MUESLIX_TXDESC_SUB 0x00100000 /* Length substractor ? */ #define MUESLIX_TXDESC_SUB_LEN 0x03000000 /* Length substrator ? */ #define MUESLIX_TXDESC_SUB_SHIFT 24 #define MUESLIX_TXDESC_PAD 0x00c00000 /* Sort of padding info ? */ #define MUESLIX_TXDESC_PAD_SHIFT 22 #define MUESLIX_TXDESC_LEN_MASK 0xffff /* RX Descriptor */ struct rx_desc { m_uint32_t rdes[2]; }; /* TX Descriptor */ struct tx_desc { m_uint32_t tdes[2]; }; /* Forward declaration of Mueslix data */ typedef struct mueslix_data mueslix_data_t; /* Mueslix channel */ struct mueslix_channel { /* Channel ID */ u_int id; /* Channel status (0=disabled) */ u_int status; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanners task id */ ptask_id_t tx_tid; /* physical addresses for start and end of RX/TX rings */ m_uint32_t rx_start,rx_end,tx_start,tx_end; /* physical addresses of current RX and TX descriptors */ m_uint32_t rx_current,tx_current; /* Parent mueslix structure */ mueslix_data_t *parent; }; /* Mueslix Data */ struct mueslix_data { char *name; /* Lock */ pthread_mutex_t lock; /* IRQ status and mask */ m_uint32_t irq_status,irq_mask; u_int irq_clearing_count; /* TPU options */ m_uint32_t tpu_options; /* Virtual machine */ vm_instance_t *vm; /* Virtual device */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev; /* Chip mode: * * 0=increment ring pointers before check + direct TX size, * 1=increment ring pointers after check + "complex" TX size. */ int chip_mode; /* Channels */ struct mueslix_channel channel[MUESLIX_NR_CHANNELS]; m_uint32_t channel_enable_mask; /* TPU microcode */ u_char ucode[MUESLIX_UCODE_LEN]; /* TPU Xmem and Ymem */ u_char xmem[MUESLIX_XYMEM_LEN]; u_char ymem[MUESLIX_XYMEM_LEN]; }; /* Offsets of the 4 channels */ static m_uint32_t channel_offset[MUESLIX_NR_CHANNELS] = { MUESLIX_CHANNEL0_OFFSET, MUESLIX_CHANNEL1_OFFSET, MUESLIX_CHANNEL2_OFFSET, MUESLIX_CHANNEL3_OFFSET, }; /* Lock/Unlock primitives */ #define MUESLIX_LOCK(d) pthread_mutex_lock(&(d)->lock) #define MUESLIX_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) /* Log a Mueslix message */ #define MUESLIX_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Returns TRUE if RX/TX is enabled for a channel */ static inline int dev_mueslix_is_rx_tx_enabled(struct mueslix_data *d,u_int id) { /* 2 bits for RX/TX, 4 channels max */ return((d->channel_enable_mask >> (id << 1)) & 0x03); } /* Update IRQ status */ static inline void dev_mueslix_update_irq_status(struct mueslix_data *d) { if (d->irq_status & d->irq_mask) pci_dev_trigger_irq(d->vm,d->pci_dev); else { if (++d->irq_clearing_count == 3) { pci_dev_clear_irq(d->vm,d->pci_dev); d->irq_clearing_count = 0; } } } /* * Access to channel registers. */ void dev_mueslix_chan_access(cpu_gen_t *cpu,struct mueslix_channel *channel, m_uint32_t offset,u_int op_size,u_int op_type, m_uint64_t *data) { switch(offset) { case 0x60: /* signals ? */ if ((op_type == MTS_READ) && (channel->nio != NULL)) *data = 0xFFFFFFFF; break; case 0x64: /* port status - cable type and probably other things */ if (op_type == MTS_READ) *data = 0x7B; break; case 0x90: /* has influence on clock rate */ if (op_type == MTS_READ) *data = 0x11111111; break; case 0x80: /* TX start */ if (op_type == MTS_WRITE) channel->tx_start = channel->tx_current = *data; else *data = channel->tx_start; break; case 0x84: /* TX end */ if (op_type == MTS_WRITE) channel->tx_end = *data; else *data = channel->tx_end; break; case 0x88: /* RX start */ if (op_type == MTS_WRITE) channel->rx_start = channel->rx_current = *data; else *data = channel->rx_start; break; case 0x8c: /* RX end */ if (op_type == MTS_WRITE) channel->rx_end = *data; else *data = channel->rx_end; break; } } /* Handle TPU commands for chip mode 0 (3600) */ static void tpu_cm0_handle_cmd(struct mueslix_data *d,u_int cmd) { struct mueslix_channel *channel; u_int opcode,channel_id; opcode = (cmd >> 12) & 0xFF; channel_id = cmd & 0x03; channel = &d->channel[channel_id]; switch(opcode) { case 0x10: MUESLIX_LOG(d,"channel %u disabled\n",channel_id); channel->status = 0; break; case 0x00: MUESLIX_LOG(d,"channel %u enabled\n",channel_id); channel->status = 1; break; default: MUESLIX_LOG(d,"unknown command 0x%5x\n",cmd); } } /* Handle TPU commands for chip mode 1 (7200) */ static void tpu_cm1_handle_cmd(struct mueslix_data *d,u_int cmd) { struct mueslix_channel *channel; u_int opcode,channel_id; opcode = (cmd >> 12) & 0xFF; channel_id = cmd & 0x03; channel = &d->channel[channel_id]; switch(opcode) { case 0x50: case 0x30: MUESLIX_LOG(d,"channel %u disabled\n",channel_id); channel->status = 0; break; case 0x00: MUESLIX_LOG(d,"channel %u enabled\n",channel_id); channel->status = 1; break; default: MUESLIX_LOG(d,"unknown command 0x%5x\n",cmd); } } /* * dev_mueslix_access() */ void *dev_mueslix_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 mueslix_data *d = dev->priv_data; int i; #if DEBUG_ACCESS >= 2 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 /* Returns 0 if we don't know the offset */ if (op_type == MTS_READ) *data = 0x00000000; /* Handle microcode access */ if ((offset >= MUESLIX_UCODE_OFFSET) && (offset < (MUESLIX_UCODE_OFFSET + MUESLIX_UCODE_LEN))) return(d->ucode + offset - MUESLIX_UCODE_OFFSET); /* Handle TPU XMem access */ if ((offset >= MUESLIX_XMEM_OFFSET) && (offset < (MUESLIX_XMEM_OFFSET + MUESLIX_XYMEM_LEN))) return(d->xmem + offset - MUESLIX_XMEM_OFFSET); /* Handle TPU YMem access */ if ((offset >= MUESLIX_YMEM_OFFSET) && (offset < (MUESLIX_YMEM_OFFSET + MUESLIX_XYMEM_LEN))) return(d->ymem + offset - MUESLIX_YMEM_OFFSET); /* Handle channel access */ for(i=0;i= channel_offset[i]) && (offset < (channel_offset[i] + MUESLIX_CHANNEL_LEN))) { MUESLIX_LOCK(d); dev_mueslix_chan_access(cpu,&d->channel[i], offset - channel_offset[i], op_size,op_type,data); MUESLIX_UNLOCK(d); return NULL; } MUESLIX_LOCK(d); /* Generic case */ switch(offset) { /* this reg is accessed when an interrupt occurs */ case 0x0: if (op_type == MTS_READ) { *data = d->irq_status; } else { d->irq_status &= ~(*data); dev_mueslix_update_irq_status(d); } break; /* Maybe interrupt mask */ case 0x10: if (op_type == MTS_READ) { *data = d->irq_mask; } else { d->irq_mask = *data; dev_mueslix_update_irq_status(d); } break; case 0x14: if (op_type == MTS_READ) *data = d->channel_enable_mask; else { #if DEBUG_ACCESS cpu_log(cpu,d->name, "channel_enable_mask = 0x%5.5llx at pc=0x%llx\n", *data,cpu_get_pc(cpu)); #endif d->channel_enable_mask = *data; } break; case 0x18: if (op_type == MTS_READ) *data = 0x7F7F7F7F; break; case 0x48: if (op_type == MTS_READ) *data = 0x00000000; break; case 0x7c: if (op_type == MTS_READ) *data = 0x492; break; case 0x2c00: if (op_type == MTS_READ) *data = d->tpu_options; else d->tpu_options = *data; break; /* cmd reg */ case MUESLIX_TPU_CMD_OFFSET: #if DEBUG_ACCESS if (op_type == MTS_WRITE) { cpu_log(cpu,d->name,"cmd_reg = 0x%5.5llx at pc=0x%llx\n", *data,cpu_get_pc(cpu)); } #endif switch(d->chip_mode) { case 0: /* 3600 */ tpu_cm0_handle_cmd(d,*data); break; case 1: /* 7200 */ tpu_cm1_handle_cmd(d,*data); break; } break; /* * cmd_rsp reg, it seems that 0xFFFF means OK * (seen on a "sh contr se1/0" with "debug serial mueslix" enabled). */ case MUESLIX_TPU_CMD_RSP_OFFSET: if (op_type == MTS_READ) *data = 0xFFFF; break; #if DEBUG_UNKNOWN default: if (op_type == MTS_READ) { cpu_log(cpu,d->name, "read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu_get_pc(cpu),op_size); } else { cpu_log(cpu,d->name, "write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n", offset,*data,cpu_get_pc(cpu),op_size); } #endif } MUESLIX_UNLOCK(d); return NULL; } /* * Get the address of the next RX descriptor. */ static m_uint32_t rxdesc_get_next(struct mueslix_channel *channel, m_uint32_t rxd_addr) { m_uint32_t nrxd_addr; switch(channel->parent->chip_mode) { case 0: nrxd_addr = rxd_addr + sizeof(struct rx_desc); if (nrxd_addr == channel->rx_end) nrxd_addr = channel->rx_start; break; case 1: default: if (rxd_addr == channel->rx_end) nrxd_addr = channel->rx_start; else nrxd_addr = rxd_addr + sizeof(struct rx_desc); break; } return(nrxd_addr); } /* Read an RX descriptor */ static void rxdesc_read(struct mueslix_data *d,m_uint32_t rxd_addr, struct rx_desc *rxd) { #if DEBUG_RECEIVE MUESLIX_LOG(d,"reading RX descriptor at address 0x%x\n",rxd_addr); #endif /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); /* byte-swapping */ rxd->rdes[0] = vmtoh32(rxd->rdes[0]); rxd->rdes[1] = vmtoh32(rxd->rdes[1]); } /* * Try to acquire the specified RX descriptor. Returns TRUE if we have it. * It assumes that the byte-swapping is done. */ static inline int rxdesc_acquire(m_uint32_t rdes0) { return(rdes0 & MUESLIX_RXDESC_OWN); } /* Put a packet in buffer of a descriptor */ static ssize_t rxdesc_put_pkt(struct mueslix_data *d,struct rx_desc *rxd, u_char **pkt,ssize_t *pkt_len) { ssize_t len,cp_len; len = rxd->rdes[0] & MUESLIX_RXDESC_LEN_MASK; /* compute the data length to copy */ cp_len = m_min(len,*pkt_len); #if DEBUG_RECEIVE MUESLIX_LOG(d,"copying %d bytes at 0x%x\n",cp_len,rxd->rdes[1]); #endif /* copy packet data to the VM physical RAM */ physmem_copy_to_vm(d->vm,*pkt,rxd->rdes[1],cp_len); *pkt += cp_len; *pkt_len -= cp_len; return(cp_len); } /* * Put a packet in the RX ring of the Mueslix specified channel. */ static void dev_mueslix_receive_pkt(struct mueslix_channel *channel, u_char *pkt,ssize_t pkt_len) { struct mueslix_data *d = channel->parent; m_uint32_t rx_start,rxdn_addr,rxdn_rdes0; struct rx_desc rxd0,rxdn,*rxdc; ssize_t cp_len,tot_len = pkt_len; u_char *pkt_ptr = pkt; int i; if ((channel->rx_start == 0) || (channel->status == 0) || (channel->nio == NULL)) return; /* Don't make anything if RX is not enabled for this channel */ if (!(dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_RX_ENABLE)) return; /* Truncate the packet if it is too big */ pkt_len = m_min(pkt_len,MUESLIX_MAX_PKT_SIZE); /* Copy the current rxring descriptor */ rxdesc_read(d,channel->rx_current,&rxd0); /* We must have the first descriptor... */ if (!rxdesc_acquire(rxd0.rdes[0])) return; /* Remember the first RX descriptor address */ rx_start = channel->rx_current; for(i=0,rxdc=&rxd0;tot_len>0;i++) { /* Put data into the descriptor buffers */ cp_len = rxdesc_put_pkt(d,rxdc,&pkt_ptr,&tot_len); /* Get address of the next descriptor */ rxdn_addr = rxdesc_get_next(channel,channel->rx_current); /* We have finished if the complete packet has been stored */ if (tot_len == 0) { rxdc->rdes[0] = MUESLIX_RXDESC_LS; rxdc->rdes[0] |= cp_len; if (i != 0) physmem_copy_u32_to_vm(d->vm,channel->rx_current,rxdc->rdes[0]); channel->rx_current = rxdn_addr; break; } #if DEBUG_RECEIVE MUESLIX_LOG(d,"trying to acquire new descriptor at 0x%x\n",rxdn_addr); #endif /* Get status of the next descriptor to see if we can acquire it */ rxdn_rdes0 = physmem_copy_u32_from_vm(d->vm,rxdn_addr); if (!rxdesc_acquire(rxdn_rdes0)) rxdc->rdes[0] = MUESLIX_RXDESC_LS | MUESLIX_RXDESC_OVERRUN; else rxdc->rdes[0] = 0x00000000; /* ok, no special flag */ rxdc->rdes[0] |= cp_len; /* Update the new status (only if we are not on the first desc) */ if (i != 0) physmem_copy_u32_to_vm(d->vm,channel->rx_current,rxdc->rdes[0]); /* Update the RX pointer */ channel->rx_current = rxdn_addr; if (rxdc->rdes[0] & MUESLIX_RXDESC_LS) break; /* Read the next descriptor from VM physical RAM */ rxdesc_read(d,rxdn_addr,&rxdn); rxdc = &rxdn; } /* Update the first RX descriptor */ rxd0.rdes[0] |= MUESLIX_RXDESC_FS; physmem_copy_u32_to_vm(d->vm,rx_start,rxd0.rdes[0]); /* Indicate that we have a frame ready (XXX something to do ?) */ /* Generate IRQ on CPU */ d->irq_status |= MUESLIX_RX_IRQ << channel->id; dev_mueslix_update_irq_status(d); } /* Handle the Mueslix RX ring of the specified channel */ static int dev_mueslix_handle_rxring(netio_desc_t *nio, u_char *pkt,ssize_t pkt_len, struct mueslix_channel *channel) { struct mueslix_data *d = channel->parent; #if DEBUG_RECEIVE MUESLIX_LOG(d,"channel %u: receiving a packet of %d bytes\n", channel->id,pkt_len); mem_dump(log_file,pkt,pkt_len); #endif MUESLIX_LOCK(d); if (dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_RX_ENABLE) dev_mueslix_receive_pkt(channel,pkt,pkt_len); MUESLIX_UNLOCK(d); return(TRUE); } /* Read a TX descriptor */ static void txdesc_read(struct mueslix_data *d,m_uint32_t txd_addr, struct tx_desc *txd) { /* get the next descriptor from VM physical RAM */ physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); /* byte-swapping */ txd->tdes[0] = vmtoh32(txd->tdes[0]); txd->tdes[1] = vmtoh32(txd->tdes[1]); } /* Set the address of the next TX descriptor */ static void txdesc_set_next(struct mueslix_channel *channel) { switch(channel->parent->chip_mode) { case 0: channel->tx_current += sizeof(struct tx_desc); if (channel->tx_current == channel->tx_end) channel->tx_current = channel->tx_start; break; case 1: default: if (channel->tx_current == channel->tx_end) channel->tx_current = channel->tx_start; else channel->tx_current += sizeof(struct tx_desc); } } /* Handle the TX ring of a specific channel (single packet) */ static int dev_mueslix_handle_txring_single(struct mueslix_channel *channel) { struct mueslix_data *d = channel->parent; u_char pkt[MUESLIX_MAX_PKT_SIZE],*pkt_ptr; m_uint32_t tx_start,clen,sub_len,tot_len,pad; struct tx_desc txd0,ctxd,*ptxd; int done = FALSE; if ((channel->tx_start == 0) || (channel->status == 0)) return(FALSE); /* Copy the current txring descriptor */ tx_start = channel->tx_current; ptxd = &txd0; txdesc_read(d,channel->tx_current,ptxd); /* If we don't own the descriptor, we cannot transmit */ if (!(txd0.tdes[0] & MUESLIX_TXDESC_OWN)) return(FALSE); #if DEBUG_TRANSMIT MUESLIX_LOG(d,"mueslix_handle_txring: 1st desc: " "tdes[0]=0x%x, tdes[1]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1]); #endif pkt_ptr = pkt; tot_len = 0; do { #if DEBUG_TRANSMIT MUESLIX_LOG(d,"mueslix_handle_txring: loop: " "tdes[0]=0x%x, tdes[1]=0x%x\n", ptxd->tdes[0],ptxd->tdes[1]); #endif if (!(ptxd->tdes[0] & MUESLIX_TXDESC_OWN)) { MUESLIX_LOG(d,"mueslix_handle_txring: descriptor not owned!\n"); return(FALSE); } switch(channel->parent->chip_mode) { case 0: clen = ptxd->tdes[0] & MUESLIX_TXDESC_LEN_MASK; break; case 1: default: clen = (ptxd->tdes[0] & MUESLIX_TXDESC_LEN_MASK) << 2; if (ptxd->tdes[0] & MUESLIX_TXDESC_SUB) { sub_len = ptxd->tdes[0] & MUESLIX_TXDESC_SUB_LEN; sub_len >>= MUESLIX_TXDESC_SUB_SHIFT; clen -= sub_len; } } /* Be sure that we have length not null */ if (clen != 0) { //printf("pkt_ptr = %p, ptxd->tdes[1] = 0x%x, clen = %d\n", //pkt_ptr, ptxd->tdes[1], clen); physmem_copy_from_vm(d->vm,pkt_ptr,ptxd->tdes[1],clen); } pkt_ptr += clen; tot_len += clen; /* Clear the OWN bit if this is not the first descriptor */ if (!(ptxd->tdes[0] & MUESLIX_TXDESC_FS)) physmem_copy_u32_to_vm(d->vm,channel->tx_current,0); /* Go to the next descriptor */ txdesc_set_next(channel); /* Copy the next txring descriptor */ if (!(ptxd->tdes[0] & MUESLIX_TXDESC_LS)) { txdesc_read(d,channel->tx_current,&ctxd); ptxd = &ctxd; } else done = TRUE; }while(!done); if (tot_len != 0) { #if DEBUG_TRANSMIT MUESLIX_LOG(d,"sending packet of %u bytes (flags=0x%4.4x)\n", tot_len,txd0.tdes[0]); mem_dump(log_file,pkt,tot_len); #endif pad = ptxd->tdes[0] & MUESLIX_TXDESC_PAD; pad >>= MUESLIX_TXDESC_PAD_SHIFT; tot_len += (pad - 1) & 0x03; /* send it on wire */ netio_send(channel->nio,pkt,tot_len); } /* Clear the OWN flag of the first descriptor */ physmem_copy_u32_to_vm(d->vm,tx_start,0); /* Interrupt on completion ? */ d->irq_status |= MUESLIX_TX_IRQ << channel->id; dev_mueslix_update_irq_status(d); return(TRUE); } /* Handle the TX ring of a specific channel */ static int dev_mueslix_handle_txring(struct mueslix_channel *channel) { struct mueslix_data *d = channel->parent; int res,i; if (!dev_mueslix_is_rx_tx_enabled(d,channel->id) & MUESLIX_TX_ENABLE) return(FALSE); for(i=0;ipriv_data; switch(reg) { case 0x08: /* Rev ID */ return(0x2800001); case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* pci_mueslix_write() */ static void pci_mueslix_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct mueslix_data *d = dev->priv_data; switch(reg) { case PCI_REG_BAR0: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); MUESLIX_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* Initialize a Mueslix chip */ struct mueslix_data * dev_mueslix_init(vm_instance_t *vm,char *name,int chip_mode, struct pci_bus *pci_bus,int pci_device,int irq) { struct pci_device *pci_dev; struct mueslix_data *d; struct vdevice *dev; int i; /* Allocate the private data structure for Mueslix chip */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (Mueslix): out of memory\n",name); return NULL; } memset(d,0,sizeof(*d)); pthread_mutex_init(&d->lock,NULL); d->chip_mode = chip_mode; for(i=0;ichannel[i].id = i; /* Add as PCI device */ pci_dev = pci_dev_add(pci_bus,name, MUESLIX_PCI_VENDOR_ID,MUESLIX_PCI_PRODUCT_ID, pci_device,0,irq, d,NULL,pci_mueslix_read,pci_mueslix_write); if (!pci_dev) { fprintf(stderr,"%s (Mueslix): unable to create PCI device.\n",name); return NULL; } /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (Mueslix): unable to create device.\n",name); return NULL; } d->name = name; d->pci_dev = pci_dev; d->vm = vm; dev->phys_addr = 0; dev->phys_len = 0x4000; dev->handler = dev_mueslix_access; dev->priv_data = d; /* Store device info */ dev->priv_data = d; d->dev = dev; return(d); } /* Remove a Mueslix device */ void dev_mueslix_remove(struct mueslix_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 a Mueslix channel */ int dev_mueslix_set_nio(struct mueslix_data *d,u_int channel_id, netio_desc_t *nio) { struct mueslix_channel *channel; if (channel_id >= MUESLIX_NR_CHANNELS) return(-1); channel = &d->channel[channel_id]; /* check that a NIO is not already bound */ if (channel->nio != NULL) return(-1); /* define the new NIO */ channel->nio = nio; channel->parent = d; channel->tx_tid = ptask_add((ptask_callback)dev_mueslix_handle_txring, channel,NULL); netio_rxl_add(nio,(netio_rx_handler_t)dev_mueslix_handle_rxring, channel,NULL); return(0); } /* Unbind a NIO from a Mueslix channel */ int dev_mueslix_unset_nio(struct mueslix_data *d,u_int channel_id) { struct mueslix_channel *channel; if (channel_id >= MUESLIX_NR_CHANNELS) return(-1); channel = &d->channel[channel_id]; if (channel->nio) { ptask_remove(channel->tx_tid); netio_rxl_remove(channel->nio); channel->nio = NULL; } return(0); }