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, &regs->dma_ctrl, GRTM_DMA_CTRL_TXRST);
	WRITE_REG(pDev, &regs->dma_ctrl, 0);	/* Leave Reset */

	/* Clear old interrupts */
	WRITE_REG(pDev, &regs->dma_status, GRTM_DMA_STS_ALL);

	/* Set Descriptor Pointer Base register to point to first descriptor */
	WRITE_REG(pDev, &regs->dma_bd, pDev->bds);
	/*drvmgr_mmap_translate(pDev->dev, 0, (void *)pDev->bds, (void **)&regs->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, &regs->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 = &regs->dma_ctrl;
		txrdy_mask = GRTM_REV0_DMA_CTRL_TXRDY;
	} else {
		txrdy_reg = &regs->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, &regs->dma_ctrl, GRTM_DMA_CTRL_IE);

	DBG("GRTM: STARTED\n");

	return RTEMS_SUCCESSFUL;
}
示例#3
0
文件: grtm.c 项目: gedare/rtems
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 **)&regs->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(&regs->dma_ctrl) &
				GRTM_REV0_DMA_CTRL_TXRDY;
		} else {
			txrdy = READ_REG(&regs->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;
}