static void grtm_tx_process(struct grtm_priv *pDev) { int num; /* 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; /* Use all available free descriptors there are frames for * in the ready queue. * (READY->SCHEDULED) */ if (pDev->running) { num = grtm_schedule_ready(pDev); pDev->ready_cnt -= num; pDev->scheduled_cnt += num; } }
static void grtm_interrupt(void *arg) { struct grtm_priv *pDev = arg; struct grtm_regs *regs = pDev->regs; unsigned int status; int num; /* Clear interrupt by reading it */ status = READ_REG(pDev, ®s->dma_status); /* Spurious Interrupt? */ if ( !pDev->running ) return; if ( status ) WRITE_REG(pDev, ®s->dma_status, status); if ( status & GRTM_DMA_STS_TFF ){ pDev->stats.err_transfer_frame++; } if ( status & GRTM_DMA_STS_TA ){ pDev->stats.err_ahb++; } if ( status & GRTM_DMA_STS_TE ){ pDev->stats.err_tx++; } if ( status & GRTM_DMA_STS_TI ){ if ( pDev->config.isr_desc_proc && (rtems_semaphore_obtain(pDev->handling_transmission, RTEMS_NO_WAIT, 0) == RTEMS_SUCCESSFUL) ) { /* 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; /* Use all available free descriptors there are frames for * in the ready queue. * (READY->SCHEDULED) */ num = grtm_schedule_ready(pDev,1); pDev->ready_cnt -= num; pDev->scheduled_cnt += num; rtems_semaphore_release(pDev->handling_transmission); #if 0 if ( (pDev->config.blocking==GRTM_BLKMODE_COMPLETE) && pDev->timeout ){ /* Signal to thread only if enough data is available */ if ( pDev->wait_for_frames > grtm_data_avail(pDev) ){ /* Not enough data available */ goto procceed_processing_interrupts; } /* Enough number of frames has been transmitted which means that * the waiting thread should be woken up. */ rtems_semaphore_release(pDev->sem_tx); } #endif } if ( pDev->config.blocking == GRTM_BLKMODE_BLK ) { /* Blocking mode */ #if 0 /* Disable further Interrupts until handled by waiting task. */ regs->dma_ctrl = READ_REG(pDev, ®s->dma_ctrl) & ~GRTM_DMA_CTRL_IE; #endif /* Signal Semaphore to wake waiting thread in ioctl(SEND|RECLAIM) */ rtems_semaphore_release(pDev->sem_tx); } } #if 0 procceed_processing_interrupts: ; #endif }
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; }