/* * Cisco router simulation platform. * Copyright (C) 2005,2006 Christophe Fillot. All rights reserved. * * PA-A1 ATM interface based on TI1570 and PLX 9060-ES. * * EEPROM types: * - 0x17: PA-A1-OC3MM * - 0x2C: PA-A1-OC3SM * - 0x2D: PA-A1-OC3UTP * * IOS command: "sh controller atm2/0" * * Manuals: * * Texas Instruments TNETA1570 ATM segmentation and reassembly device * with integrated 64-bit PCI-host interface * http://focus.ti.com/docs/prod/folders/print/tneta1570.html * * PLX 9060-ES * http://www.plxtech.com/products/io_accelerators/PCI9060/default.htm * * TODO: * - RX error handling and RX AAL5-related stuff * - HEC and AAL5 CRC fields. * * Cell trains for faster NETIO communications ? */ #include #include #include #include #include #include "crc.h" #include "atm.h" #include "cpu.h" #include "vm.h" #include "dynamips.h" #include "memory.h" #include "device.h" #include "ptask.h" #include "dev_c7200.h" /* Debugging flags */ #define DEBUG_ACCESS 0 #define DEBUG_UNKNOWN 0 #define DEBUG_TRANSMIT 0 #define DEBUG_RECEIVE 0 #define DEBUG_TX_DMA 0 /* PCI vendor/product codes */ #define TI1570_PCI_VENDOR_ID 0x104c #define TI1570_PCI_PRODUCT_ID 0xa001 #define PLX_9060ES_PCI_VENDOR_ID 0x10b5 #define PLX_9060ES_PCI_PRODUCT_ID 0x906e /* Number of buffers transmitted at each TX DMA ring scan pass */ #define TI1570_TXDMA_PASS_COUNT 16 /* TI1570 Internal Registers (p.58 of doc) */ #define TI1570_REG_CONFIG 0x0000 /* Configuration registers */ #define TI1570_REG_STATUS 0x0001 /* Status register */ #define TI1570_REG_IMASK 0x0002 /* Interrupt-mask register */ #define TI1570_REG_RGT_RAT 0x0003 /* RGT + RAT cycle-counter */ #define TI1570_REG_RX_UNKNOWN 0x0004 /* RX Unknown Register */ #define TI1570_REG_TX_CRING_SIZE 0x0005 /* TX Completion ring sizes */ #define TI1570_REG_RX_CRING_SIZE 0x0006 /* RX Completion ring sizes */ #define TI1570_REG_TX_PSR_SIZE 0x0007 /* TX Pkt-seg ring size + FIFO */ #define TI1570_REG_HEC_AAL5_DISC 0x0008 /* HEC err + AAL5 CPCS discard */ #define TI1570_REG_UNK_PROTO_CNT 0x0009 /* Unknown-protocols counter */ #define TI1570_REG_RX_ATM_COUNT 0x000A /* ATM-cells-received counter */ #define TI1570_REG_TX_ATM_COUNT 0x000B /* ATM-cells-tranmitted counter */ #define TI1570_REG_TX_RX_FIFO 0x000C /* TX/RX FIFO occupancy, VCI mask */ #define TI1570_REG_SCHED_SIZE 0x000D /* Scheduler Table size */ #define TI1570_REG_SOFT_RESET 0x000E /* Software Reset */ #define TI1570_REG_TCR_WOI_ADDR 0x0080 /* TX Compl. Ring w/o IRQ addr. */ #define TI1570_REG_TCR_WI_ADDR 0x0081 /* TX Compl. Ring w/ IRQ addr. */ #define TI1570_REG_RCR_WOI_ADDR 0x0082 /* RX Compl. Ring w/o IRQ addr. */ #define TI1570_REG_RCR_WI_ADDR 0x0083 /* RX Compl. Ring w/ IRQ addr. */ /* TI1570 configuration register (p.59) */ #define TI1570_CFG_EN_RAT 0x00000001 /* Reassembly Aging */ #define TI1570_CFG_BP_SEL 0x00000002 /* IRQ on packet or buffer */ #define TI1570_CFG_EN_RX 0x00000010 /* RX enable */ #define TI1570_CFG_EN_TX 0x00000020 /* TX enable */ #define TI1570_CFG_SMALL_MAP 0x00000040 /* Small map */ /* TI1570 status register (p.61) */ #define TI1570_STAT_CP_TX 0x00000001 /* Transmit completion ring */ #define TI1570_STAT_RX_IRR 0x00000040 /* Receive unknown reg set */ #define TI1570_STAT_CP_RX 0x00000080 /* Receive completion ring */ #define TI1570_STAT_TX_FRZ 0x00000100 /* TX Freeze */ #define TI1570_STAT_RX_FRZ 0x00000200 /* RX Freeze */ /* Mask for RX/TX completion-ring sizes */ #define TI1570_TCR_SIZE_MASK 0x00001FFF /* TX compl. ring size mask */ #define TI1570_RCR_SIZE_MASK 0x000003FF /* RX compl. ring size mask */ /* TI1750 TX packet segmentation ring register */ #define TI1570_PSR_SIZE_MASK 0x000000FF /* pkt-seg ring size */ /* Total size of the TI1570 Control Memory */ #define TI1570_CTRL_MEM_SIZE 0x100000 /* Offsets of the TI1570 structures (p.66) */ #define TI1570_TX_SCHED_OFFSET 0x0000 /* TX scheduler table */ #define TI1570_INTERNAL_REGS_OFFSET 0x3200 /* Internal Registers */ #define TI1570_FREE_BUFFERS_OFFSET 0x3800 /* Free-Buffer Pointers */ #define TI1570_RX_DMA_PTR_TABLE_OFFSET 0x4000 /* RX VPI/VCI pointer table */ #define TI1570_TX_DMA_TABLE_OFFSET 0x8000 /* TX DMA state table */ #define TI1570_RX_DMA_TABLE_OFFSET 0x10000 /* RX DMA state table */ /* TX scheduler table */ #define TI1570_TX_SCHED_ENTRY_COUNT 6200 #define TI1570_TX_SCHED_ENTRY_MASK 0x3FF /* Entry mask */ #define TI1570_TX_SCHED_E0_SHIFT 0 /* Shift for entry 0 */ #define TI1570_TX_SCHED_E1_SHIFT 16 /* Shift for entry 0 */ /* TX DMA state table */ #define TI1570_TX_DMA_ACT 0x80000000 /* ACTive (word 0) */ #define TI1570_TX_DMA_SOP 0x40000000 /* Start of Packet (SOP) */ #define TI1570_TX_DMA_EOP 0x20000000 /* End of Packet (EOP) */ #define TI1570_TX_DMA_ABORT 0x10000000 /* Abort */ #define TI1570_TX_DMA_TCR_SELECT 0x02000000 /* TX comp. ring selection */ #define TI1570_TX_DMA_AAL_TYPE_MASK 0x0C000000 /* AAL-type mask */ #define TI1570_TX_DMA_AAL_TRWPTI 0x00000000 /* Transp. AAL w/ PTI set */ #define TI1570_TX_DMA_AAL_AAL5 0x04000000 /* AAL5 */ #define TI1570_TX_DMA_AAL_TRWOPTI 0x08000000 /* Transp. AAL w/o PTI set */ #define TI1570_TX_DMA_OFFSET_MASK 0x00FF0000 #define TI1570_TX_DMA_OFFSET_SHIFT 16 #define TI1570_TX_DMA_DCOUNT_MASK 0x0000FFFF #define TI1570_TX_DMA_ON 0x80000000 /* DMA state (word 3) */ #define TI1570_TX_DMA_RING_OFFSET_MASK 0x3FFFFF00 #define TI1570_TX_DMA_RING_OFFSET_SHIFT 8 #define TI1570_TX_DMA_RING_INDEX_MASK 0x000000FF #define TI1570_TX_DMA_RING_AAL5_LEN_MASK 0x0000FFFF typedef struct ti1570_tx_dma_entry ti1570_tx_dma_entry_t; struct ti1570_tx_dma_entry { m_uint32_t ctrl_buf; /* Ctrl, Buffer Offset, Buffer data-byte count */ m_uint32_t cb_addr; /* Current Buffer Address */ m_uint32_t atm_hdr; /* 4-byte ATM header */ m_uint32_t dma_state; /* DMA state + Packet segmentation ring address */ m_uint32_t nb_addr; /* Next Buffer address */ m_uint32_t sb_addr; /* Start of Buffer address */ m_uint32_t aal5_crc; /* Partial AAL5-transmit CRC */ m_uint32_t aal5_ctrl; /* AAL5-control field and length field */ }; /* TX Packet-Segmentation Rings */ #define TI1570_TX_RING_OWN 0x80000000 /* If set, packet is ready */ #define TI1570_TX_RING_PTR_MASK 0x3FFFFFFF /* Buffer pointer */ /* TX Data Buffers */ #define TI1570_TX_BUFFER_RDY 0x80000000 /* If set, buffer is ready */ #define TI1570_TX_BUFFER_SOP 0x40000000 /* First buffer of packet */ #define TI1570_TX_BUFFER_EOP 0x20000000 /* Last buffer of packet */ #define TI1570_TX_BUFFER_ABORT 0x10000000 /* Abort */ #define TI1570_TX_BUFFER_OFFSET_MASK 0x00FF0000 #define TI1570_TX_BUFFER_OFFSET_SHIFT 16 #define TI1570_TX_BUFFER_DCOUNT_MASK 0x0000FFFF typedef struct ti1570_tx_buffer ti1570_tx_buffer_t; struct ti1570_tx_buffer { m_uint32_t ctrl_buf; /* Ctrl, Buffer offset, Buffer data-byte count */ m_uint32_t nb_addr; /* Start-of-next buffer pointer */ m_uint32_t atm_hdr; /* 4-byte ATM header */ m_uint32_t aal5_ctrl; /* PCS-UU/CPI field (AAL5 control field) */ }; /* TX completion-ring */ #define TI1570_TCR_OWN 0x80000000 /* OWNner bit */ #define TI1570_TCR_ABORT 0x40000000 /* Abort */ /* RX VPI/VCI DMA pointer table */ #define TI1570_RX_VPI_ENABLE 0x80000000 /* VPI enabled ? */ #define TI1570_RX_BASE_PTR_MASK 0x7FFF0000 /* Base pointer mask */ #define TI1570_RX_BASE_PTR_SHIFT 16 /* Base pointer shift */ #define TI1570_RX_VCI_RANGE_MASK 0x0000FFFF /* Valid VCI range */ /* RX DMA state table (p.36) */ #define TI1570_RX_DMA_ACT 0x80000000 /* ACTive (word 0) */ #define TI1570_RX_DMA_RCR_SELECT 0x20000000 /* RX comp. ring selection */ #define TI1570_RX_DMA_WAIT_EOP 0x10000000 /* Wait for EOP */ #define TI1570_RX_DMA_AAL_TYPE_MASK 0x0C000000 /* AAL-type mask */ #define TI1570_RX_DMA_AAL_PTI 0x00000000 /* PTI based tr. AAL pkt */ #define TI1570_RX_DMA_AAL_AAL5 0x04000000 /* AAL5 */ #define TI1570_RX_DMA_AAL_CNT 0x08000000 /* Cnt based tr. AAL pkt */ #define TI1570_RX_DMA_FIFO 0x02000000 /* FIFO used for free bufs */ #define TI1570_RX_DMA_TR_CNT_MASK 0xFFFF0000 /* Cnt-based Tr-AAL */ #define TI1570_RX_DMA_TR_CNT_SHIFT 16 #define TI1570_RX_DMA_CB_LEN_MASK 0x0000FFFF /* Current buffer length */ #define TI1570_RX_DMA_ON 0x80000000 /* DMA state (word 6) */ #define TI1570_RX_DMA_FILTER 0x40000000 /* Filter */ #define TI1570_RX_DMA_FB_PTR_MASK 0x3FFFFFFF /* Free-buffer ptr mask */ #define TI1570_RX_DMA_FB_INDEX_MASK 0x000000FF /* Index with Free-buf ring */ typedef struct ti1570_rx_dma_entry ti1570_rx_dma_entry_t; struct ti1570_rx_dma_entry { m_uint32_t ctrl; /* Control field, EFCN cell cnt, pkt length */ m_uint32_t cb_addr; /* Current Buffer Address */ m_uint32_t sb_addr; /* Start of Buffer address */ m_uint32_t cb_len; /* Transp-AAL pkt counter, current buf length */ m_uint32_t sp_ptr; /* Start-of-packet pointer */ m_uint32_t aal5_crc; /* Partial AAL5-receive CRC */ m_uint32_t fbr_entry; /* Free-buffer ring-pointer table entry */ m_uint32_t timeout; /* Timeout value, current timeout count */ }; /* RX free-buffer ring pointer table entry (p.39) */ #define TI1570_RX_FBR_PTR_MASK 0xFFFFFFFC #define TI1570_RX_FBR_BS_MASK 0xFFFF0000 /* Buffer size mask */ #define TI1570_RX_FBR_BS_SHIFT 16 #define TI1570_RX_FBR_RS_MASK 0x0000FC00 /* Ring size mask */ #define TI1570_RX_FBR_RS_SHIFT 10 #define TI1570_RX_FBR_IDX_MASK 0x000003FF /* Current index mask */ typedef struct ti1570_rx_fbr_entry ti1570_rx_fbr_entry_t; struct ti1570_rx_fbr_entry { m_uint32_t fbr_ptr; /* RX free-buffer ring pointer */ m_uint32_t ring_size; /* Ring size and buffer size */ }; /* RX buffer pointer (p.41) */ #define TI1570_RX_BUFPTR_OWN 0x80000000 /* If set, buffer is ready */ #define TI1570_RX_BUFPTR_MASK 0x3FFFFFFF /* Buffer address mask */ /* RX data buffer (p.42) */ #define TI1570_RX_BUFFER_SOP 0x80000000 /* Start-of-Packet buffer */ #define TI1570_RX_BUFFER_EOP 0x40000000 /* End-of-Packet buffer */ typedef struct ti1570_rx_buffer ti1570_rx_buffer_t; struct ti1570_rx_buffer { m_uint32_t reserved; /* Reserved, not used by the TI1570 */ m_uint32_t ctrl; /* Control field, Start of next buffer pointer */ m_uint32_t atm_hdr; /* ATM header */ m_uint32_t user; /* User-defined value */ }; /* Internal structure to hold free buffer info */ typedef struct ti1570_rx_buf_holder ti1570_rx_buf_holder_t; struct ti1570_rx_buf_holder { m_uint32_t buf_addr; m_uint32_t buf_size; ti1570_rx_buffer_t rx_buf; }; /* RX completion ring entry */ #define TI1570_RCR_PKT_OVFLW 0x80000000 /* Packet overflow (word 0) */ #define TI1570_RCR_CRC_ERROR 0x40000000 /* CRC error */ #define TI1570_RCR_BUF_STARV 0x20000000 /* Buffer starvation */ #define TI1570_RCR_TIMEOUT 0x10000000 /* Reassembly timeout */ #define TI1570_RCR_ABORT 0x08000000 /* Abort condition */ #define TI1570_RCR_AAL5 0x04000000 /* AAL5 indicator */ #define TI1570_RCR_VALID 0x80000000 /* Start-ptr valid (word 2) */ #define TI1570_RCR_OWN 0x80000000 /* Buffer ready (word 4) */ #define TI1570_RCR_ERROR 0x40000000 /* Error entry */ typedef struct ti1570_rcr_entry ti1570_rcr_entry_t; struct ti1570_rcr_entry { m_uint32_t atm_hdr; /* ATM header */ m_uint32_t error; /* Error Indicator + Congestion cell count */ m_uint32_t sp_addr; /* Start of packet */ m_uint32_t aal5_trailer; /* AAL5 trailer */ m_uint32_t fbr_entry; /* Free-buffer ring-pointer table entry */ m_uint32_t res[3]; /* Reserved, not used by the TI1570 */ }; /* TI1570 Data */ struct pa_a1_data { char *name; /* IRQ clearing counter */ u_int irq_clear_count; /* Control Memory pointer */ m_uint32_t *ctrl_mem_ptr; /* TI1570 internal registers */ m_uint32_t *iregs; /* TX FIFO cell */ m_uint8_t txfifo_cell[ATM_CELL_SIZE]; m_uint32_t txfifo_avail,txfifo_pos; /* TX Scheduler table */ m_uint32_t *tx_sched_table; /* TX DMA state table */ ti1570_tx_dma_entry_t *tx_dma_table; /* TX/RX completion ring current position */ m_uint32_t tcr_wi_pos,tcr_woi_pos; m_uint32_t rcr_wi_pos,rcr_woi_pos; /* RX VPI/VCI DMA pointer table */ m_uint32_t *rx_vpi_vci_dma_table; /* RX DMA state table */ ti1570_rx_dma_entry_t *rx_dma_table; /* RX Free-buffer ring pointer table */ ti1570_rx_fbr_entry_t *rx_fbr_table; /* Virtual device */ struct vdevice *dev; /* PCI device information */ struct pci_device *pci_dev_ti,*pci_dev_plx; /* Virtual machine */ vm_instance_t *vm; /* NetIO descriptor */ netio_desc_t *nio; /* TX ring scanner task id */ ptask_id_t tx_tid; }; /* Log a TI1570 message */ #define TI1570_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) /* Reset the TI1570 (forward declaration) */ static void ti1570_reset(struct pa_a1_data *d,int clear_ctrl_mem); /* Update the interrupt status */ static inline void dev_pa_a1_update_irq_status(struct pa_a1_data *d) { if (d->iregs[TI1570_REG_STATUS] & d->iregs[TI1570_REG_IMASK]) { pci_dev_trigger_irq(d->vm,d->pci_dev_ti); } else { pci_dev_clear_irq(d->vm,d->pci_dev_ti); } } /* * dev_pa_a1_access() */ void *dev_pa_a1_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 pa_a1_data *d = dev->priv_data; if (op_type == MTS_READ) *data = 0; #if DEBUG_ACCESS if (op_type == MTS_READ) { cpu_log(cpu,"TI1570","read access to offset = 0x%x, pc = 0x%llx\n", offset,cpu_get_pc(cpu)); } else { cpu_log(cpu,"TI1570","write access to vaddr = 0x%x, pc = 0x%llx, " "val = 0x%llx\n",offset,cpu_get_pc(cpu),*data); } #endif /* Specific cases */ switch(offset) { /* Status register */ case 0x3204: if (op_type == MTS_READ) { *data = d->iregs[TI1570_REG_STATUS]; if (++d->irq_clear_count == 2) { d->iregs[TI1570_REG_STATUS] &= ~0x3FF; d->irq_clear_count = 0; } dev_pa_a1_update_irq_status(d); } break; /* Software Reset register */ case 0x3238: TI1570_LOG(d,"reset issued.\n"); ti1570_reset(d,FALSE); break; case 0x18000c: if (op_type == MTS_READ) { *data = 0xa6; return NULL; } break; } /* Control Memory access */ if (offset < TI1570_CTRL_MEM_SIZE) { if (op_type == MTS_READ) *data = d->ctrl_mem_ptr[offset >> 2]; else d->ctrl_mem_ptr[offset >> 2] = *data; return NULL; } /* Unknown offset */ #if DEBUG_UNKNOWN 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 return NULL; } /* Fetch a TX data buffer from host memory */ static void ti1570_read_tx_buffer(struct pa_a1_data *d,m_uint32_t addr, ti1570_tx_buffer_t *tx_buf) { physmem_copy_from_vm(d->vm,tx_buf,addr,sizeof(ti1570_tx_buffer_t)); /* byte-swapping */ tx_buf->ctrl_buf = vmtoh32(tx_buf->ctrl_buf); tx_buf->nb_addr = vmtoh32(tx_buf->nb_addr); tx_buf->atm_hdr = vmtoh32(tx_buf->atm_hdr); tx_buf->aal5_ctrl = vmtoh32(tx_buf->aal5_ctrl); } /* Acquire a TX buffer */ static int ti1570_acquire_tx_buffer(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, m_uint32_t buf_addr) { ti1570_tx_buffer_t tx_buf; m_uint32_t buf_offset; #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_acquire_tx_buffer: acquiring buffer at address 0x%x\n", buf_addr); #endif /* Read the TX buffer from host memory */ ti1570_read_tx_buffer(d,buf_addr,&tx_buf); /* The buffer must be ready to be acquired */ if (!(tx_buf.ctrl_buf & TI1570_TX_BUFFER_RDY)) return(FALSE); /* Put the TX buffer data into the TX DMA state entry */ tde->ctrl_buf = tx_buf.ctrl_buf; tde->nb_addr = tx_buf.nb_addr << 2; /* Read the ATM header only from the first buffer */ if (tx_buf.ctrl_buf & TI1570_TX_BUFFER_SOP) { tde->atm_hdr = tx_buf.atm_hdr; tde->aal5_ctrl = tx_buf.aal5_ctrl; tde->aal5_crc = 0xFFFFFFFF; } /* Compute the current-buffer-data address */ buf_offset = tx_buf.ctrl_buf & TI1570_TX_BUFFER_OFFSET_MASK; buf_offset >>= TI1570_TX_BUFFER_OFFSET_SHIFT; tde->cb_addr = buf_addr + sizeof(tx_buf) + buf_offset; /* Remember the start address of the buffer */ tde->sb_addr = buf_addr; return(TRUE); } /* Returns TRUE if the TX DMA entry is for an AAL5 packet */ static inline int ti1570_is_tde_aal5(ti1570_tx_dma_entry_t *tde) { m_uint32_t pkt_type; pkt_type = tde->ctrl_buf & TI1570_TX_DMA_AAL_TYPE_MASK; return(pkt_type == TI1570_TX_DMA_AAL_AAL5); } /* Update the AAL5 partial CRC */ static void ti1570_update_aal5_crc(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde) { tde->aal5_crc = crc32_compute(tde->aal5_crc, &d->txfifo_cell[ATM_HDR_SIZE], ATM_PAYLOAD_SIZE); } /* * Update the TX DMA entry buffer offset and count when "data_len" bytes * have been transmitted. */ static void ti1570_update_tx_dma_bufinfo(ti1570_tx_dma_entry_t *tde, m_uint32_t buf_size, m_uint32_t data_len) { m_uint32_t tmp,tot_len; /* update the current buffer address */ tde->cb_addr += data_len; /* set the remaining byte count */ tmp = tde->ctrl_buf & ~TI1570_TX_BUFFER_DCOUNT_MASK; tde->ctrl_buf = tmp + (buf_size - data_len); /* update the AAL5 count */ if (ti1570_is_tde_aal5(tde)) { tot_len = tde->aal5_ctrl & TI1570_TX_DMA_RING_AAL5_LEN_MASK; tot_len += data_len; tmp = (tde->aal5_ctrl & ~TI1570_TX_DMA_RING_AAL5_LEN_MASK) + tot_len; tde->aal5_ctrl = tmp; } } /* Clear the TX fifo */ static void ti1570_clear_tx_fifo(struct pa_a1_data *d) { d->txfifo_avail = ATM_PAYLOAD_SIZE; d->txfifo_pos = ATM_HDR_SIZE; memset(d->txfifo_cell,0,ATM_CELL_SIZE); } /* * Transmit the TX FIFO cell through the NETIO infrastructure if * it is full. */ static void ti1570_send_tx_fifo(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int update_aal5_crc) { if (d->txfifo_avail == 0) { #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_transmit_cell: transmitting to NETIO device\n"); mem_dump(log_file,d->txfifo_cell,ATM_CELL_SIZE); #endif if (update_aal5_crc) ti1570_update_aal5_crc(d,tde); netio_send(d->nio,d->txfifo_cell,ATM_CELL_SIZE); ti1570_clear_tx_fifo(d); } } /* Add padding to the FIFO */ static void ti1570_add_tx_padding(struct pa_a1_data *d,m_uint32_t len) { if (len > d->txfifo_avail) { TI1570_LOG(d,"ti1570_add_tx_padding: trying to add too large " "padding (avail: 0x%x, pad: 0x%x)\n",d->txfifo_avail,len); len = d->txfifo_avail; } memset(&d->txfifo_cell[d->txfifo_pos],0,len); d->txfifo_pos += len; d->txfifo_avail -= len; } /* Initialize an ATM cell for tranmitting */ static m_uint32_t ti1570_init_tx_atm_cell(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int set_pti) { m_uint32_t buf_size,len,atm_hdr; buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; len = m_min(buf_size,d->txfifo_avail); #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_init_tx_atm_cell: data ptr=0x%x, " "buf_size=%u (0x%x), len=%u (0x%x), atm_hdr=0x%x\n", tde->cb_addr,buf_size,buf_size,len,len,tde->atm_hdr); #endif /* copy the ATM header */ atm_hdr = tde->atm_hdr; if (set_pti) { atm_hdr &= ~ATM_PTI_NETWORK; atm_hdr |= ATM_PTI_EOP; } *(m_uint32_t *)d->txfifo_cell = htonl(atm_hdr); /* compute HEC field */ atm_insert_hec(d->txfifo_cell); /* copy the payload and try to transmit if the FIFO is full */ if (len > 0) { physmem_copy_from_vm(d->vm,&d->txfifo_cell[d->txfifo_pos], tde->cb_addr,len); d->txfifo_pos += len; d->txfifo_avail -= len; } ti1570_update_tx_dma_bufinfo(tde,buf_size,len); return(len); } /* * Transmit an Transparent-AAL ATM cell through the NETIO infrastructure. */ static int ti1570_transmit_transp_cell(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int atm_set_eop,int *buf_end) { m_uint32_t buf_size,len; int pkt_end,last_cell; pkt_end = tde->ctrl_buf & TI1570_TX_DMA_EOP; buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; last_cell = FALSE; if (!pkt_end) { len = ti1570_init_tx_atm_cell(d,tde,FALSE); ti1570_send_tx_fifo(d,tde,FALSE); if ((buf_size - len) == 0) *buf_end = TRUE; return(FALSE); } /* this is the end of packet and the last buffer */ if (buf_size <= d->txfifo_avail) last_cell = TRUE; len = ti1570_init_tx_atm_cell(d,tde,last_cell & atm_set_eop); if (last_cell) ti1570_add_tx_padding(d,d->txfifo_avail); ti1570_send_tx_fifo(d,tde,FALSE); return(last_cell); } /* Add the AAL5 trailer to the TX FIFO */ static void ti1570_add_aal5_trailer(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde) { m_uint8_t *trailer; trailer = &d->txfifo_cell[ATM_AAL5_TRAILER_POS]; /* Control field + Length */ *(m_uint32_t *)trailer = htonl(tde->aal5_ctrl); /* Final CRC-32 computation */ tde->aal5_crc = crc32_compute(tde->aal5_crc, &d->txfifo_cell[ATM_HDR_SIZE], ATM_PAYLOAD_SIZE - 4); *(m_uint32_t *)(trailer+4) = htonl(~tde->aal5_crc); /* Consider the FIFO as full */ d->txfifo_avail = 0; } /* * Tranmit an AAL5 cell through the NETIO infrastructure. * * Returns TRUE if this is the real end of packet. */ static int ti1570_transmit_aal5_cell(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde, int *buf_end) { m_uint32_t buf_size,len; int pkt_end; pkt_end = tde->ctrl_buf & TI1570_TX_DMA_EOP; buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_transmit_aal5_cell: data ptr=0x%x, " "buf_size=0x%x (%u)\n",tde->cb_addr,buf_size,buf_size); #endif /* If this is not the end of packet, transmit the cell normally */ if (!pkt_end) { len = ti1570_init_tx_atm_cell(d,tde,FALSE); ti1570_send_tx_fifo(d,tde,TRUE); if ((buf_size - len) == 0) *buf_end = TRUE; return(FALSE); } /* * This is the end of packet, check if we need to emit a special cell * for the AAL5 trailer. */ if ((buf_size + ATM_AAL5_TRAILER_SIZE) <= d->txfifo_avail) { len = ti1570_init_tx_atm_cell(d,tde,TRUE); /* add the padding */ ti1570_add_tx_padding(d,d->txfifo_avail - ATM_AAL5_TRAILER_SIZE); /* add the AAL5 trailer at offset 40 */ ti1570_add_aal5_trailer(d,tde); /* we can transmit the cell */ ti1570_send_tx_fifo(d,tde,FALSE); *buf_end = TRUE; return(TRUE); } /* Transmit the cell normally */ len = ti1570_init_tx_atm_cell(d,tde,FALSE); ti1570_add_tx_padding(d,d->txfifo_avail); ti1570_send_tx_fifo(d,tde,TRUE); return(FALSE); } /* Update the TX completion ring */ static void ti1570_update_tx_cring(struct pa_a1_data *d, ti1570_tx_dma_entry_t *tde) { m_uint32_t tcr_addr,tcr_end,val; if (tde->ctrl_buf & TI1570_TX_DMA_TCR_SELECT) { /* TX completion ring with interrupt */ tcr_addr = d->iregs[TI1570_REG_TCR_WI_ADDR] + (d->tcr_wi_pos * 4); } else { /* TX completion ring without interrupt */ tcr_addr = d->iregs[TI1570_REG_TCR_WOI_ADDR] + (d->tcr_woi_pos * 4); } #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_update_tx_cring: posting 0x%x at address 0x%x\n", tde->sb_addr,tcr_addr); physmem_dump_vm(d->vm,tde->sb_addr,sizeof(ti1570_tx_buffer_t) >> 2); #endif /* we have a TX freeze if the buffer belongs to the host */ val = physmem_copy_u32_from_vm(d->vm,tcr_addr); if (!(val & TI1570_TCR_OWN)) { d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_TX_FRZ; return; } /* put the buffer address in the ring */ val = tde->sb_addr >> 2; if (tde->ctrl_buf & TI1570_TX_DMA_ABORT) val |= TI1570_TCR_ABORT; physmem_copy_u32_to_vm(d->vm,tcr_addr,val); /* update the internal position pointer */ if (tde->ctrl_buf & TI1570_TX_DMA_TCR_SELECT) { tcr_end = d->iregs[TI1570_REG_TX_CRING_SIZE] & TI1570_TCR_SIZE_MASK; if ((d->tcr_wi_pos++) == tcr_end) d->tcr_wi_pos = 0; } else { tcr_end = (d->iregs[TI1570_REG_TX_CRING_SIZE] >> 16); tcr_end &= TI1570_TCR_SIZE_MASK; if ((d->tcr_woi_pos++) == tcr_end) d->tcr_woi_pos = 0; } } /* Analyze a TX DMA state table entry */ static int ti1570_scan_tx_dma_entry_single(struct pa_a1_data *d, m_uint32_t index) { ti1570_tx_dma_entry_t *tde; m_uint32_t psr_base,psr_addr,psr_entry,psr_end; m_uint32_t buf_addr,buf_size,pkt_type,tmp; m_uint32_t psr_index; int atm_set_eop = 0; int pkt_end,buf_end = 0; tde = &d->tx_dma_table[index]; /* The DMA channel state flag must be ON */ if (!(tde->dma_state & TI1570_TX_DMA_ON)) return(FALSE); #if DEBUG_TX_DMA /* We have a running DMA channel */ TI1570_LOG(d,"ti1570_scan_tx_dma_entry: TX DMA entry %u is ON " "(ctrl_buf = 0x%x)\n",index,tde->ctrl_buf); #endif /* Is this the start of a new packet ? */ if (!(tde->ctrl_buf & TI1570_TX_DMA_ACT)) { #if DEBUG_TX_DMA TI1570_LOG(d,"ti1570_scan_tx_dma_entry: TX DMA entry %u is not ACT\n", index); #endif /* No packet yet, fetch it from the packet-segmentation ring */ psr_base = tde->dma_state & TI1570_TX_DMA_RING_OFFSET_MASK; psr_index = tde->dma_state & TI1570_TX_DMA_RING_INDEX_MASK; /* Compute address of the current packet segmentation ring entry */ psr_addr = (psr_base + psr_index) << 2; psr_entry = physmem_copy_u32_from_vm(d->vm,psr_addr); #if DEBUG_TX_DMA TI1570_LOG(d,"ti1570_scan_tx_dma_entry: psr_addr = 0x%x, " "psr_entry = 0x%x\n",psr_addr,psr_entry); #endif /* The packet-segmentation-ring entry is owned by host, quit now */ if (!(psr_entry & TI1570_TX_RING_OWN)) return(FALSE); /* Acquire the first buffer (it MUST be in the ready state) */ buf_addr = (psr_entry & TI1570_TX_RING_PTR_MASK) << 2; if (!ti1570_acquire_tx_buffer(d,tde,buf_addr)) { TI1570_LOG(d,"ti1570_scan_tx_dma_entry: PSR entry with OWN bit set " "but buffer without RDY bit set.\n"); return(FALSE); } /* Set ACT bit for the DMA channel */ tde->ctrl_buf |= TI1570_TX_DMA_ACT; } /* Compute the remaining size and determine the packet type */ buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; pkt_type = tde->ctrl_buf & TI1570_TX_DMA_AAL_TYPE_MASK; pkt_end = tde->ctrl_buf & TI1570_TX_DMA_EOP; #if DEBUG_TRANSMIT TI1570_LOG(d,"ti1570_scan_tx_dma_entry: ctrl_buf=0x%8.8x, " "cb_addr=0x%8.8x, atm_hdr=0x%8.8x, dma_state=0x%8.8x\n", tde->ctrl_buf, tde->cb_addr, tde->atm_hdr, tde->dma_state); TI1570_LOG(d,"ti1570_scan_tx_dma_entry: nb_addr=0x%8.8x, " "sb_addr=0x%8.8x, aal5_crc=0x%8.8x, aal5_ctrl=0x%8.8x\n", tde->nb_addr, tde->sb_addr, tde->aal5_crc, tde->aal5_ctrl); #endif /* * If the current buffer is now empty and if this is not the last * buffer in the current packet, try to fetch a new buffer. * If the next buffer is not yet ready, we have finished. */ if (!buf_size && !pkt_end && !ti1570_acquire_tx_buffer(d,tde,tde->nb_addr)) return(FALSE); switch(pkt_type) { case TI1570_TX_DMA_AAL_TRWPTI: atm_set_eop = 1; case TI1570_TX_DMA_AAL_TRWOPTI: /* Transmit the ATM cell transparently */ pkt_end = ti1570_transmit_transp_cell(d,tde,atm_set_eop,&buf_end); break; case TI1570_TX_DMA_AAL_AAL5: pkt_end = ti1570_transmit_aal5_cell(d,tde,&buf_end); break; default: TI1570_LOG(d,"ti1570_scan_tx_dma_entry: invalid AAL-type\n"); return(FALSE); } /* Re-read the remaining buffer size */ buf_size = tde->ctrl_buf & TI1570_TX_DMA_DCOUNT_MASK; /* Put the buffer address in the transmit completion ring */ if (buf_end) ti1570_update_tx_cring(d,tde); /* * If we have reached end of packet (EOP): clear the ACT bit, * give back the packet-segmentation ring entry to the host, * and increment the PSR index. */ if (pkt_end) { tde->ctrl_buf &= ~TI1570_TX_DMA_ACT; /* Clear the OWN bit of the packet-segmentation ring entry */ psr_base = tde->dma_state & TI1570_TX_DMA_RING_OFFSET_MASK; psr_index = (tde->dma_state & TI1570_TX_DMA_RING_INDEX_MASK); psr_addr = (psr_base + psr_index) << 2; psr_entry = physmem_copy_u32_from_vm(d->vm,psr_addr); psr_entry &= ~TI1570_TX_RING_OWN; physmem_copy_u32_to_vm(d->vm,psr_addr,psr_entry); /* Increment the packet-segmentation ring index */ psr_index++; psr_end = d->iregs[TI1570_REG_TX_PSR_SIZE] >> 16; psr_end &= TI1570_PSR_SIZE_MASK; if (psr_index > psr_end) { psr_index = 0; #if DEBUG_TX_DMA TI1570_LOG(d,"ti1570_scan_tx_dma_entry: PSR ring rotation " "(psr_end = %u)\n",psr_end); #endif } tmp = (tde->dma_state & ~TI1570_TX_DMA_RING_INDEX_MASK); tmp |= (psr_index & TI1570_TX_DMA_RING_INDEX_MASK); tde->dma_state = tmp; } /* Generate an interrupt if required */ if (tde->ctrl_buf & TI1570_TX_DMA_TCR_SELECT) { if (((d->iregs[TI1570_REG_CONFIG] & TI1570_CFG_BP_SEL) && buf_end) || pkt_end) { d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_CP_TX; dev_pa_a1_update_irq_status(d); } } return(TRUE); } /* Analyze a TX DMA state table entry */ static void ti1570_scan_tx_dma_entry(struct pa_a1_data *d,m_uint32_t index) { int i; for(i=0;i>1;i++) { cw = d->tx_sched_table[i]; /* We have 2 index in TX DMA state table per word */ index0 = (cw >> TI1570_TX_SCHED_E0_SHIFT) & TI1570_TX_SCHED_ENTRY_MASK; index1 = (cw >> TI1570_TX_SCHED_E1_SHIFT) & TI1570_TX_SCHED_ENTRY_MASK; /* Scan the two entries (null entry => nothing to do) */ if (index0) ti1570_scan_tx_dma_entry(d,index0); if (index1) ti1570_scan_tx_dma_entry(d,index1); } } /* * Read a RX buffer from the host memory. */ static void ti1570_read_rx_buffer(struct pa_a1_data *d,m_uint32_t addr, ti1570_rx_buffer_t *rx_buf) { physmem_copy_from_vm(d->vm,rx_buf,addr,sizeof(ti1570_rx_buffer_t)); /* byte-swapping */ rx_buf->reserved = vmtoh32(rx_buf->reserved); rx_buf->ctrl = vmtoh32(rx_buf->ctrl); rx_buf->atm_hdr = vmtoh32(rx_buf->atm_hdr); rx_buf->user = vmtoh32(rx_buf->user); } /* Update the RX completion ring */ static void ti1570_update_rx_cring(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, m_uint32_t atm_hdr, m_uint32_t aal5_trailer, m_uint32_t err_ind, m_uint32_t fbuf_valid) { m_uint32_t rcr_addr,rcr_end,aal_type,ptr,val; ti1570_rcr_entry_t rcre; if (rde->ctrl & TI1570_RX_DMA_RCR_SELECT) { /* RX completion ring with interrupt */ rcr_addr = d->iregs[TI1570_REG_RCR_WI_ADDR]; rcr_addr += (d->rcr_wi_pos * sizeof(rcre)); } else { /* RX completion ring without interrupt */ rcr_addr = d->iregs[TI1570_REG_RCR_WOI_ADDR]; rcr_addr += (d->rcr_woi_pos * sizeof(rcre)); } #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_update_rx_cring: posting 0x%x at address 0x%x\n", (rde->sp_ptr << 2),rcr_addr); physmem_dump_vm(d->vm,rde->sp_ptr<<2,sizeof(ti1570_rx_buffer_t) >> 2); #endif /* we have a RX freeze if the buffer belongs to the host */ ptr = rcr_addr + OFFSET(ti1570_rcr_entry_t,fbr_entry); val = physmem_copy_u32_from_vm(d->vm,ptr); if (!(val & TI1570_RCR_OWN)) { TI1570_LOG(d,"ti1570_update_rx_cring: RX freeze...\n"); d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_RX_FRZ; return; } /* fill the RX completion ring entry and write it back to the host */ memset(&rcre,0,sizeof(rcre)); /* word 0: atm header from last cell received */ rcre.atm_hdr = atm_hdr; /* word 1: error indicator */ aal_type = rde->ctrl & TI1570_RX_DMA_AAL_TYPE_MASK; if (aal_type == TI1570_RX_DMA_AAL_AAL5) rcre.error |= TI1570_RCR_AAL5; rcre.error |= err_ind; /* word 2: Start of packet */ if (fbuf_valid) rcre.sp_addr = TI1570_RCR_VALID | rde->sp_ptr; /* word 3: AAL5 trailer */ rcre.aal5_trailer = aal5_trailer; /* word 4: OWN + error entry + free-buffer ring pointer */ rcre.fbr_entry = rde->fbr_entry & TI1570_RX_DMA_FB_PTR_MASK; if (err_ind) rcre.fbr_entry |= TI1570_RCR_ERROR; /* byte-swap and write this back to the host memory */ rcre.atm_hdr = htonl(rcre.atm_hdr); rcre.error = htonl(rcre.error); rcre.sp_addr = htonl(rcre.sp_addr); rcre.aal5_trailer = htonl(rcre.aal5_trailer); rcre.fbr_entry = htonl(rcre.fbr_entry); physmem_copy_to_vm(d->vm,&rcre,rcr_addr,sizeof(rcre)); /* clear the active bit of the RX DMA entry */ rde->ctrl &= ~TI1570_RX_DMA_ACT; /* update the internal position pointer */ if (rde->ctrl & TI1570_RX_DMA_RCR_SELECT) { rcr_end = d->iregs[TI1570_REG_RX_CRING_SIZE] & TI1570_RCR_SIZE_MASK; if ((d->rcr_wi_pos++) == rcr_end) d->rcr_wi_pos = 0; /* generate the appropriate IRQ */ d->iregs[TI1570_REG_STATUS] |= TI1570_STAT_CP_RX; dev_pa_a1_update_irq_status(d); } else { rcr_end = (d->iregs[TI1570_REG_RX_CRING_SIZE] >> 16); rcr_end &= TI1570_RCR_SIZE_MASK; if ((d->rcr_woi_pos++) == rcr_end) d->rcr_woi_pos = 0; } } /* * Acquire a free RX buffer. * * Returns FALSE if no buffer is available (buffer starvation). */ static int ti1570_acquire_rx_buffer(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, ti1570_rx_buf_holder_t *rbh, m_uint32_t atm_hdr) { ti1570_rx_fbr_entry_t *fbr_entry = NULL; m_uint32_t bp_addr,buf_addr,buf_size,buf_idx; m_uint32_t ring_index,ring_size; m_uint32_t buf_ptr,val; int fifo = FALSE; /* To keep this fucking compiler quiet */ ring_size = 0; buf_idx = 0; if (rde->ctrl & TI1570_RX_DMA_FIFO) { bp_addr = (rde->fbr_entry & TI1570_RX_DMA_FB_PTR_MASK) << 2; buf_ptr = physmem_copy_u32_from_vm(d->vm,bp_addr); buf_size = d->iregs[TI1570_REG_TX_PSR_SIZE] & 0xFFFF; fifo = TRUE; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: acquiring FIFO buffer\n"); #endif } else { ring_index = rde->fbr_entry & TI1570_RX_DMA_FB_INDEX_MASK; fbr_entry = &d->rx_fbr_table[ring_index]; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: acquiring non-FIFO buffer, " "ring index=%u (0x%x)\n",ring_index,ring_index); #endif /* Compute the number of entries in ring */ ring_size = fbr_entry->ring_size & TI1570_RX_FBR_RS_MASK; ring_size >>= TI1570_RX_FBR_RS_SHIFT; ring_size = (ring_size << 4) + 15 + 1; /* Compute the buffer size */ buf_size = fbr_entry->ring_size & TI1570_RX_FBR_BS_MASK; buf_size >>= TI1570_RX_FBR_BS_SHIFT; /* Compute the buffer address */ buf_idx = fbr_entry->ring_size & TI1570_RX_FBR_IDX_MASK; bp_addr = fbr_entry->fbr_ptr + (buf_idx << 2); #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: ring size=%u (0x%x), " "buf size=%u ATM cells\n",ring_size,ring_size,buf_size); TI1570_LOG(d,"ti1570_acquire_rx_buffer: buffer index=%u (0x%x), " "buffer ptr address = 0x%x\n",buf_idx,buf_idx,bp_addr); #endif buf_ptr = physmem_copy_u32_from_vm(d->vm,bp_addr); } #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: buf_ptr = 0x%x\n",buf_ptr); #endif /* The TI1570 must own the buffer */ if (!(buf_ptr & TI1570_RX_BUFPTR_OWN)) { TI1570_LOG(d,"ti1570_acquire_rx_buffer: no free buffer available.\n"); return(FALSE); } /* * If we are using a ring, we have to clear the OWN bit and increment * the index field. */ if (!fifo) { buf_ptr &= ~TI1570_RX_BUFPTR_OWN; physmem_copy_u32_to_vm(d->vm,bp_addr,buf_ptr); if (++buf_idx == ring_size) { #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: buf_idx=0x%x, " "ring_size=0x%x -> resetting buf_idx\n", buf_idx-1,ring_size); #endif buf_idx = 0; } val = fbr_entry->ring_size & ~TI1570_RX_FBR_IDX_MASK; val |= buf_idx; fbr_entry->ring_size = val; } /* Get the buffer address */ buf_addr = (buf_ptr & TI1570_RX_BUFPTR_MASK) << 2; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_acquire_rx_buffer: buf_addr = 0x%x\n",buf_addr); #endif /* Read the buffer descriptor itself and store info for caller */ rbh->buf_addr = buf_addr; rbh->buf_size = buf_size; ti1570_read_rx_buffer(d,buf_addr,&rbh->rx_buf); /* Clear the control field */ physmem_copy_u32_to_vm(d->vm,buf_addr+OFFSET(ti1570_rx_buffer_t,ctrl),0); /* Store the ATM header in data buffer */ physmem_copy_u32_to_vm(d->vm,buf_addr+OFFSET(ti1570_rx_buffer_t,atm_hdr), atm_hdr); return(TRUE); } /* Insert a new free buffer in a RX DMA entry */ static void ti1570_insert_rx_free_buf(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, ti1570_rx_buf_holder_t *rbh) { m_uint32_t val,aal_type; aal_type = rde->ctrl & TI1570_RX_DMA_AAL_TYPE_MASK; /* Set current and start of buffer addresses */ rde->cb_addr = rbh->buf_addr + sizeof(ti1570_rx_buffer_t); rde->sb_addr = rbh->buf_addr >> 2; /* Set the buffer length */ val = rbh->buf_size; if (aal_type == TI1570_RX_DMA_AAL_CNT) val |= (rde->aal5_crc & 0xFFFF) << 16; rde->cb_len = val; } /* Store a RX cell */ static int ti1570_store_rx_cell(struct pa_a1_data *d, ti1570_rx_dma_entry_t *rde, m_uint8_t *atm_cell) { m_uint32_t aal_type,atm_hdr,aal5_trailer,pti,real_eop,pti_eop; m_uint32_t prev_buf_addr,buf_len,val,ptr,cnt; ti1570_rx_buf_holder_t rbh; real_eop = pti_eop = FALSE; aal_type = rde->ctrl & TI1570_RX_DMA_AAL_TYPE_MASK; /* Extract PTI from the ATM header */ atm_hdr = ntohl(*(m_uint32_t *)&atm_cell[0]); pti = (atm_hdr & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT; /* PTI == 0x1 => EOP */ if ((pti == 0x01) || (pti == 0x03)) pti_eop = TRUE; if (rde->ctrl & TI1570_RX_DMA_WAIT_EOP) { TI1570_LOG(d,"ti1570_store_rx_cell: EOP processing, not handled yet.\n"); return(FALSE); } /* AAL5 special processing */ if (aal_type == TI1570_RX_DMA_AAL_AAL5) { /* Check that we don't exceed 1366 cells for AAL5 */ /* XXX TODO */ } else { /* EOP processing for non counter-based transparent-AAL packets */ if ((rde->ctrl & TI1570_RX_DMA_WAIT_EOP) && pti_eop) { /* XXX TODO */ } } /* do we have enough room in buffer ? */ buf_len = rde->cb_len & TI1570_RX_DMA_CB_LEN_MASK; if (!buf_len) { prev_buf_addr = rde->sb_addr << 2; /* acquire a new free buffer */ if (!ti1570_acquire_rx_buffer(d,rde,&rbh,atm_hdr)) { rde->ctrl |= TI1570_RX_DMA_WAIT_EOP; return(FALSE); } /* insert the free buffer in the RX DMA structure */ ti1570_insert_rx_free_buf(d,rde,&rbh); /* chain the buffers (keep SOP/EOP bits intact) */ ptr = prev_buf_addr + OFFSET(ti1570_rx_buffer_t,ctrl); val = physmem_copy_u32_from_vm(d->vm,ptr); val |= rde->sb_addr; physmem_copy_u32_to_vm(d->vm,ptr,val); /* read the new buffer length */ buf_len = rde->cb_len & TI1570_RX_DMA_CB_LEN_MASK; } /* copy the ATM payload */ #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_store_rx_cell: storing cell payload at 0x%x " "(buf_addr=0x%x)\n",rde->cb_addr,rde->sb_addr << 2); #endif physmem_copy_to_vm(d->vm,&atm_cell[ATM_HDR_SIZE], rde->cb_addr,ATM_PAYLOAD_SIZE); rde->cb_addr += ATM_PAYLOAD_SIZE; /* update the current buffer length */ val = rde->cb_len & ~TI1570_RX_DMA_CB_LEN_MASK; rde->cb_len = val | (--buf_len); #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_store_rx_cell: new rde->cb_len = 0x%x, " "buf_len=0x%x\n",rde->cb_len,buf_len); #endif /* determine if this is the end of the packet (EOP) */ if (aal_type == TI1570_RX_DMA_AAL_CNT) { /* counter-based tranparent-AAL packets */ cnt = rde->cb_len & TI1570_RX_DMA_TR_CNT_MASK; cnt >>= TI1570_RX_DMA_TR_CNT_SHIFT; /* if the counter reaches 0, this is the EOP */ if (--cnt == 0) real_eop = TRUE; val = rde->cb_len & ~TI1570_RX_DMA_TR_CNT_MASK; val |= cnt << TI1570_RX_DMA_TR_CNT_SHIFT; } else { /* PTI-based transparent AAL packets or AAL5 */ if (pti_eop) real_eop = TRUE; } if (real_eop) { /* mark the buffer as EOP */ ptr = (rde->sb_addr << 2) + OFFSET(ti1570_rx_buffer_t,ctrl); val = physmem_copy_u32_from_vm(d->vm,ptr); val |= TI1570_RX_BUFFER_EOP; physmem_copy_u32_to_vm(d->vm,ptr,val); /* get the aal5 trailer */ aal5_trailer = ntohl(*(m_uint32_t *)&atm_cell[ATM_AAL5_TRAILER_POS]); /* post the entry into the appropriate RX completion ring */ ti1570_update_rx_cring(d,rde,atm_hdr,aal5_trailer,0,TRUE); } return(TRUE); } /* Handle a received ATM cell */ static int ti1570_handle_rx_cell(netio_desc_t *nio, u_char *atm_cell,ssize_t cell_len, struct pa_a1_data *d) { m_uint32_t atm_hdr,vpi,vci,vci_idx,vci_mask; m_uint32_t vci_max,rvd_entry,bptr,pti,ptr; ti1570_rx_dma_entry_t *rde = NULL; ti1570_rx_buf_holder_t rbh; if (cell_len != ATM_CELL_SIZE) { TI1570_LOG(d,"invalid RX cell size (%ld)\n",(long)cell_len); return(FALSE); } /* Extract the VPI/VCI used as index in the RX VPI/VCI DMA pointer table */ atm_hdr = ntohl(*(m_uint32_t *)&atm_cell[0]); vpi = (atm_hdr & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; vci = (atm_hdr & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; pti = (atm_hdr & ATM_HDR_PTI_MASK) >> ATM_HDR_PTI_SHIFT; #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_handle_rx_cell: received cell with VPI/VCI=%u/%u\n", vpi,vci); #endif /* Get the entry corresponding to this VPI in RX VPI/VCI dma ptr table */ rvd_entry = d->rx_vpi_vci_dma_table[vpi]; if (!(rvd_entry & TI1570_RX_VPI_ENABLE)) { TI1570_LOG(d,"ti1570_handle_rx_cell: received cell with " "unknown VPI %u (VCI=%u)\n",vpi,vci); return(FALSE); } /* * Special routing for OAM F4 cells: * - VCI 3 : OAM F4 segment cell * - VCI 4 : OAM F4 end-to-end cell */ if ((vci == 3) || (vci == 4)) rde = &d->rx_dma_table[2]; else { if ((atm_hdr & ATM_PTI_NETWORK) != 0) { switch(pti) { case 0x04: /* OAM F5-segment cell */ case 0x05: /* OAM F5 end-to-end cell */ rde = &d->rx_dma_table[0]; break; case 0x06: case 0x07: rde = &d->rx_dma_table[1]; break; } } else { /* * Standard VPI/VCI. * Apply the VCI mask if we don't have an OAM cell. */ if (!(atm_hdr & ATM_PTI_NETWORK)) { vci_mask = d->iregs[TI1570_REG_TX_RX_FIFO] >> 16; vci_idx = vci & (~vci_mask); vci_max = rvd_entry & TI1570_RX_VCI_RANGE_MASK; if (vci_idx > vci_max) { TI1570_LOG(d,"ti1570_handle_rx_cell: out-of-range VCI %u " "(VPI=%u,vci_mask=%u,vci_max=%u)\n", vci,vpi,vci_mask,vci_max); return(FALSE); } #if DEBUG_RECEIVE TI1570_LOG(d,"ti1570_handle_rx_cell: VPI/VCI=%u/%u, " "vci_mask=0x%x, vci_idx=%u (0x%x), vci_max=%u (0x%x)\n", vpi,vci,vci_mask,vci_idx,vci_idx,vci_max,vci_max); #endif bptr = (rvd_entry & TI1570_RX_BASE_PTR_MASK); bptr >>= TI1570_RX_BASE_PTR_SHIFT; bptr = (bptr + vci) * sizeof(ti1570_rx_dma_entry_t); if (bptr < TI1570_RX_DMA_TABLE_OFFSET) { TI1570_LOG(d,"ti1570_handle_rx_cell: inconsistency in " "RX VPI/VCI table, VPI/VCI=%u/u, bptr=0x%x\n", vpi,vci,bptr); return(FALSE); } bptr -= TI1570_RX_DMA_TABLE_OFFSET; rde = &d->rx_dma_table[bptr / sizeof(ti1570_rx_dma_entry_t)]; } } } if (!rde) { TI1570_LOG(d,"ti1570_handle_rx_cell: no RX DMA table entry found!\n"); return(FALSE); } /* The entry must be active */ if (!(rde->fbr_entry & TI1570_RX_DMA_ON)) return(FALSE); /* Is this the start of a new packet ? */ if (!(rde->ctrl & TI1570_RX_DMA_ACT)) { /* Try to acquire a free buffer */ if (!ti1570_acquire_rx_buffer(d,rde,&rbh,atm_hdr)) { rde->ctrl |= TI1570_RX_DMA_WAIT_EOP; return(FALSE); } /* Insert the free buffer in the RX DMA structure */ ti1570_insert_rx_free_buf(d,rde,&rbh); rde->sp_ptr = rde->sb_addr; /* Mark the RX buffer as the start of packet (SOP) */ ptr = (rde->sb_addr << 2) + OFFSET(ti1570_rx_buffer_t,ctrl); physmem_copy_u32_to_vm(d->vm,ptr,TI1570_RX_BUFFER_SOP); /* Set ACT bit for the DMA channel */ rde->ctrl |= TI1570_RX_DMA_ACT; } /* Store the received cell */ ti1570_store_rx_cell(d,rde,atm_cell); return(TRUE); } /* * pci_ti1570_read() */ static m_uint32_t pci_ti1570_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"pci_ti1570_read: read reg 0x%x\n",reg); #endif switch(reg) { case PCI_REG_BAR0: return(d->dev->phys_addr); default: return(0); } } /* * pci_ti1570_write() */ static void pci_ti1570_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"pci_ti1570_write: write reg 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); TI1570_LOG(d,"registers are mapped at 0x%x\n",value); break; } } /* * pci_plx9060es_read() */ static m_uint32_t pci_plx9060es_read(cpu_gen_t *cpu,struct pci_device *dev, int reg) { struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"PLX9060ES","read reg 0x%x\n",reg); #endif switch(reg) { default: return(0); } } /* * pci_plx9060es_write() */ static void pci_plx9060es_write(cpu_gen_t *cpu,struct pci_device *dev, int reg,m_uint32_t value) { struct pa_a1_data *d = dev->priv_data; #if DEBUG_ACCESS TI1570_LOG(d,"PLX9060ES","write reg 0x%x, value 0x%x\n",reg,value); #endif switch(reg) { } } /* Reset the TI1570 */ static void ti1570_reset(struct pa_a1_data *d,int clear_ctrl_mem) { ti1570_clear_tx_fifo(d); d->tcr_wi_pos = d->tcr_woi_pos = 0; d->rcr_wi_pos = d->rcr_woi_pos = 0; if (clear_ctrl_mem) memset(d->ctrl_mem_ptr,0,TI1570_CTRL_MEM_SIZE); } /* * dev_c7200_pa_a1_init() * * Add a PA-A1 port adapter into specified slot. */ int dev_c7200_pa_a1_init(c7200_t *router,char *name,u_int pa_bay) { struct pci_device *pci_dev_ti,*pci_dev_plx; struct pa_a1_data *d; struct vdevice *dev; m_uint8_t *p; /* Allocate the private data structure for TI1570 chip */ if (!(d = malloc(sizeof(*d)))) { fprintf(stderr,"%s (TI1570): out of memory\n",name); return(-1); } memset(d,0,sizeof(*d)); /* Set the EEPROM */ c7200_pa_set_eeprom(router,pa_bay,cisco_eeprom_find_pa("PA-A1")); /* Add PCI device TI1570 */ pci_dev_ti = pci_dev_add(router->pa_bay[pa_bay].pci_map,name, TI1570_PCI_VENDOR_ID,TI1570_PCI_PRODUCT_ID, 0,0,c7200_net_irq_for_slot_port(pa_bay,0),d, NULL,pci_ti1570_read,pci_ti1570_write); if (!pci_dev_ti) { fprintf(stderr,"%s (TI1570): unable to create PCI device TI1570.\n", name); return(-1); } /* Add PCI device PLX9060ES */ pci_dev_plx = pci_dev_add(router->pa_bay[pa_bay].pci_map,name, PLX_9060ES_PCI_VENDOR_ID, PLX_9060ES_PCI_PRODUCT_ID, 1,0,-1,d, NULL,pci_plx9060es_read,pci_plx9060es_write); if (!pci_dev_plx) { fprintf(stderr,"%s (PLX_9060ES): unable to create PCI device " "PLX 9060ES.\n",name); return(-1); } /* Create the TI1570 structure */ d->name = name; d->vm = router->vm; d->pci_dev_ti = pci_dev_ti; d->pci_dev_plx = pci_dev_plx; /* Allocate the control memory */ if (!(d->ctrl_mem_ptr = malloc(TI1570_CTRL_MEM_SIZE))) { fprintf(stderr,"%s (PA-A1): unable to create control memory.\n",name); return(-1); } /* Standard tables for the TI1570 */ p = (m_uint8_t *)d->ctrl_mem_ptr; d->iregs = (m_uint32_t *)(p + TI1570_INTERNAL_REGS_OFFSET); d->tx_sched_table = (m_uint32_t *)(p + TI1570_TX_SCHED_OFFSET); d->tx_dma_table = (ti1570_tx_dma_entry_t *)(p + TI1570_TX_DMA_TABLE_OFFSET); d->rx_vpi_vci_dma_table = (m_uint32_t *)(p+TI1570_RX_DMA_PTR_TABLE_OFFSET); d->rx_dma_table = (ti1570_rx_dma_entry_t *)(p + TI1570_RX_DMA_TABLE_OFFSET); d->rx_fbr_table = (ti1570_rx_fbr_entry_t *)(p + TI1570_FREE_BUFFERS_OFFSET); ti1570_reset(d,TRUE); /* Create the device itself */ if (!(dev = dev_create(name))) { fprintf(stderr,"%s (PA-A1): unable to create device.\n",name); return(-1); } dev->phys_addr = 0; dev->phys_len = 0x200000; dev->handler = dev_pa_a1_access; /* Store device info */ dev->priv_data = d; d->dev = dev; /* Store device info into the router structure */ return(c7200_pa_set_drvinfo(router,pa_bay,d)); } /* Remove a PA-A1 from the specified slot */ int dev_c7200_pa_a1_shutdown(c7200_t *router,u_int pa_bay) { struct c7200_pa_bay *bay; struct pa_a1_data *d; if (!(bay = c7200_pa_get_info(router,pa_bay))) return(-1); d = bay->drv_info; /* Remove the PA EEPROM */ c7200_pa_unset_eeprom(router,pa_bay); /* Remove the PCI devices */ pci_dev_remove(d->pci_dev_ti); pci_dev_remove(d->pci_dev_plx); /* Remove the device from the VM address space */ vm_unbind_device(router->vm,d->dev); cpu_group_rebuild_mts(router->vm->cpu_group); /* Free the control memory */ free(d->ctrl_mem_ptr); /* Free the device structure itself */ free(d->dev); free(d); return(0); } /* Bind a Network IO descriptor to a specific port */ int dev_c7200_pa_a1_set_nio(c7200_t *router,u_int pa_bay,u_int port_id, netio_desc_t *nio) { struct pa_a1_data *d; if ((port_id > 0) || !(d = c7200_pa_get_drvinfo(router,pa_bay))) return(-1); if (d->nio != NULL) return(-1); d->nio = nio; d->tx_tid = ptask_add((ptask_callback)ti1570_scan_tx_sched_table,d,NULL); netio_rxl_add(nio,(netio_rx_handler_t)ti1570_handle_rx_cell,d,NULL); return(0); } /* Unbind a Network IO descriptor to a specific port */ int dev_c7200_pa_a1_unset_nio(c7200_t *router,u_int pa_bay,u_int port_id) { struct pa_a1_data *d; if ((port_id > 0) || !(d = c7200_pa_get_drvinfo(router,pa_bay))) return(-1); if (d->nio) { ptask_remove(d->tx_tid); netio_rxl_remove(d->nio); d->nio = NULL; } return(0); } /* PA-A1 driver */ struct c7200_pa_driver dev_c7200_pa_a1_driver = { "PA-A1", 1, dev_c7200_pa_a1_init, dev_c7200_pa_a1_shutdown, dev_c7200_pa_a1_set_nio, dev_c7200_pa_a1_unset_nio, NULL, };