static rtems_device_driver grtm_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg) { struct grtm_priv *pDev; struct drvmgr_dev *dev; rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t *)arg; unsigned int *data = ioarg->buffer; int status; struct grtm_ioc_config *cfg; struct grtm_ioc_hw_status *hwregs; IRQ_GLOBAL_PREPARE(oldLevel); struct grtm_list *chain; struct grtm_frame *curr; struct grtm_ioc_hw *hwimpl; struct grtm_ioc_stats *stats; int num,ret; FUNCDBG(); if ( drvmgr_get_dev(&grtm_rmap_drv_info.general, minor, &dev) ) { return RTEMS_INVALID_NUMBER; } pDev = (struct grtm_priv *)dev->priv; if (!ioarg) return RTEMS_INVALID_NAME; ioarg->ioctl_return = 0; switch(ioarg->command) { case GRTM_IOC_START: if ( pDev->running ) { return RTEMS_RESOURCE_IN_USE; /* EBUSY */ } if ( (status=grtm_start(pDev)) != RTEMS_SUCCESSFUL ){ return status; } /* Register ISR & Enable interrupt */ drvmgr_interrupt_register(dev, 0, "grtm_rmap", grtm_interrupt, pDev); /* Read and write are now open... */ break; case GRTM_IOC_STOP: if ( !pDev->running ) { return RTEMS_RESOURCE_IN_USE; } /* Disable interrupts */ drvmgr_interrupt_unregister(dev, 0, grtm_interrupt, pDev); grtm_stop(pDev); pDev->running = 0; break; case GRTM_IOC_ISSTARTED: if ( !pDev->running ) { return RTEMS_RESOURCE_IN_USE; } break; case GRTM_IOC_SET_BLOCKING_MODE: if ( (unsigned int)data > GRTM_BLKMODE_BLK ) { return RTEMS_INVALID_NAME; } DBG("GRTM: Set blocking mode: %d\n",(unsigned int)data); pDev->config.blocking = (unsigned int)data; break; case GRTM_IOC_SET_TIMEOUT: DBG("GRTM: Timeout: %d\n",(unsigned int)data); pDev->config.timeout = (rtems_interval)data; break; case GRTM_IOC_SET_CONFIG: cfg = (struct grtm_ioc_config *)data; if ( !cfg ) { return RTEMS_INVALID_NAME; } if ( pDev->running ) { return RTEMS_RESOURCE_IN_USE; } pDev->config = *cfg; break; case GRTM_IOC_GET_STATS: stats = (struct grtm_ioc_stats *)data; if ( !stats ) { return RTEMS_INVALID_NAME; } memcpy(stats,&pDev->stats,sizeof(struct grtm_ioc_stats)); break; case GRTM_IOC_CLR_STATS: memset(&pDev->stats,0,sizeof(struct grtm_ioc_stats)); break; case GRTM_IOC_GET_CONFIG: cfg = (struct grtm_ioc_config *)data; if ( !cfg ) { return RTEMS_INVALID_NAME; } *cfg = pDev->config; break; case GRTM_IOC_GET_OCFREG: if ( !pDev->hw_avail.ocf ) { /* Hardware does not implement the OCF register */ return RTEMS_NOT_DEFINED; } if ( !data ) { return RTEMS_INVALID_NAME; } #warning THIS IOCTL COPY THE REMOTE ADDRESS *(unsigned int **)data = (unsigned int *)&pDev->regs->ocf; break; case GRTM_IOC_GET_HW_IMPL: hwimpl = (struct grtm_ioc_hw *)data; if ( !hwimpl ) { return RTEMS_INVALID_NAME; } *hwimpl = pDev->hw_avail; break; case GRTM_IOC_GET_HW_STATUS: hwregs = (struct grtm_ioc_hw_status *)data; if ( !hwregs ) { return RTEMS_INVALID_NAME; } /* We disable interrupt in order to get a snapshot of the registers */ IRQ_GLOBAL_DISABLE(oldLevel); #warning IMPLEMENT HWREGS HERE IRQ_GLOBAL_ENABLE(oldLevel); break; /* Put a chain of frames at the back of the "Ready frames" queue. This * triggers the driver to put frames from the Ready queue into unused * available descriptors. (Ready -> Scheduled) */ case GRTM_IOC_SEND: if ( !pDev->running ){ return RTEMS_RESOURCE_IN_USE; } num=0; /* Get pointer to frame chain wished be sent */ chain = (struct grtm_list *)ioarg->buffer; if ( !chain ){ /* No new frames to send ==> just trigger hardware * to send previously made ready frames to be sent. */ rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_WAIT, RTEMS_NO_TIMEOUT); goto trigger_transmission; } if ( !chain->tail || !chain->head ){ return RTEMS_INVALID_NAME; } DBG("GRTM_SEND: head: 0x%x, tail: 0x%x\n",chain->head,chain->tail); /* Mark ready frames unsent by clearing GRTM_FLAGS_SENT of all frames */ curr = chain->head; while(curr != chain->tail){ curr->flags = curr->flags & ~(GRTM_FLAGS_SENT|GRRM_FLAGS_ERR); curr = curr->next; num++; } curr->flags = curr->flags & ~(GRTM_FLAGS_SENT|GRRM_FLAGS_ERR); num++; rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_WAIT, RTEMS_NO_TIMEOUT); /* 1. Put frames into ready queue * (New Frames->READY) */ if ( pDev->ready.head ){ /* Frames already on ready queue (no free descriptors previously) ==> * Put frames at end of ready queue */ pDev->ready.tail->next = chain->head; pDev->ready.tail = chain->tail; chain->tail->next = NULL; }else{ /* All frames is put into the ready queue for later processing */ pDev->ready.head = chain->head; pDev->ready.tail = chain->tail; chain->tail->next = NULL; } pDev->ready_cnt += num; /* Added 'num' frames to ready queue */ trigger_transmission: /* 2. Free used descriptors and put the sent frame into the "Sent queue" * (SCHEDULED->SENT) */ num = grtm_free_sent(pDev); pDev->scheduled_cnt -= num; pDev->sent_cnt += num; /* 3. Use all available free descriptors there are frames for * in the ready queue. * (READY->SCHEDULED) */ num = grtm_schedule_ready(pDev,0); pDev->ready_cnt -= num; pDev->scheduled_cnt += num; rtems_semaphore_release(pDev->handling_transmission); break; /* Take all available sent frames from the "Sent frames" queue. * If no frames has been sent, the thread may get blocked if in blocking * mode. The blocking mode is not available if driver is not in running mode. * * Note this ioctl may return success even if the driver is not in STARTED mode. * This is because in case of a error (link error of similar) and the driver switch * from START to STOP mode we must still be able to get our frames back. * * Note in case the driver fails to send a frame for some reason (link error), * the sent flag is set to 0 indicating a failure. * */ case GRTM_IOC_RECLAIM: /* Get pointer to were to place reaped chain */ chain = (struct grtm_list *)ioarg->buffer; if ( !chain ){ return RTEMS_INVALID_NAME; } /* Lock out interrupt handler */ rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_WAIT, RTEMS_NO_TIMEOUT); do { /* Move sent frames from descriptors to Sent queue. This makes more * descriptors (BDs) available. */ num = grtm_free_sent(pDev); pDev->scheduled_cnt -= num; pDev->sent_cnt += num; if ( pDev->running ){ /* Fill descriptors with as many frames from the ready list * as possible. */ num = grtm_schedule_ready(pDev,0); pDev->ready_cnt -= num; pDev->scheduled_cnt += num; } /* Are there any frames on the sent queue waiting to be * reclaimed? */ if ( !pDev->sent.head ){ /* No frames to reclaim - no frame in sent queue. * Instead we block thread until frames have been sent * if in blocking mode. */ if ( pDev->running && pDev->config.blocking ){ ret = rtems_semaphore_obtain(pDev->sem_tx,RTEMS_WAIT,pDev->config.timeout); if ( ret == RTEMS_TIMEOUT ) { /* do not lock out interrupt handler any more */ rtems_semaphore_release(pDev->handling_transmission); return RTEMS_TIMEOUT; } else if ( ret == RTEMS_SUCCESSFUL ) { /* There might be frames available, go check */ continue; } else { /* any error (driver closed, internal error etc.) */ rtems_semaphore_release(pDev->handling_transmission); return RTEMS_UNSATISFIED; } }else{ /* non-blocking mode, we quit */ chain->head = NULL; chain->tail = NULL; /* do not lock out interrupt handler any more */ rtems_semaphore_release(pDev->handling_transmission); return RTEMS_TIMEOUT; } }else{ /* Take all sent framess from sent queue to userspace queue */ chain->head = pDev->sent.head; chain->tail = pDev->sent.tail; chain->tail->next = NULL; /* Just for sure */ /* Mark no Sent */ grtm_list_clr(&pDev->sent); DBG("TX_RECLAIM: head: 0x%x, tail: 0x%x\n",chain->head,chain->tail); break; } }while(1); /* do not lock out interrupt handler any more */ rtems_semaphore_release(pDev->handling_transmission); break; default: return RTEMS_NOT_DEFINED; } return RTEMS_SUCCESSFUL; }
static int grtm_start(struct grtm_priv *pDev) { struct grtm_regs *regs = pDev->regs; int i; struct grtm_ioc_config *cfg = &pDev->config; volatile unsigned int *txrdy_reg; unsigned int txrdy_mask; unsigned int frame_buffer_ofs; /* Clear Descriptors */ MEMSET(pDev, pDev->bds, 0, 0x400); /* Clear stats */ memset(&pDev->stats,0,sizeof(struct grtm_ioc_stats)); /* Init Descriptor Ring */ memset(pDev->_ring, 0, sizeof(struct grtm_ring)*DESCRIPTOR_MAX); frame_buffer_ofs = 0; for(i=0; i<DESCRIPTOR_MAX-1; i++){ pDev->_ring[i].next = &pDev->_ring[i+1]; pDev->_ring[i].bd = &pDev->bds[i]; pDev->_ring[i].frm = NULL; pDev->_ring[i].buf = &pDev->_frame_buffers[frame_buffer_ofs]; WRITE_REG(pDev, &pDev->_ring[i].bd->address, pDev->_ring[i].buf); /* INIT BD ADDRESS */ frame_buffer_ofs += pDev->frame_length_max; } pDev->_ring[DESCRIPTOR_MAX-1].next = &pDev->_ring[0]; pDev->_ring[DESCRIPTOR_MAX-1].bd = &pDev->bds[DESCRIPTOR_MAX-1]; pDev->_ring[DESCRIPTOR_MAX-1].frm = NULL; pDev->_ring[DESCRIPTOR_MAX-1].buf = &pDev->_frame_buffers[frame_buffer_ofs]; WRITE_REG(pDev, &pDev->_ring[DESCRIPTOR_MAX-1].bd->address, pDev->_ring[DESCRIPTOR_MAX-1].buf); /* INIT BD ADDRESS */ pDev->ring = &pDev->_ring[0]; pDev->ring_end = &pDev->_ring[0]; /* Clear Scheduled, Ready and Sent list */ grtm_list_clr(&pDev->ready); grtm_list_clr(&pDev->scheduled); grtm_list_clr(&pDev->sent); /* Software init */ /*pDev->handling_transmission = 0;*/ /* Reset the transmitter */ WRITE_REG(pDev, ®s->dma_ctrl, GRTM_DMA_CTRL_TXRST); WRITE_REG(pDev, ®s->dma_ctrl, 0); /* Leave Reset */ /* Clear old interrupts */ WRITE_REG(pDev, ®s->dma_status, GRTM_DMA_STS_ALL); /* Set Descriptor Pointer Base register to point to first descriptor */ WRITE_REG(pDev, ®s->dma_bd, pDev->bds); /*drvmgr_mmap_translate(pDev->dev, 0, (void *)pDev->bds, (void **)®s->dma_bd);*/ /*regs->dma_bd = (unsigned int)pDev->bds;*/ /* Set hardware options as defined by config */ if ( grtm_hw_set_config(pDev, cfg, &pDev->hw_avail) ) { return RTEMS_IO_ERROR; } /* Enable TM Transmitter */ WRITE_REG(pDev, ®s->ctrl, GRTM_CTRL_EN); /* Wait for TXRDY to be cleared */ i=1000; while( i > 0 ) { asm volatile ("nop"::); i--; } /* Location of TXRDY Bit is different for different revisions */ if ( pDev->subrev == 0 ) { txrdy_reg = ®s->dma_ctrl; txrdy_mask = GRTM_REV0_DMA_CTRL_TXRDY; } else { txrdy_reg = ®s->dma_status; txrdy_mask = GRTM_REV1_DMA_STS_TXRDY; } /* Check transmitter startup OK */ i=0; while( !(READ_REG(pDev, txrdy_reg) & txrdy_mask) && (i<1000) ){ i++; } if ( !(READ_REG(pDev, txrdy_reg) & txrdy_mask) ){ /* Reset Failed */ DBG("GRTM: start: Reseting transmitter failed (%d)\n",i); return RTEMS_IO_ERROR; } DBG("GRTM: reset time %d\n",i); /* Everything is configured, the TM transmitter is started * and idle frames has been sent. */ /* Mark running before enabling the DMA transmitter */ pDev->running = 1; /* Enable interrupts (Error and DMA TX) */ WRITE_REG(pDev, ®s->dma_ctrl, GRTM_DMA_CTRL_IE); DBG("GRTM: STARTED\n"); return RTEMS_SUCCESSFUL; }
static int grtm_start(struct grtm_priv *pDev) { struct grtm_regs *regs = pDev->regs; int i; struct grtm_ioc_config *cfg = &pDev->config; unsigned int txrdy; /* Clear Descriptors */ memset(pDev->bds,0,0x400); /* Clear stats */ memset(&pDev->stats,0,sizeof(struct grtm_ioc_stats)); /* Init Descriptor Ring */ memset(pDev->_ring,0,sizeof(struct grtm_ring)*128); for(i=0;i<127;i++){ pDev->_ring[i].next = &pDev->_ring[i+1]; pDev->_ring[i].bd = &pDev->bds[i]; pDev->_ring[i].frm = NULL; } pDev->_ring[127].next = &pDev->_ring[0]; pDev->_ring[127].bd = &pDev->bds[127]; pDev->_ring[127].frm = NULL; pDev->ring = &pDev->_ring[0]; pDev->ring_end = &pDev->_ring[0]; /* Clear Scheduled, Ready and Sent list */ grtm_list_clr(&pDev->ready); grtm_list_clr(&pDev->scheduled); grtm_list_clr(&pDev->sent); /* Software init */ pDev->handling_transmission = 0; /* Reset the transmitter */ regs->dma_ctrl = GRTM_DMA_CTRL_TXRST; regs->dma_ctrl = 0; /* Leave Reset */ /* Clear old interrupts */ regs->dma_status = GRTM_DMA_STS_ALL; /* Set Descriptor Pointer Base register to point to first descriptor */ drvmgr_translate_check(pDev->dev, CPUMEM_TO_DMA, (void *)pDev->bds, (void **)®s->dma_bd, 0x400); /* Set hardware options as defined by config */ if ( grtm_hw_set_config(pDev, cfg, &pDev->hw_avail) ) { return RTEMS_IO_ERROR; } /* Enable TM Transmitter */ regs->ctrl = GRTM_CTRL_EN; /* Wait for TXRDY to be cleared */ i=1000; while( i > 0 ) { asm volatile ("nop"::); i--; } /* Check transmitter startup OK */ i = 1000000; do { /* Location of TXRDY Bit is different for different revisions */ if ( pDev->subrev == 0 ) { txrdy = READ_REG(®s->dma_ctrl) & GRTM_REV0_DMA_CTRL_TXRDY; } else { txrdy = READ_REG(®s->dma_status) & GRTM_REV1_DMA_STS_TXRDY; } if (txrdy != 0) break; asm volatile ("nop"::); } while ( --i > 0 ); if ( i == 0 ) { /* Reset Failed */ DBG("GRTM: start: Reseting transmitter failed (%d)\n",i); return RTEMS_IO_ERROR; } DBG("GRTM: reset time %d\n",i); /* Everything is configured, the TM transmitter is started * and idle frames has been sent. */ /* Mark running before enabling the DMA transmitter */ pDev->running = 1; /* Enable interrupts (Error and DMA TX) */ regs->dma_ctrl = GRTM_DMA_CTRL_IE; DBG("GRTM: STARTED\n"); return RTEMS_SUCCESSFUL; }