Project

General

Profile

RE: FPGA Driver ยป fpga_main.c

John Pruitt, 10/27/2011 05:04 PM

 

/* R E S O U R C E M A N A G E R - An FPGA driver */

/* Project Name: "fpga" */

/* What is a Resource Manager under QNX Neutrino?
* A resource manager is a superset of a device driver. The QNX
* resource manager framework is used to create the POSIX
* interface (open, read, write, etc.) for any resource you
* can think of. Imagine you are coding a device driver for
* a device that reads credit cards. Your application would then
* be able to just use open() to access the card, read() to retrieve
* data from the card, and write() to store data on the card.
* If later your hardware changes, and you don't need to read
* credit cards any more but are reading serial data from some
* field bus instead, you replace/update the resource manager
* while your application still just uses open(), read() and write()!
*/

/*
* This project is an fpga driver. It provides a means of programming
* the fpga and displaying the status of the fpga.
*
* Start the driver as:
* fpga-variant -b 0x01e26000 -f 0x66000000 &
*
* It will create the following devices:
* /dev/fpga/cmd Writes to this device perform different commands
* 1 Assert FPGA reset
* 2 Initiate programming cycle
* 3 Check if programming worked (enumerate cores?)
* /dev/fpga/image Writes to this device send data to the fpga when being programmed
* /dev/fpga/state Reads from this device give current state of fpga. Possible states
* UNKNOWN fpga might or might not be programmed
* RESET fpga is being reset
* PROGRAM_FAIL an attempt was made to program the fpga and it failed
* PROGRAMMING fpga is currently being programmed
* PROGRAMMED fpga has been successfully programmed
* /dev/fpga/version Reads from this device give version numbers when device is programmed
* /dev/fpga/int_status Reads from this device give interrupt information (not implemented now)
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct fpga_attr; // This overrides the default structure so we can add our own fields
#define IOFUNC_ATTR_T struct fpga_attr
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#include <sys/resmgr.h>
#include <sys/mman.h>
#include <hw/inout.h>
#define FPGA_CTRL_C
#include "core_ids.h"

/*
* Enumeration for different directions of pins
*/
enum gpio_direct { GPIO_IN, GPIO_OUT };
typedef enum gpio_direct gpio_direct_t;

/*
* Our attribute structure. This includes the default structure
* at the front and then specifies our own added fields.
*/
#define PAGE_SIZE 4096
typedef struct fpga_attr fpga_attr_t;
struct fpga_attr
{
iofunc_attr_t attr; /* must be first, this is the regular structure */
// unsigned bank; /* bank of the pin */
// unsigned pin; /* pin number within the bank (0-15) */
// gpio_direct_t direction; /* direction of pin */
// unsigned init_value; /* initial value if direction is GPIO_OUT */
char buffer[PAGE_SIZE]; /* buffer for values being read */
int id; /* resmgr id associated with the file, filled in after attach */
// gpio_attr_t* next; /* pointer to next gpio_attr structure */
// const char * name; /* pointer to name for the device */
};

/*
* defines to specify register offsets within each bank.
*/
#define GPIO_DIR (0x00)
#define GPIO_OUT_DATA (0x04)
#define GPIO_SET_DATA (0x08)
#define GPIO_CLR_DATA (0x0C)
#define GPIO_IN_DATA (0x10)
#define GPIO_SET_RIS_TRIG (0x14)
#define GPIO_CLR_RIS_TRIG (0x18)
#define GPIO_SET_FAL_TRIG (0x1C)
#define GPIO_CLR_FAL_TRIG (0x20)
#define GPIO_INTSTAT (0x24)
#define BANKSIZE (0x28)
#define NUM_BANKS (9)

/*
* Macros to help calculate the bank offset from the base address
* and to generate the pin-mask for each pin.
*
* Note that each bank of registers actually has 2 banks of pins.
* Each bank of pins has 16 pins. The even number banks are in the
* low 16 bits and the odd number banks are in the high 16 bits.
*/
#define BANKOFF(bank) (0x10 + ((bank)>>1) * BANKSIZE)
#define PINMASK(bank,pin) (1 << ((pin) + (((bank)&1)? 16 : 0 ) ) )

static uintptr_t gpio_base = 0x01e26000; /* base address of the gpio peripheral */
static uintptr_t gpio_vbase; /* mmap version of base */

#define FPGA_BASE_ADDR 0x66000000
#define FPGA_CORE_SIZE 0x80
#define FPGA_MAX_CORES 32
#define FPGA_BASEMODULE_OFFSET 0

static uintptr_t fpga_base = FPGA_BASE_ADDR; /* base address of the FPGA */
static uintptr_t fpga_vbase; /* mmap version of fpga base */

#define GPIO_TO_PIN( bank, pinno ) (((bank)<<16)|(pinno))
#define PIN_TO_BANK(gpiopin) (((gpiopin)>>16)&0xff)
#define PIN_TO_PIN(gpiopin) ((gpiopin)&0xff)
#define FPGA_PROGRAM GPIO_TO_PIN(6,15)
#define FPGA_INIT GPIO_TO_PIN(1,15)
#define FPGA_RDWR GPIO_TO_PIN(3,9)
#define FPGA_INT0 GPIO_TO_PIN(6,12)
#define FPGA_INT1 GPIO_TO_PIN(6,13)

#define FPGA_STATE_UNKNOWN 0 /* FPGA is not in a known state */
#define FPGA_STATE_RESET 1 /* FPGA has been reset, but we aren't programming */
#define FPGA_STATE_PROGRAMMING 2 /* FPGA is being programmed */
#define FPGA_STATE_PROGRAM_FAIL 3 /* FPGA failed programming */
#define FPGA_STATE_PROGRAMMED 4 /* FPGA has been programmed */

#define FPGA_CMD_RESET 1 /* Assert FPGA reset */
#define FPGA_CMD_PROGRAM 2 /* Initiate programming cycle */
#define FPGA_CMD_FINISHPROGRAM 3 /* Check if programming worked, then enumerate cores */


/**
* Core Version Register, FIFO_no = 0
*/
typedef union corever0 {
struct bits0 {
unsigned int core_id : 8; /* Core ID 0xF0-0xFF are reserved for customers */
unsigned int vector : 4; /* interrupt vector level */
unsigned int level : 2; /* interrupt level */
unsigned int FIFO_no : 2; /* = 00 */
} bits;
uint16_t word;
} corever0;

/**
* Core Version Register, FIFO_no = 1
*/
typedef union corever1 {
struct bits1 {
unsigned int minor : 4; /* minor revision */
unsigned int major : 4; /* major revision */
unsigned int year : 5; /* years since 2000 */
unsigned int reserved : 1; /* not used */
unsigned int FIFO_no : 2; /* = 01 */
} bits;
uint16_t word;
} corever1;

/**
* Core Version Register, FIFO_no = 2
*/
typedef union corever2 {
struct bits2 {
unsigned int day : 5; /* minor revision */
unsigned int reserved : 3; /* not used */
unsigned int month : 4; /* major revision */
unsigned int reserved1 : 2; /* not used */
unsigned int FIFO_no : 2; /* = 10 */
} bits;
uint16_t word;
} corever2;
/**
* Core Version Register, FIFO_no = 3
*/
typedef union corever3 {
struct bits3 {
unsigned int reserved1 : 14; /* not used */
unsigned int FIFO_no : 2; /* = 10 */
} bits;
uint16_t word;
} corever3;

/**
* This structure holds the FPGA core version information.
*/
struct coreversion {
corever0 ver0;
corever1 ver1;
corever2 ver2;
corever3 ver3;
};

struct fpga_ctrl
{
unsigned int state;
uintptr_t vbaseaddr;
uintptr_t baseaddr;
struct coreversion bm_version;
struct coreversion app_version;
} fpga_ctrl = {
.state = FPGA_STATE_UNKNOWN
};
static struct fpga_ctrl *fpgactrl = &fpga_ctrl;


/*
* options processing
*
* This routine handles the command-line options.
* -v verbose operation
* -b addr gpio base address (default is 0x01e26000)
* -f addr fpga base address (default is 0x66000000)
*/
static int optv; // -v for verbose operation
#if NEVER
enum gpio_pinoptions { IN, OUT, END };
static char * gpio_pinopts[] =
{
[IN] = "in",
[OUT] = "out",
[END] = NULL
};
#endif
static void
options (int argc, char **argv)
{
int opt;
optv = 0;

while (optind < argc) {
while ((opt = getopt (argc, argv, "vb:f:")) != -1) {
switch (opt) {
case 'v':
optv = 1;
break;
case 'b':
gpio_base = strtoul( optarg, 0, 0 );
break;
case 'f':
fpga_base = strtoul( optarg, 0, 0 );
break;
}
}
}
}

/* A resource manager mainly consists of callbacks for POSIX
* functions a client could call. In the example, we have
* callbacks for the open(), read() and write() calls. More are
* possible. If we don't supply own functions (e.g. for stat(),
* seek(), etc.), the resource manager framework will use default
* system functions, which in most cases return with an error
* code to indicate that this resource manager doesn't support
* this function.*/

/* These prototypes are needed since we are using their names
* in main(). */

//static int io_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);
static int io_cmd_write (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb);
static int io_image_write (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb);
//static int io_version_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);
static int io_state_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);
static int io_devices_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);

/*
* Attribute structures for each of the files to be created
*/
static IOFUNC_ATTR_T attr_cmd;
static IOFUNC_ATTR_T attr_image;
static IOFUNC_ATTR_T attr_version;
static IOFUNC_ATTR_T attr_state;
static IOFUNC_ATTR_T attr_devices;

/*
* Our connect and I/O functions - we supply two tables
* which will be filled with pointers to callback functions
* for each POSIX function. The connect functions are all
* functions that take a path, e.g. open(), while the I/O
* functions are those functions that are used with a file
* descriptor (fd), e.g. read().
*/

static resmgr_connect_funcs_t connect_funcs;
static resmgr_io_funcs_t io_cmd_funcs;
static resmgr_io_funcs_t io_image_funcs;
static resmgr_io_funcs_t io_version_funcs;
static resmgr_io_funcs_t io_state_funcs;
static resmgr_io_funcs_t io_devices_funcs;

/*
* Our dispatch, resource manager, and iofunc variables
* are declared here. These are some small administrative things
* for our resource manager.
*/

static dispatch_t *dpp;
static resmgr_attr_t rattr;
static dispatch_context_t *ctp;


static char *progname = "fpga";

void attachFile( IOFUNC_ATTR_T *pAttr, const char *name, mode_t mode, resmgr_io_funcs_t *pio_funcs )
{
iofunc_attr_init (&pAttr->attr, 0660, NULL, NULL);
pAttr->attr.nbytes=sizeof(pAttr->buffer); /* we have a buffer size of 128 byte */

pAttr -> id = resmgr_attach (dpp, &rattr, name,
_FTYPE_ANY, 0,
&connect_funcs,
pio_funcs,
pAttr);
if (pAttr -> id == -1) {
fprintf (stderr, "%s: couldn't attach pathname(%s): %s\n",
progname, name, strerror (errno));
exit (1);
}
}
void gpio_init( unsigned gpiopin, gpio_direct_t direction, unsigned init_value )
{
unsigned bankoff = BANKOFF(PIN_TO_BANK(gpiopin));
unsigned reg_direction = in32( gpio_vbase+bankoff+GPIO_DIR );
unsigned pinmask = PINMASK( PIN_TO_BANK(gpiopin), PIN_TO_PIN(gpiopin) );
if( direction == GPIO_OUT )
{
/*
* Turn off pin position for output
*/
reg_direction &= ~(pinmask) ;
}
else
{
/*
* Turn on pin position for input
*/
reg_direction |= pinmask;
}
/*
* No interrupts for rising or falling
*/
out32( gpio_vbase + bankoff + GPIO_CLR_RIS_TRIG, pinmask );
out32( gpio_vbase + bankoff + GPIO_CLR_FAL_TRIG, pinmask );
/*
* Write the new direction register with this pin set properly
* Leave the other pin positions the same
*/
out32( gpio_vbase + bankoff + GPIO_DIR, reg_direction );

if( direction == GPIO_OUT )
{
/*
* Set the initial value. For the initial value,
* binary zero says to clear the value, non-zero
* says to set the value.
*/
if( init_value )
{
out32( gpio_vbase + bankoff + GPIO_SET_DATA, pinmask );
}
else
{
out32( gpio_vbase + bankoff + GPIO_CLR_DATA, pinmask );
}
}
}
void gpio_direction_output( unsigned int pin, unsigned value )
{
unsigned bankoff = BANKOFF( PIN_TO_BANK(pin) );
unsigned pinmask = PINMASK( PIN_TO_BANK(pin), PIN_TO_PIN(pin) );
out32( gpio_vbase + bankoff + (value == 1 ? GPIO_SET_DATA : GPIO_CLR_DATA), pinmask );
}
void setFPGAState( unsigned state )
{
char *state_name;
fpgactrl->state = state;

switch( fpgactrl->state )
{
default:
case FPGA_STATE_UNKNOWN: state_name = "UNKNOWN"; break;
case FPGA_STATE_RESET: state_name = "RESET"; break;
case FPGA_STATE_PROGRAMMING: state_name = "PROGRAMMING"; break;
case FPGA_STATE_PROGRAM_FAIL: state_name = "PROGRAM_FAIL"; break;
case FPGA_STATE_PROGRAMMED: state_name = "PROGRAMMED"; break;
}
attr_state.attr.nbytes = snprintf( attr_state.buffer, PAGE_SIZE, state_name );
}
void setVersion()
{
int rv = 0;
char *buf = &attr_version.buffer[0];
switch( fpgactrl->state )
{
default:
case FPGA_STATE_UNKNOWN:
case FPGA_STATE_RESET:
case FPGA_STATE_PROGRAMMING:
case FPGA_STATE_PROGRAM_FAIL:
rv += snprintf( &buf[rv], PAGE_SIZE-rv, "NOT_PROGRAMMED" );
break;
case FPGA_STATE_PROGRAMMED:
rv += snprintf(&buf[rv], PAGE_SIZE-rv, "PROGRAMMED\n");
rv += snprintf(&buf[rv], PAGE_SIZE-rv, "FPGA Version : %02d.%02d\n",
fpgactrl->app_version.ver1.bits.major, fpgactrl->app_version.ver1.bits.minor);
rv += snprintf(&buf[rv], PAGE_SIZE-rv, "FPGA Date : %04d-%02d-%02d\n",
fpgactrl->app_version.ver1.bits.year,
fpgactrl->app_version.ver2.bits.month,
fpgactrl->app_version.ver2.bits.day);

rv += snprintf(&buf[rv], PAGE_SIZE-rv, "Base Module Version : %02d.%02d\n",
fpgactrl->bm_version.ver1.bits.major,
fpgactrl->bm_version.ver1.bits.minor);
rv += snprintf(&buf[rv], PAGE_SIZE-rv, "Base Module Date : %04d-%02d-%02d\n",
fpgactrl->bm_version.ver1.bits.year,
fpgactrl->bm_version.ver2.bits.month,
fpgactrl->bm_version.ver2.bits.day);
break;
}
attr_version.attr.nbytes = rv;
}
/**
* Reads the core version information out of a spot in the
* FPGA.
*
* \param[in] baseaddr location of the core version register
* \param[in] pdata location to store the core version data
*
* \return non-zero if the core data is invalid
*/
int read_core_version(uintptr_t baseaddr, struct coreversion* pdata)
{
int i;
corever0 ver;
int found = 0;
int rv = -1;

for (i = 0; i < 4; i++)
{
ver.word = in16(baseaddr);
switch(ver.bits.FIFO_no)
{
case 0:
pdata->ver0.word = ver.word;
break;
case 1:
pdata->ver1.word = ver.word;
break;
case 2:
pdata->ver2.word = ver.word;
break;
case 3:
pdata->ver3.word = ver.word;
break;
}
found |= (1<<ver.bits.FIFO_no);
}
if (found == 0x0F)
rv = 0;
return rv;
}
int main (int argc, char **argv)
{
progname = argv[0];
/* Check for command line options (-v and pin specifications) */
options (argc, argv);

/* Allocate and initialize a dispatch structure for use
* by our main loop. This is for the resource manager
* framework to use. It will receive messages for us,
* analyze the message type integer and call the matching
* handler callback function (i.e. io_open, io_read, etc.) */
dpp = dispatch_create ();
if (dpp == NULL) {
fprintf (stderr, "%s: couldn't dispatch_create: %s\n",
argv[0], strerror (errno));
exit (1);
}

/* Set up the resource manager attributes structure. We'll
* use this as a way of passing information to
* resmgr_attach(). The attributes are used to specify
* the maximum message length to be received at once,
* and the number of message fragments (iov's) that
* are possible for the reply.
* For now, we'll just use defaults by setting the
* attribute structure to zeroes. */
memset (&rattr, 0, sizeof (rattr));

/* Now, let's initialize the tables of connect functions and
* I/O functions to their defaults (system fallback
* routines) and then override the defaults with the
* functions that we are providing. */
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_cmd_funcs);
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_image_funcs);
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_state_funcs);
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_version_funcs);
iofunc_func_init (_RESMGR_CONNECT_NFUNCS, &connect_funcs,
_RESMGR_IO_NFUNCS, &io_devices_funcs);

/* Now we override the default function pointers with
* some of our own coded functions: */
//connect_funcs.open = io_open;
//io_funcs.read = io_read;
//io_funcs.write = io_write;
/*
* Map the GPIO registers so we can access them.
*/
gpio_vbase = mmap_device_io( BANKSIZE*(NUM_BANKS+1)/2+0x10, gpio_base );
if( gpio_vbase == (uintptr_t)MAP_FAILED )
{
fprintf( stderr, "mmap_device_io gpio failed: errno = %d\n", errno );
exit(1);
}

/*
* Map the FPGA memory so we can access them.
*/
fpga_vbase = mmap_device_io( FPGA_CORE_SIZE, fpga_base );
if( fpga_vbase == (uintptr_t)MAP_FAILED )
{
fprintf( stderr, "mmap_device_io fpga failed: errno = %d\n", errno );
exit(1);
}
fpgactrl->vbaseaddr = fpga_vbase;
fpgactrl->baseaddr = fpga_base;

/*
* Create all the different resource files. (cmd, image, state, and version)
*/
io_cmd_funcs.write = io_cmd_write;
attachFile( &attr_cmd, "/dev/fpga/cmd", S_IFCHR | S_IWUSR | S_IWGRP | S_IWOTH, &io_cmd_funcs );
io_image_funcs.write = io_image_write;
attachFile( &attr_image, "/dev/fpga/image", S_IFCHR | S_IWUSR | S_IWGRP | S_IWOTH, &io_image_funcs );
io_state_funcs.read = io_state_read;
attachFile( &attr_state, "/dev/fpga/state", S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, &io_state_funcs );
io_version_funcs.read = io_state_read; // version and state share a read function
attachFile( &attr_version, "/dev/fpga/version", S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, &io_version_funcs );
io_devices_funcs.read = io_devices_read;
attachFile( &attr_devices, "/dev/fpga/devices", S_IFCHR | S_IRUSR | S_IRGRP | S_IROTH, &io_devices_funcs );

gpio_init( FPGA_PROGRAM, GPIO_OUT, 1 );
gpio_init( FPGA_INIT, GPIO_IN, 0 );
gpio_init( FPGA_RDWR, GPIO_OUT, 1 );
gpio_init( FPGA_INT0, GPIO_IN, 0 );
gpio_init( FPGA_INT1, GPIO_IN, 0 );
setFPGAState( FPGA_STATE_UNKNOWN );
setVersion();

#if NEVER
if( gpio_attr_head )
{
gpio_attr_t *pAttr;
for( pAttr = gpio_attr_head; pAttr; pAttr = pAttr -> next )
{
unsigned bankoff = BANKOFF(pAttr -> bank);
unsigned reg_direction = in32( gpio_vbase+bankoff+GPIO_DIR );
unsigned pinmask = PINMASK( pAttr -> bank, pAttr -> pin );
char devname[PATH_MAX+1];
if( pAttr -> direction == GPIO_OUT )
{
/*
* Turn off pin position for output
*/
reg_direction &= ~(pinmask) ;
}
else
{
/*
* Turn on pin position for input
*/
reg_direction |= pinmask;
}
/*
* No interrupts for rising or falling
*/
out32( gpio_vbase + bankoff + GPIO_CLR_RIS_TRIG, pinmask );
out32( gpio_vbase + bankoff + GPIO_CLR_FAL_TRIG, pinmask );
/*
* Write the new direction register with this pin set properly
* Leave the other pin positions the same
*/
out32( gpio_vbase + bankoff + GPIO_DIR, reg_direction );
if( pAttr -> direction == GPIO_OUT )
{
/*
* Set the initial value. For the initial value,
* binary zero says to clear the value, non-zero
* says to set the value.
*/
if( pAttr ->init_value )
{
out32( gpio_vbase + bankoff + GPIO_SET_DATA, pinmask );
}
else
{
out32( gpio_vbase + bankoff + GPIO_CLR_DATA, pinmask );
}
}

iofunc_attr_init (&pAttr->attr, S_IFCHR | 0666, NULL, NULL);
pAttr->attr.nbytes=1; /* we have a buffer size of 1 byte */
/*
* Create the file name with 3 possibilities:
* No name provided: use /dev/gpBpP
* Name provided that starts with a /, use the provided name
* Name provided that is relative, use /dev/name
*/
if( pAttr->name == NULL )
{
snprintf(devname, PATH_MAX, "/dev/gp%dp%d", pAttr->bank, pAttr->pin);
}
else if( pAttr->name[0] == '/' )
{
snprintf(devname, PATH_MAX, "%s", pAttr->name );
}
else
{
snprintf(devname, PATH_MAX, "/dev/%s", pAttr->name );
}
pAttr -> id = resmgr_attach (dpp, &rattr, devname,
_FTYPE_ANY, 0,
&connect_funcs,
&io_funcs,
pAttr);
if (pAttr -> id == -1) {
fprintf (stderr, "%s: couldn't attach pathname: %s\n",
argv[0], strerror (errno));
exit (1);
}
}
}
else
{
fprintf( stderr, "%s: no pins specified\n", argv[0] );
exit(1);
}
#endif

/* Now we allocate some memory for the dispatch context
* structure, which will later be used when we receive
* messages. */
ctp = dispatch_context_alloc (dpp);

/* Done! We can now go into our "receive loop" and wait
* for messages. The dispatch_block() function is calling
* MsgReceive() under the covers, and receives for us.
* The dispatch_handler() function analyzes the message
* for us and calls the appropriate callback function. */
while (1) {
if ((ctp = dispatch_block (ctp)) == NULL) {
fprintf (stderr, "%s: dispatch_block failed: %s\n",
argv[0], strerror (errno));
exit (1);
}
/* Call the correct callback function for the message
* received. This is a single-threaded resource manager,
* so the next request will be handled only when this
* call returns. Consult QNX documentation if you want
* to create a multi-threaded resource manager. */
dispatch_handler (ctp);
}
}

/*
* io_read
*
* At this point, the client has called the library read()
* function, and expects zero or more bytes. If this is a read
* for exactly 1 byte, we will always return the current
* value for the pin. If this is a read for multiple bytes,
* then we use our "buffer" and return one byte and then
* on the next request for multiple bytes, we will return
* EOF. This allows repeated reads for polling and convenient
* access from the command line.
*/


/* The message that we received can be accessed via the
* pointer *msg. A pointer to the OCB that belongs to this
* read is the *ocb. The *ctp pointer points to a context
* structure that is used by the resource manager framework
* to determine whom to reply to, and more.
*/

static int
io_state_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb)
{
int status;
int nparts;
int nbytes = 0;
int offset = ocb->offset;

if (optv) {
printf ("%s: in io_read, id=%d\n", progname, ctp->id);
}

/* Here we verify if the client has the access
* rights needed to read from our device */
if ((status = iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK) {
return (status);
}

/* We check if our read callback was called because of
* a pread() or a normal read() call. If pread(), we return
* with an error code indicating that we don't support it.*/
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return (ENOSYS);
}
/*
* Get the number of bytes to read. If requested is >1, look
* at the current offset to see if we have a byte to return.
* If the requested number is exactly 1, then read 1 byte.
*/
if( msg -> i.nbytes > 0 )
{
int nleft;
nleft = ocb -> attr -> attr.nbytes - ocb -> offset;
nbytes = min( msg -> i.nbytes, nleft );
if( nleft )
ocb -> offset += nbytes;
}

if( nbytes > 0 )
{
/* Here we set the number of bytes we will return. */
_IO_SET_READ_NBYTES(ctp, nbytes);

/* The next line is used to tell the system how
* large your buffer is in which you want to return your
* data for the read() call.
*
*/
SETIOV( ctp->iov, &ocb -> attr -> buffer[offset], nbytes);
nparts = 1;

}
else
{
_IO_SET_READ_NBYTES(ctp, 0);
nparts = 0;
}

if (msg->i.nbytes > 0) {
ocb->attr->attr.flags |= IOFUNC_ATTR_ATIME;
}

/*
* Return the number of parts specified above (1 or 0).
*/
return (_RESMGR_NPARTS (nparts));


}
/**
* Retrieve human readable core description.
*
* \param[in] ID core number
* \return string description of core
*/
const char* CoreName(unsigned char ID)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(KnownCores); i++)
{
if (ID == KnownCores[i].ID)
{
return KnownCores[i].Name;
}
}
return "Unknown";
}
static int
io_devices_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb)
{
int status;
int nparts;
int nbytes = 0;
int offset = ocb->offset;
int rv = 0;
char *buf = &ocb->attr->buffer[0];

if( offset == 0 )
{
rv += snprintf( &buf[rv], PAGE_SIZE-rv, "Enumerating Devices\n" );
int i;
struct coreversion cv;

for (i = 0; i < FPGA_MAX_CORES; i++)
{
uintptr_t vbaseaddr = (uintptr_t)((char*)(fpgactrl->vbaseaddr)+FPGA_CORE_SIZE*i);
uintptr_t baseaddr = (uintptr_t)((char*)(fpgactrl->baseaddr )+FPGA_CORE_SIZE*i);
if (0 == read_core_version(vbaseaddr,&cv))
{
//struct fpga_device* fpgadev;
//int ret;

rv += snprintf( &buf[rv], PAGE_SIZE-rv, "Found Device ID %02d-%s (%02d.%02d) at %08X\n",
cv.ver0.bits.core_id,
CoreName(cv.ver0.bits.core_id),
cv.ver1.bits.major, cv.ver1.bits.minor,baseaddr);
}
}
ocb->attr->attr.nbytes = rv;
}
if (optv) {
printf ("%s: in io_read, id=%d\n", progname, ctp->id);
}

/* Here we verify if the client has the access
* rights needed to read from our device */
if ((status = iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK) {
return (status);
}

/* We check if our read callback was called because of
* a pread() or a normal read() call. If pread(), we return
* with an error code indicating that we don't support it.*/
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return (ENOSYS);
}
/*
* Get the number of bytes to read. If requested is >1, look
* at the current offset to see if we have a byte to return.
* If the requested number is exactly 1, then read 1 byte.
*/
if( msg -> i.nbytes > 0 )
{
int nleft;
nleft = ocb -> attr -> attr.nbytes - ocb -> offset;
nbytes = min( msg -> i.nbytes, nleft );
if( nleft )
ocb -> offset += nbytes;
}

if( nbytes > 0 )
{
/* Here we set the number of bytes we will return. */
_IO_SET_READ_NBYTES(ctp, nbytes);

/* The next line is used to tell the system how
* large your buffer is in which you want to return your
* data for the read() call.
*
*/
SETIOV( ctp->iov, &ocb -> attr -> buffer[offset], nbytes);
nparts = 1;

}
else
{
_IO_SET_READ_NBYTES(ctp, 0);
nparts = 0;
}

if (msg->i.nbytes > 0) {
ocb->attr->attr.flags |= IOFUNC_ATTR_ATIME;
}

/*
* Return the number of parts specified above (1 or 0).
*/
return (_RESMGR_NPARTS (nparts));


}
#if NEVER
static int
io_version_read (resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb)
{
int status;
int nparts;
int nbytes = 0;
int offset = ocb->offset;

if (optv) {
printf ("%s: in io_read, id=%d\n", progname, ctp->id);
}

/* Here we verify if the client has the access
* rights needed to read from our device */
if ((status = iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK) {
return (status);
}

/* We check if our read callback was called because of
* a pread() or a normal read() call. If pread(), we return
* with an error code indicating that we don't support it.*/
if (msg->i.xtype & _IO_XTYPE_MASK != _IO_XTYPE_NONE) {
return (ENOSYS);
}
/*
* Get the number of bytes to read. If requested is >1, look
* at the current offset to see if we have a byte to return.
* If the requested number is exactly 1, then read 1 byte.
*/
if( msg -> i.nbytes > 0)
{
int nleft;
nleft = ocb -> attr -> attr.nbytes - ocb -> offset;
nbytes = min( msg -> i.nbytes, nleft );
if( nleft )
ocb -> offset += nbytes;
}
if( nbytes )
{
/* Here we set the number of bytes we will return. */
_IO_SET_READ_NBYTES(ctp, nbytes);
/* The next line is used to tell the system how
* large your buffer is in which you want to return your
* data for the read() call.
*
* We get the pin value by reading the IN_DATA register, masking
* with the pinmask and if the value is 0, returning an ascii '0'.
* If the value is not 0, then we return an ascii '1'.
*/
SETIOV( ctp->iov, &ocb -> attr -> buffer[ocb->offset], nbytes);
nparts = 1;

}
else
{
_IO_SET_READ_NBYTES(ctp, 0);
nparts = 0;
}

if (msg->i.nbytes > 0) {
ocb->attr->attr.flags |= IOFUNC_ATTR_ATIME;
}

/*
* Return the number of parts specified above (1 or 0).
*/
return (_RESMGR_NPARTS (nparts));


}
#endif

/*
* io_write
*
* At this point, the client has called the library write()
* function, and expects that our resource manager will write
* the number of bytes that have been specified to the device.
*
* Since this is /dev/Null, all of the clients' writes always
* work -- they just go into Deep Outer Space.
*/

static int
io_cmd_write (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb)
{
int status;
char *buf;
char free_buf = 0;
//char value;

if (optv) {
printf ("%s: in io_write, id=%d\n", progname, ctp->id);
}

/* Check the access permissions of the client */
if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK) {
return (status);
}

/* Check if pwrite() or normal write() */
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return (ENOSYS);
}

/* Set the number of bytes successfully written for
* the client. This information will be passed to the
* client by the resource manager framework upon reply.
* In this example, we just take the number of bytes that
* were sent to us and we always write them. */
_IO_SET_WRITE_NBYTES (ctp, msg -> i.nbytes);
if( optv ) printf("got write of %d bytes, data:\n", msg->i.nbytes);

/* First check if our message buffer was large enough
* to receive the whole write at once. If yes, print data.*/
if( (msg->i.nbytes <= ctp->info.msglen - ctp->offset - sizeof(msg->i)) &&
(ctp->info.msglen < ctp->msg_max_size)) { // space for NUL byte
buf = (char *)(msg+1);

} else {
/* If we did not receive the whole message because the
* client wanted to send more than we could receive, we
* allocate memory for all the data and use resmgr_msgread()
* to read all the data at once. Although we did not receive
* the data completely first, because our buffer was not big
* enough, the data is still fully available on the client
* side, because its write() call blocks until we return
* from this callback! */
buf = malloc( msg->i.nbytes + 1);
free_buf = 1;
if( buf )
{
resmgr_msgread( ctp, buf, msg->i.nbytes, sizeof(msg->i));
}
}
/*
* Write all the values to the pin.
* The bytes passed in can be 3 types of values:
* '1' causes the pin to be set
* '0' causes the pin to be cleared
* other values have no effect
*/
if( buf )
{

int rv;
int tmp;
unsigned int cmd;
rv = sscanf( buf, "%d", &cmd );
switch( cmd )
{
case FPGA_CMD_RESET:
gpio_direction_output( FPGA_PROGRAM, 0 );
setFPGAState( FPGA_STATE_RESET );
break;
case FPGA_CMD_PROGRAM:
gpio_direction_output( FPGA_PROGRAM, 1 );
gpio_direction_output( FPGA_RDWR, 0 );
setFPGAState( FPGA_STATE_PROGRAMMING );
break;
case FPGA_CMD_FINISHPROGRAM:
gpio_direction_output( FPGA_RDWR, 1 );
tmp = read_core_version( fpgactrl->vbaseaddr, &fpgactrl->app_version );
if( !tmp )
{
tmp = read_core_version( fpgactrl->vbaseaddr, &fpgactrl->bm_version );
setFPGAState( FPGA_STATE_PROGRAMMED );
setVersion();
}
else
{
setFPGAState( FPGA_STATE_PROGRAM_FAIL );
}
break;
}


if( free_buf )
free( buf );
}


/* Finally, if we received more than 0 bytes, we mark the
* file information for the device to be updated:
* modification time and change of file status time. To
* avoid constant update of the real file status information
* (which would involve overhead getting the current time), we
* just set these flags. The actual update is done upon
* closing, which is valid according to POSIX. */
if (msg->i.nbytes > 0) {
ocb->attr->attr.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;
}

return (_RESMGR_NPARTS (0));
}
/*
* io_write
*
* At this point, the client has called the library write()
* function, and expects that our resource manager will write
* the number of bytes that have been specified to the device.
*
* Since this is /dev/Null, all of the clients' writes always
* work -- they just go into Deep Outer Space.
*/

static int
io_image_write (resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb)
{
int status;
char *buf;
char free_buf = 0;
//char value;

if (optv) {
printf ("%s: in io_write, id=%d\n", progname, ctp->id);
}

/* Check the access permissions of the client */
if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK) {
return (status);
}

/* Check if pwrite() or normal write() */
if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE) {
return (ENOSYS);
}

/* Set the number of bytes successfully written for
* the client. This information will be passed to the
* client by the resource manager framework upon reply.
* In this example, we just take the number of bytes that
* were sent to us and we always write them. */
_IO_SET_WRITE_NBYTES (ctp, msg -> i.nbytes);

if( optv ) printf("got write of %d bytes, data:\n", msg->i.nbytes);

/* First check if our message buffer was large enough
* to receive the whole write at once. If yes, print data.*/
if( (msg->i.nbytes <= ctp->info.msglen - ctp->offset - sizeof(msg->i)) &&
(ctp->info.msglen < ctp->msg_max_size)) { // space for NUL byte
buf = (char *)(msg+1);

} else {
/* If we did not receive the whole message because the
* client wanted to send more than we could receive, we
* allocate memory for all the data and use resmgr_msgread()
* to read all the data at once. Although we did not receive
* the data completely first, because our buffer was not big
* enough, the data is still fully available on the client
* side, because its write() call blocks until we return
* from this callback! */
buf = malloc( msg->i.nbytes + 1);
free_buf = 1;
resmgr_msgread( ctp, buf, msg->i.nbytes, sizeof(msg->i));
}
/*
* Write all the values to the pin.
* The bytes passed in can be 3 types of values:
* '1' causes the pin to be set
* '0' causes the pin to be cleared
* other values have no effect
*/
if( buf )
{
int i;
char *p = buf;
for( i = 0; i < msg -> i.nbytes; i++, p++ )
{
out8( fpgactrl->vbaseaddr, *p );
}
if( free_buf )
free( buf );
}


/* Finally, if we received more than 0 bytes, we mark the
* file information for the device to be updated:
* modification time and change of file status time. To
* avoid constant update of the real file status information
* (which would involve overhead getting the current time), we
* just set these flags. The actual update is done upon
* closing, which is valid according to POSIX. */
if (msg->i.nbytes > 0) {
ocb->attr->attr.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;
}

return (_RESMGR_NPARTS (0));
}
/* Why we don't have any close callback? Because the default
* function, iofunc_close_ocb_default(), does all we need in this
* case: Free the ocb, update the time stamps etc. See the docs
* for more info.
*/


    (1-1/1)