summaryrefslogblamecommitdiffstats
path: root/bsps/shared/grlib/can/satcan.c
blob: a94d57095525f6f4e112b1f3f2dfdcffa7156e5c (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                           
                                         








                                     

                         
 
                             
 






























































































































                                                                                                                           
                              


                                                        
                                                  





















































































                                                                                                                    
                                                      

















                                                                        
                                                      

















                                                                           
                                                      

















                                                                            
                                                      































































































































































































































































































                                                                                                                         
                                                                                    






































































                                                                                          
                                                               

                                                        

















                                                                                                                           
                                                           




                                                                                         
                                                                     






















                                                                                                           
/*
 *  SatCAN FPGA driver
 * 
 *  COPYRIGHT (c) 2008.
 *  Cobham Gaisler AB.
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#include <rtems/libio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <bsp.h>
#include <rtems/bspIo.h> /* printk */

#include <grlib/satcan.h>
#include <grlib/ambapp.h>

#include <grlib/grlib_impl.h>

#ifndef GAISLER_SATCAN
#define GAISLER_SATCAN 0x080
#endif

#if !defined(SATCAN_DEVNAME) 
 #undef SATCAN_DEVNAME
 #define SATCAN_DEVNAME "/dev/satcan"
#endif

/* Enable debug output? */
/* #define DEBUG */ 

#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...) 
#endif


/* Defines related to DMA */
#define ALIGN_2KMEM 32*1024
#define ALIGN_8KMEM 128*1024

#define OFFSET_2K_LOW_POS 15
#define OFFSET_8K_LOW_POS 17

#define DMA_2K_DATA_SELECT (1 << 14)
#define DMA_8K_DATA_SELECT (1 << 16)

#define DMA_2K_DATA_OFFSET 16*1024
#define DMA_8K_DATA_OFFSET 64*1024

/* Core register structures and defines */

/* Indexes to SatCAN registers in satcan array are declared in satcan.h*/
/* Fields for some of the SatCAN FPGA registers */

/* CmdReg0 */
#define CAN_TODn_Int_sel   (1 << 5)

/* CmdReg1 */
#define Sel_2k_8kN         (1 << 0)

/* Read FIFO */
#define FIFO_Full          (1 << 8)
#define FIFO_Empty         (1 << 9)

/* DMA Ch_Enable */
#define DMA_AutoInitDmaTx  (1 << 3)
#define DMA_EnTx2          (1 << 2)
#define DMA_EnTx1          (1 << 1)
#define DMA_EnRx           (1 << 0)

/* SatCAN wrapper register fields */
#define CTRL_BT_P     9
#define CTRL_NODENO_P 5
#define CTRL_DIS      (1 << 2)
#define CTRL_DPS_P    1
#define CTRL_RST      (1 << 0)

#define IRQ_AHB       (1 << 8)
#define IRQ_PPS       (1 << 7)
#define IRQ_M5        (1 << 6)
#define IRQ_M4        (1 << 5)
#define IRQ_M3        (1 << 4)
#define IRQ_M2        (1 << 3)
#define IRQ_M1        (1 << 2)
#define IRQ_SYNC      (1 << 1)
#define IRQ_CAN       (1 << 0)

#define MSK_AHB       (1 << 8)
#define MSK_PPS       (1 << 7)
#define MSK_M5        (1 << 6)
#define MSK_M4        (1 << 5)
#define MSK_M3        (1 << 4)
#define MSK_M2        (1 << 3)
#define MSK_M1        (1 << 2)
#define MSK_SYNC      (1 << 1)
#define MSK_CAN       (1 << 0)



struct satcan_regs {
	volatile unsigned int satcan[32];
	volatile unsigned int ctrl;
	volatile unsigned int irqpend;
	volatile unsigned int irqmask;
	volatile unsigned int membase;
};


struct satcan_priv {
	/* config */
	void           *dmaptr;
	unsigned char  *alptr;
	satcan_config  *cfg;
	
	/* driver state */
	rtems_id       devsem;
	rtems_id       txsem;
	int            open;
	int            txactive;
	int            dmaen;
	int            doff;
	rtems_interval timeout;
	int            dmamode;
};

static struct satcan_regs *regs;
static struct satcan_priv *priv;

static rtems_device_driver satcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver satcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver satcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver satcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver satcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg);
static rtems_device_driver satcan_initialize(rtems_device_major_number major, rtems_device_minor_number unused, void *arg);


/*
 * almalloc: allocate memory area of size sz aligned on sz boundary 
 * alptr: Utilized to return aligned pointer
 * ptr:   Unaligned pointer
 * sz:    Size of memory area
 */
static void almalloc(unsigned char **alptr, void **ptr, int sz)
{
  *ptr = rtems_calloc(1,2*sz);
  *alptr = (unsigned char *) (((int)*ptr+sz) & ~(sz-1));
}

static rtems_isr satcan_interrupt_handler(void *v)
{
	unsigned int irq;
	unsigned int fifo;
	
	irq = regs->irqpend;
	
	if (irq & IRQ_AHB && priv->cfg->ahb_irq_callback) {
		priv->cfg->ahb_irq_callback();
	}
	if (irq & IRQ_PPS && priv->cfg->pps_irq_callback) {
		priv->cfg->pps_irq_callback();
	}
	if (irq & IRQ_M5 && priv->cfg->m5_irq_callback) {
		priv->cfg->m5_irq_callback();
	}
	if (irq & IRQ_M4 && priv->cfg->m4_irq_callback) {
		priv->cfg->m4_irq_callback();
	}
	if (irq & IRQ_M3 && priv->cfg->m3_irq_callback) {
		priv->cfg->m3_irq_callback();
	}
	if (irq & IRQ_M2 && priv->cfg->m2_irq_callback) {
		priv->cfg->m2_irq_callback();
	}
	if (irq & IRQ_M1 && priv->cfg->m1_irq_callback) {
		priv->cfg->m1_irq_callback();
	}
	if (irq & IRQ_SYNC && priv->cfg->sync_irq_callback) {
		priv->cfg->sync_irq_callback();
	}
	if (irq & IRQ_CAN) {
		fifo = regs->satcan[SATCAN_FIFO];
		if (!(fifo & FIFO_Empty) && priv->txactive &&
		    (((fifo & 0xff) == SATCAN_IRQ_EOD1) || ((fifo & 0xff) == SATCAN_IRQ_EOD2))) { 
			rtems_semaphore_release(priv->txsem);
		}
		if (priv->cfg->can_irq_callback)
			priv->cfg->can_irq_callback(fifo);
	}
}



static rtems_device_driver satcan_ioctl(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
	rtems_libio_ioctl_args_t *ioarg = (rtems_libio_ioctl_args_t*)arg;
	int *value;
	rtems_interval *timeout;
	satcan_regmod *regmod;

	DBG("SatCAN:  IOCTL %d\n\r", ioarg->command);

	ioarg->ioctl_return = 0;
	switch(ioarg->command) {
	case SATCAN_IOC_DMA_2K:
		DBG("SatCAN: ioctl: setting 2K DMA mode\n\r");
		free(priv->dmaptr);
		almalloc(&priv->alptr, &priv->dmaptr, ALIGN_2KMEM);
		if (priv->dmaptr == NULL) {
			printk("SatCAN: Failed to allocate DMA memory\n\r");
			return RTEMS_NO_MEMORY;
		}

		regs->membase = (unsigned int)priv->alptr;
		regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> OFFSET_2K_LOW_POS;
		regs->satcan[SATCAN_CMD1] = regs->satcan[SATCAN_CMD1] | Sel_2k_8kN;
		break;

	case SATCAN_IOC_DMA_8K:
		DBG("SatCAN: ioctl: setting 8K DMA mode\n\r");
		free(priv->dmaptr);
		almalloc(&priv->alptr, &priv->dmaptr, ALIGN_8KMEM);
		if (priv->dmaptr == NULL) {
			printk("SatCAN: Failed to allocate DMA memory\n\r");
			return RTEMS_NO_MEMORY;
		}
		
		regs->membase = (unsigned int)priv->alptr;
		regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> OFFSET_8K_LOW_POS;
		regs->satcan[SATCAN_CMD1] = regs->satcan[SATCAN_CMD1] & ~Sel_2k_8kN;
		break;
		
	case SATCAN_IOC_GET_REG:
		/* Get regmod structure from argument */
		regmod = (satcan_regmod*)ioarg->buffer;
		DBG("SatCAN: ioctl: getting register %d\n\r", regmod->reg);
		if (regmod->reg <= SATCAN_FILTER_STOP)
			regmod->val = regs->satcan[regmod->reg];
		else if (regmod->reg == SATCAN_WCTRL)
			regmod->val = regs->ctrl;
		else if (regmod->reg == SATCAN_WIPEND)
			regmod->val = regs->irqpend;
		else if (regmod->reg == SATCAN_WIMASK)
			regmod->val = regs->irqmask;
		else if (regmod->reg == SATCAN_WAHBADDR)
			regmod->val = regs->membase;
		else
			return RTEMS_INVALID_NAME;
		break;

	case SATCAN_IOC_SET_REG:
		/* Get regmod structure from argument */
		regmod = (satcan_regmod*)ioarg->buffer;
		DBG("SatCAN: ioctl: setting register %d, value %x\n\r", 
		    regmod->reg, regmod->val);
		if (regmod->reg <= SATCAN_FILTER_STOP)
			regs->satcan[regmod->reg] = regmod->val;
		else if (regmod->reg == SATCAN_WCTRL)
			regs->ctrl = regmod->val;
		else if (regmod->reg == SATCAN_WIPEND)
			regs->irqpend = regmod->val;
		else if (regmod->reg == SATCAN_WIMASK)
			regs->irqmask = regmod->val;
		else if (regmod->reg == SATCAN_WAHBADDR)
			regs->membase = regmod->val;
		else
			return RTEMS_INVALID_NAME;
		break;

	case SATCAN_IOC_OR_REG:
		/* Get regmod structure from argument */
		regmod = (satcan_regmod*)ioarg->buffer;
		DBG("SatCAN: ioctl: or:ing register %d, with value %x\n\r",
		    regmod->reg, regmod->val);
		if (regmod->reg <= SATCAN_FILTER_STOP)
			regs->satcan[regmod->reg] |= regmod->val;
		else if (regmod->reg == SATCAN_WCTRL)
			regs->ctrl |= regmod->val;
		else if (regmod->reg == SATCAN_WIPEND)
			regs->irqpend |= regmod->val;
		else if (regmod->reg == SATCAN_WIMASK)
			regs->irqmask |= regmod->val;
		else if (regmod->reg == SATCAN_WAHBADDR)
			regs->membase |= regmod->val;
		else
			return RTEMS_INVALID_NAME;
		break;

	case SATCAN_IOC_AND_REG:
		/* Get regmod structure from argument */
		regmod = (satcan_regmod*)ioarg->buffer;
		DBG("SatCAN: ioctl: masking register %d, with value %x\n\r",
		    regmod->reg, regmod->val);
		if (regmod->reg <= SATCAN_FILTER_STOP)
			regs->satcan[regmod->reg] &= regmod->val;
		else if (regmod->reg == SATCAN_WCTRL)
			regs->ctrl &= regmod->val;
		else if (regmod->reg == SATCAN_WIPEND)
			regs->irqpend &= regmod->val;
		else if (regmod->reg == SATCAN_WIMASK)
			regs->irqmask &= regmod->val;
		else if (regmod->reg == SATCAN_WAHBADDR)
			regs->membase &= regmod->val;
		else
			return RTEMS_INVALID_NAME;
		break;

	case SATCAN_IOC_EN_TX1_DIS_TX2:
		priv->dmaen = SATCAN_DMA_ENABLE_TX1;
		break;

	case SATCAN_IOC_EN_TX2_DIS_TX1:
		priv->dmaen = SATCAN_DMA_ENABLE_TX2;
		break;

	case SATCAN_IOC_GET_DMA_MODE:
		value = (int*)ioarg->buffer;
		*value = priv->dmamode;
		break;
		
	case SATCAN_IOC_SET_DMA_MODE:
		value = (int*)ioarg->buffer;
		if (*value != SATCAN_DMA_MODE_USER && *value != SATCAN_DMA_MODE_SYSTEM) {
			DBG("SatCAN: ioctl: invalid DMA mode\n\r");
			return RTEMS_INVALID_NAME;
		}
		priv->dmamode = *value;
		break;

	case SATCAN_IOC_ACTIVATE_DMA:
		if (priv->dmamode != SATCAN_DMA_MODE_USER) {
			DBG("SatCAN: ioctl: ACTIVATE_DMA: not in user mode\n\r");
			return RTEMS_INVALID_NAME;
		}
		value = (int*)ioarg->buffer;
		if (*value != SATCAN_DMA_ENABLE_TX1 && *value != SATCAN_DMA_ENABLE_TX2) {
			DBG("SatCAN: ioctl: ACTIVATE_DMA: Illegal channel\n\r");
			return RTEMS_INVALID_NAME;
		}
		regs->satcan[SATCAN_DMA] |= *value << 1;
		break;

	case SATCAN_IOC_DEACTIVATE_DMA:
		if (priv->dmamode != SATCAN_DMA_MODE_USER) {
			DBG("SatCAN: ioctl: DEACTIVATE_DMA: not in user mode\n\r");
			return RTEMS_INVALID_NAME;
		}
		value = (int*)ioarg->buffer;
		if (*value != SATCAN_DMA_ENABLE_TX1 && *value != SATCAN_DMA_ENABLE_TX2) {
			DBG("SatCAN: ioctl: DEACTIVATE_DMA: Illegal channel\n\r");
			return RTEMS_INVALID_NAME;
		}
		regs->satcan[SATCAN_DMA] &= ~(*value << 1);
		break;	

	case SATCAN_IOC_GET_DOFFSET:
		value = (int*)ioarg->buffer;
		*value = priv->doff;
		break;
	
	case SATCAN_IOC_SET_DOFFSET:
		value = (int*)ioarg->buffer;
		priv->doff = *value;
		break;
		
	case SATCAN_IOC_GET_TIMEOUT:
		timeout = (rtems_interval*)ioarg->buffer;
		*timeout = priv->timeout;
		break;

	case SATCAN_IOC_SET_TIMEOUT:
		timeout = (rtems_interval*)ioarg->buffer;
		priv->timeout = *timeout;
		break;

	default:
		return RTEMS_NOT_DEFINED;
	}

	return RTEMS_SUCCESSFUL;
}

static rtems_device_driver satcan_write(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
	int i;
	int doff;
	int msgindex;
	int messages;
	rtems_libio_rw_args_t *rw_args=(rtems_libio_rw_args_t *) arg;
	satcan_msg *msgs;
	rtems_status_code status;
	
	DBG("SatCAN: Writing %d bytes from %p\n\r",rw_args->count,rw_args->buffer);

	if ((rw_args->count < sizeof(satcan_msg)) || (!rw_args->buffer)) {
		DBG("SatCAN: write: returning EINVAL\n\r");
		return RTEMS_INVALID_NAME; /* EINVAL */
	}

	messages = rw_args->count / sizeof(satcan_msg);
	msgs = (satcan_msg*)rw_args->buffer;

	/* Check that size matches any number of satcan_msg */
	if (rw_args->count % sizeof(satcan_msg)) {
		DBG("SatCAN: write: count can not be evenly divided with satcan_msg size\n\r");
		return RTEMS_INVALID_NAME; /* EINVAL */
	}


	/* DMA channel must be set if we are in system DMA mode */
	DBG("SatCAN: write: dma channel select is %x\n\r", priv->dmaen);
	if (!priv->dmaen && priv->dmamode == SATCAN_DMA_MODE_SYSTEM)
		return RTEMS_INVALID_NAME; /* EINVAL */

	/* DMA must not be active */
	if (regs->satcan[SATCAN_DMA] & (DMA_EnTx1 | DMA_EnTx2 | DMA_AutoInitDmaTx)) {
		DBG("SatCAN: write: DMA was active\n\r");
		rw_args->bytes_moved = 0;
		return RTEMS_IO_ERROR; /* EIO */
	}		

	doff = regs->satcan[SATCAN_CMD1] & Sel_2k_8kN ? DMA_2K_DATA_OFFSET : DMA_8K_DATA_OFFSET;

	for (msgindex = 0; msgindex < messages; msgindex++) {
		/* Place header in DMA area */
		for (i = 0; i < SATCAN_HEADER_SIZE; i++) {
			priv->alptr[priv->doff+8*msgindex+i] = msgs[msgindex].header[i];
		}

		/* Place data in DMA area */
		for (i = 0; i < SATCAN_PAYLOAD_SIZE; i++)
			priv->alptr[priv->doff+doff+8*msgindex+i] = msgs[msgindex].payload[i];
	}

	if ((priv->dmaen & SATCAN_DMA_ENABLE_TX1) ||  priv->dmamode == SATCAN_DMA_MODE_USER) {
		regs->satcan[SATCAN_DMA_TX_1_CUR] = 0;
		regs->satcan[SATCAN_DMA_TX_1_END] = messages<<3; 
	}

	if ((priv->dmaen & SATCAN_DMA_ENABLE_TX2) || priv->dmamode == SATCAN_DMA_MODE_USER) {
		regs->satcan[SATCAN_DMA_TX_2_CUR] = 0;
		regs->satcan[SATCAN_DMA_TX_2_END] = messages<<3; 
	}

	/* If we are in DMA user mode we are done here, otherwise we block */
	if (priv->dmamode == SATCAN_DMA_MODE_SYSTEM) {
		priv->txactive = 1;

		/* Enable DMA */
		regs->satcan[SATCAN_DMA] |= priv->dmaen << 1;
		
		/* Wait for TX interrupt */
		status = rtems_semaphore_obtain(priv->txsem, RTEMS_WAIT, priv->timeout);
		
		priv->txactive = 0;

		/* Disable activated Tx DMA */
		regs->satcan[SATCAN_DMA] &= ~(priv->dmaen << 1);

		if (status != RTEMS_SUCCESSFUL) {
			rw_args->bytes_moved = 0;
			return status;
		}
	}

	rw_args->bytes_moved = rw_args->count;
	
	return RTEMS_SUCCESSFUL;
}

static rtems_device_driver satcan_read(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
	char *buf;
	int i;
	int canid;
	int messages;
	rtems_libio_rw_args_t *rw_args = (rtems_libio_rw_args_t*)arg;
	satcan_msg *ret;

	/* Check that there is room for the return */
	if (rw_args->count < sizeof(satcan_msg)) {
		DBG("SatCAN: read: length of buffer must be at least %d, current is %d\n\r",
		    sizeof(satcan_msg) + sizeof(int), rw_args->count);
		return RTEMS_INVALID_NAME; /* -EINVAL */
	}
	
	/* Check that size matches any number of satcan_msg */
	if (rw_args->count % sizeof(satcan_msg)) {
		DBG("SatCAN: read: count can not be evenly divided with satcan_msg size\n\r");
		return RTEMS_INVALID_NAME; /* EINVAL */
	}
	
	messages = rw_args->count / sizeof(satcan_msg);
	ret = (satcan_msg*)rw_args->buffer;

	DBG("SatCAN: read: reading %d messages to %p\n\r", messages, ret);
	
	for (i = 0; i < messages; i++) {
		canid = (ret[i].header[1] << 8) | ret[i].header[0];
	
		/* Copy message header from DMA header area to buffer */
		buf = (char*)((int)priv->alptr | (canid << 3));
		memcpy(ret[i].header, buf, SATCAN_HEADER_SIZE);

		DBG("SatCAN: read: copied header from %p to %p\n\r", buf, ret[i].header);

		/* Clear New Message Marker */
		buf[SATCAN_HEADER_NMM_POS] = 0;
		
		/* Copy message payload from DMA data area to buffer */
		buf = (char*)((int)buf | 
			      (regs->satcan[SATCAN_CMD1] & Sel_2k_8kN ? DMA_2K_DATA_SELECT : DMA_8K_DATA_SELECT));
		memcpy(ret[i].payload, buf, SATCAN_PAYLOAD_SIZE);
	
		DBG("SatCAN: read: copied payload from %p to %p\n\r", buf, ret[i].payload);
	}
	rw_args->bytes_moved = rw_args->count;

	return RTEMS_SUCCESSFUL;
}


static rtems_device_driver satcan_close(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
	DBG("SatCAN: Closing %d\n\r",minor);

	if (priv->open) {
		regs->irqmask = 0;
		regs->satcan[SATCAN_INT_EN] = 0;
		regs->satcan[SATCAN_RX] = 0;
		regs->satcan[SATCAN_DMA] = 0;
		priv->open = 0;
		priv->dmaen = 0;
		priv->doff = 0;
		priv->timeout = RTEMS_NO_TIMEOUT;
		priv->dmamode = SATCAN_DMA_MODE_SYSTEM;
	}

	return RTEMS_SUCCESSFUL;
}


static rtems_device_driver satcan_open(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
	DBG("SatCAN: Opening %d\n\r",minor);
	
	rtems_semaphore_obtain(priv->devsem,RTEMS_WAIT, RTEMS_NO_TIMEOUT);
	if (priv->open) {
		rtems_semaphore_release(priv->devsem);
		return RTEMS_RESOURCE_IN_USE; /* EBUSY */
	}
	priv->open = 1;
	rtems_semaphore_release(priv->devsem);

	/* Enable AHB and CAN IRQs in wrapper and EOD1, EOD2 and CAN critical  IRQs in SatCAN core */
	regs->irqmask = MSK_AHB | MSK_CAN;
	regs->satcan[SATCAN_INT_EN] = ((1 << SATCAN_IRQ_EOD1) | (1 << SATCAN_IRQ_EOD2) |
				       (1 << SATCAN_IRQ_CRITICAL));

	/* Select can_int as IRQ source */
	regs->satcan[SATCAN_CMD0] = CAN_TODn_Int_sel;
	/* CAN RX DMA Enable */
	regs->satcan[SATCAN_DMA] = 1;
	/* CAN RX Enable */
	regs->satcan[SATCAN_RX] = 1;

	DBG("SatCAN: Opening %d success\n\r",minor);

	return RTEMS_SUCCESSFUL;
}

static rtems_device_driver satcan_initialize(rtems_device_major_number major, rtems_device_minor_number minor, void *arg)
{
	struct ambapp_ahb_info d;
	char fs_name[20];
	rtems_status_code status;

	DBG("SatCAN: Initialize..\n\r");

	strcpy(fs_name, SATCAN_DEVNAME);

	/* Find core and initialize register pointer */
	if (!ambapp_find_ahbslv(ambapp_plb(), VENDOR_GAISLER, GAISLER_SATCAN, &d)) {
		printk("SatCAN: Failed to find SatCAN core\n\r");
		return -1;
	}

	status = rtems_io_register_name(fs_name, major, minor);
	if (RTEMS_SUCCESSFUL != status)
		rtems_fatal_error_occurred(status);

	regs = (struct satcan_regs*)d.start[0];
		
	/* Set node number and DPS */
	regs->ctrl |= ((priv->cfg->nodeno & 0xf) << 5) | (priv->cfg->dps << 1);

	/* Reset core */
	regs->ctrl |= CTRL_RST;

	/* Allocate DMA area */
	almalloc(&priv->alptr, &priv->dmaptr, ALIGN_2KMEM);
	if (priv->dmaptr == NULL) {
		printk("SatCAN: Failed to allocate DMA memory\n\r");
		free(priv->cfg);
		free(priv);
		return -1; 
	}
	
	/* Wait until core reset has completed */
	while (regs->ctrl & CTRL_RST)
		;

	/* Initialize core registers, default is 2K messages */
	regs->membase = (unsigned int)priv->alptr;
	regs->satcan[SATCAN_RAM_BASE] = (unsigned int)priv->alptr >> 15;
	
	DBG("regs->membase = %x\n\r", (unsigned int)priv->alptr);
	DBG("regs->satcan[SATCAN_RAM_BASE] = %x\n\r", (unsigned int)priv->alptr >> 15);

	status = rtems_semaphore_create(
		rtems_build_name('S', 'd', 'v', '0'),
		1,
		RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \
		RTEMS_NO_PRIORITY_CEILING,
		0, 
		&priv->devsem);
	if (status != RTEMS_SUCCESSFUL) {
		printk("SatCAN: Failed to create dev semaphore (%d)\n\r", status);
		free(priv->cfg);
		free(priv);
		return RTEMS_UNSATISFIED;
	}
	status = rtems_semaphore_create(
		rtems_build_name('S', 't', 'x', '0'),
		0, 
		RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY | \
		RTEMS_NO_PRIORITY_CEILING,
		0, 
		&priv->txsem);
	if (status != RTEMS_SUCCESSFUL) {
	  printk("SatCAN: Failed to create tx semaphore (%d)\n\r", status);
	  free(priv->cfg);
	  free(priv);
	  return RTEMS_UNSATISFIED;
	}

	priv->txactive = 0;
	priv->open = 0;
	priv->dmaen = 0;
	priv->doff = 0;
	priv->timeout = RTEMS_NO_TIMEOUT;
	priv->dmamode = SATCAN_DMA_MODE_SYSTEM;

	/* Register interrupt handler */
	rtems_interrupt_handler_install(d.common.irq, "satcan",
			RTEMS_INTERRUPT_SHARED,
			satcan_interrupt_handler, NULL);

	return RTEMS_SUCCESSFUL;
}



#define SATCAN_DRIVER_TABLE_ENTRY { satcan_initialize, satcan_open, satcan_close, satcan_read, satcan_write, satcan_ioctl }

static rtems_driver_address_table satcan_driver = SATCAN_DRIVER_TABLE_ENTRY;

int satcan_register(satcan_config *conf)
{
	rtems_status_code r;
	rtems_device_major_number m;

	DBG("SatCAN: satcan_register called\n\r");

	/* Create private structure */
	if ((priv = grlib_malloc(sizeof(*priv))) == NULL) {
		printk("SatCAN driver could not allocate memory for priv structure\n\r");
		return -1;
	}

	DBG("SatCAN: Creating local copy of config structure\n\r");
	if ((priv->cfg = grlib_malloc(sizeof(*priv->cfg))) == NULL) {
		printk("SatCAN driver could not allocate memory for cfg structure\n\r");
		return 1;
	}
	memcpy(priv->cfg, conf, sizeof(satcan_config));

	if ((r = rtems_io_register_driver(0, &satcan_driver, &m)) == RTEMS_SUCCESSFUL) {
		DBG("SatCAN driver successfully registered, major: %d\n\r", m);
	} else {
		switch(r) {
		case RTEMS_TOO_MANY:
			printk("SatCAN rtems_io_register_driver failed: RTEMS_TOO_MANY\n\r"); break;
		case RTEMS_INVALID_NUMBER:  
			printk("SatCAN rtems_io_register_driver failed: RTEMS_INVALID_NUMBER\n\r"); break;
		case RTEMS_RESOURCE_IN_USE:
			printk("SatCAN rtems_io_register_driver failed: RTEMS_RESOURCE_IN_USE\n\r"); break;
		default:
			printk("SatCAN rtems_io_register_driver failed\n\r");
		}
		return 1;
	}

	return 0;
}