Commit 17614052 authored by Michel Arruat's avatar Michel Arruat Committed by Adam Wujek

liblinux:ldevmap device mapping library implementation

        The main objective is to provide a unique API to map a device
        resource whatever the bus.
        Of course for VME devices the implementation is for the time being,
        highly dependant of the PCI/VME_BRIDGE implementation. There is an on
        going initiative at CERN to expose VME device like PCI device resource
        file.
        Currently CERN_VME_BRIDGE is supported, but only activated through
        a condiational compile statement (see Makefile).
Signed-off-by: Michel Arruat's avatarMichel Arruat <michel.arruat@cern.ch>
parent e78434c7
......@@ -2,18 +2,36 @@
# specific Makefile code that you want to run before this. For example,
# build a particular environment.
-include Makefile.specific
SUPPORT_CERN_VMEBRIDGE ?= n
LIB = libwhiterabbit.a
LOBJ := vuart.o
LIB = libdevmap.a
LOBJ := devmap.o
CFLAGS = -Wall -ggdb -O2 -fPIC -Werror -I./ -I../include
LDFLAGS = -L. -lwhiterabbit
CFLAGS = -Wall -ggdb -fPIC -Werror -I./ -I../include
LDFLAGS = -L.
ifeq ($(SUPPORT_CERN_VMEBRIDGE), y)
LIBVME_A = $(VMEBRIDGE)/lib
LIBVME_H = $(VMEBRIDGE)/include
CFLAGS += -I$(LIBVME_H) -I$(LIBVME_A)
CFLAGS += -DSUPPORT_CERN_VMEBRIDGE
LDFLAGS += -L$(LIBVME_A) -lvmebus
LIB_DEP += $(LIBVME_A)/libvmebus.a
endif
%: %.c $(LIB)
$(CC) $(CFLAGS) $*.c $(LDFLAGS) -o $@
$(LIB): $(LOBJ)
$(LIB): $(LOBJ) $(LIB_DEP)
ifeq ($(SUPPORT_CERN_VMEBRIDGE), y)
$(AR) r tmp$@ $(LOBJ)
# transform a thin library into a normal one
echo -e "create $@\naddlib tmp$@\naddlib $(LIBVME_A)/libvmebus.a\nsave\nend" | $(AR) -M
$(RM) tmp$@
else
$(AR) r $@ $^
endif
clean:
rm -f $(LIB) .depend *.o *~
......
......@@ -3,137 +3,312 @@
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <hw/wb_uart.h>
#include <inttypes.h>
#include "libwhiterabbit.h"
#include "libdevmap.h"
#ifdef SUPPORT_CERN_VMEBRIDGE
#include <libvmebus.h>
#endif
#ifdef SUPPORT_CERN_VMEBRIDGE
static struct mapping_desc *cern_vmebridge_dev_map(
struct mapping_args *map_args, int mapping_length)
{
struct mapping_desc *desc;
struct vme_mapping *vme_mapping;
volatile void *ptr_map;
vme_mapping = malloc(sizeof(struct vme_mapping));
if (!vme_mapping)
return NULL;
/* Prepare mmap description */
memset(vme_mapping, 0, sizeof(struct vme_mapping));
vme_mapping->am = map_args->am;
vme_mapping->data_width = map_args->data_width;
vme_mapping->sizel = map_args->offset + mapping_length;
vme_mapping->vme_addrl = map_args->addr;
/* Do mmap */
ptr_map = vme_map(vme_mapping, 1);
if (!ptr_map)
goto out_alloc;
desc = malloc(sizeof(struct mapping_desc));
if (!desc)
goto out_map;
desc->base = ptr_map + map_args->offset;
desc->mmap = (void *)vme_mapping;
return desc;
out_map:
vme_unmap(vme_mapping, 1);
out_alloc:
free(vme_mapping);
return NULL;
}
#endif
#define MAX_SIZE 0x100000
/**
* It opens a White Rabbit Virtual UART
* It maps into memory the given device resource
* @parma[in] resource_path path to the resource file which can be mmapped
* @param[in] offset virtual address where the UART is available within
* the resource
* @param[in] offset virtual address of the device memory region of interest
* within the resource
*
* @todo make the open more user firendly
* @todo make the open more user friendly
*
* @return NULL on error and errno is approproately set.
* On success a vuart token.
* On success a mapping descriptor.
*/
struct wr_vuart *wr_vuart_open(char *resource_path, unsigned int offset)
struct mapping_desc *dev_map(struct mapping_args *map_args, uint32_t map_length)
{
struct wr_vuart *vuart;
struct mapping_desc *desc;
off_t pa_offset /*page aligned offset */;
vuart = malloc(sizeof(struct wr_vuart));
if (!vuart)
#ifdef SUPPORT_CERN_VMEBRIDGE
if (!map_args->resource_file) { //map device through CERN VME_BRIDGE
desc = cern_vmebridge_dev_map(map_args, map_length);
if (!desc) {
return NULL;
}
desc->is_be = 1; /* VME Bus is big endian */
desc->args = map_args;
return desc;
}
#endif
desc = malloc(sizeof(struct mapping_desc));
if (!desc)
goto out_alloc;
vuart->fd = open(resource_path, O_RDWR | O_SYNC);
if (vuart->fd <=0)
desc->fd = open(map_args->resource_file, O_RDWR | O_SYNC);
if (desc->fd <= 0)
goto out_open;
vuart->mmap = mmap(NULL, MAX_SIZE & ~(getpagesize() - 1),
/* offset is page aligned */
pa_offset = map_args->offset & ~(getpagesize() - 1);
desc->map_pa_length = map_length + map_args->offset - pa_offset;
desc->mmap = mmap(NULL, desc->map_pa_length,
PROT_READ | PROT_WRITE,
MAP_SHARED, vuart->fd, 0);
if ((long)vuart->mmap == -1)
MAP_SHARED, desc->fd, pa_offset);
if ((long)desc->mmap == -1)
goto out_mmap;
vuart->base = vuart->mmap + offset;
return vuart;
desc->base = desc->mmap + map_args->offset - pa_offset;
/*
* @todo for future VME devices handled via a resource file,
* exposed in standard place (/sys/bus/vme/device/xxx/resource-file)
* the bus type (pci or vme) should be checked to set properly
* the endianess (could be get from the path's resource)
*/
desc->is_be = 0; /* default set to little endian */
return desc;
out_mmap:
close(vuart->fd);
close(desc->fd);
out_open:
free(vuart);
free(desc);
out_alloc:
return NULL;
}
/**
* It releases the resources allocated by wr_vuart_open(). The vuart will
* not be available anymore and the token will be invalidated
* @param[in,out] vuart token from wr_vuart_open()
* It releases the resources allocated by dev_map(). The mapping will
* not be available anymore and the descriptor will be invalidated
* @param[in,out] descriptor token from dev_map()
*/
void wr_vuart_close(struct wr_vuart *vuart)
void dev_unmap(struct mapping_desc *desc)
{
close(vuart->fd);
free(vuart);
#ifdef SUPPORT_CERN_VMEBRIDGE
if (!desc->fd) { /* cern vmebridge resource */
vme_unmap((struct vme_mapping *)desc->mmap, 1);
free(desc->args);
free(desc);
return;
}
#endif
munmap(desc->mmap, desc->map_pa_length);
close(desc->fd);
free(desc->args);
free(desc);
}
/**
* It reads a number of bytes and it stores them in a given buffer
* @param[in] vuart token from wr_vuart_open()
* @param[out] buf destination for read bytes
* @param[in] size numeber of bytes to read
*
* @return the number of read bytes
#ifdef SUPPORT_CERN_VMEBRIDGE
#define CERN_VMEBRIDGE_REQUIRED_ARG_NB 2
#define CERN_VMEBRIDGE 1
static struct option long_options[] = {
{"cern-vmebridge", no_argument, 0, CERN_VMEBRIDGE},
{"address", required_argument, 0, 'a'},
{"offset", required_argument, 0, 'o'},
{"data-width", required_argument, 0, 'w'},
{"am", required_argument, 0, 'm'},
{0, 0, 0, 0}
};
/*
* looks for vme-compat option
* returns 1 if vme-compat is matched, otherwhise 0.
*/
size_t wr_vuart_read(struct wr_vuart *vuart, char *buf, size_t size)
static int cern_vmebridge_match(int argc, char *argv[])
{
size_t s = size, n_rx = 0;
int c;
while(s--) {
c = wr_vuart_rx(vuart);
if(c < 0)
return n_rx;
*buf++ = c;
n_rx ++;
int i;
for (i = 0; i < argc; ++i) {
if (!strcmp(argv[i], "--cern-vmebridge")) {
return 1;
}
}
return n_rx;
return 0;
}
/**
* It writes a number of bytes from a given buffer
* @param[in] vuart token from wr_vuart_open()
* @param[in] buf buffer to write
* @param[in] size numeber of bytes to write
*
* @return the number of written bytes
/*
* Parse mandatory arguments to mmap VME physical address space
* return 0 on sucess, -1 in case of error
*/
size_t wr_vuart_write(struct wr_vuart *vuart, char *buf, size_t size)
static int cern_vmebridge_parse_args(int argc, char *argv[],
struct mapping_args *map_args)
{
int ret, arg_count = 0, c, option_index = 0;
size_t s = size;
/* set default values in case they are not provided*/
map_args->data_width = 32;
map_args->am = 0x39;
while ((c = getopt_long(argc, argv, "w:o:m:a:CERN_VMEBRIDGE", long_options,
&option_index)) != -1) {
switch(c) {
case CERN_VMEBRIDGE:
// nothing to do
break;
case 'w': /* optional arg */
ret = sscanf(optarg, "%u", &map_args->data_width);
if (ret != 1)
return -1;
if ( !(map_args->data_width == 8 ||
map_args->data_width == 16 ||
map_args->data_width == 32 ||
map_args->data_width == 64) )
return -1;
break;
case 'o': /* mandatory arg */
ret = sscanf(optarg, "0x%"SCNx64, &map_args->offset);
if (ret != 1)
return -1;
++arg_count;
break;
case 'm': /* optional arg */
ret = sscanf(optarg, "0x%x", &map_args->am);
if (ret != 1)
return -1;
break;
case 'a': /* mandatory arg */
ret = sscanf(optarg, "0x%"SCNx64, &map_args->addr);
if (ret != 1)
return -1;
++arg_count;
break;
case '?':
/* ignore unknown arguments */
break;
}
}
return (arg_count == CERN_VMEBRIDGE_REQUIRED_ARG_NB) ? 0 : -1;
}
#endif
while(s--)
wr_vuart_tx(vuart, *buf++);
#define REQUIRED_ARG_NB 2
struct mapping_args *dev_parse_mapping_args(int argc, char *argv[])
{
struct mapping_args *map_args;
char c;
int ret, arg_count = 0;
return size;
}
map_args = malloc(sizeof(struct mapping_args));
if (!map_args)
return NULL;
/*
* getopt variable: cancel error message printing because we look for
* only mapping options among other options
*/
opterr = 0;
#ifdef SUPPORT_CERN_VMEBRIDGE
if (cern_vmebridge_match(argc, argv)) {
ret = cern_vmebridge_parse_args(argc, argv, map_args);
if (ret < 0) {
goto out;
}
/*
* getopts variable: reset argument index in case application
* needs to parse arguments lokkink for specific args.
*/
optind = 1;
return map_args;
}
#endif
while ((c = getopt (argc, argv, "o:f:")) != -1)
{
switch (c)
{
case 'o':
ret = sscanf(optarg, "0x%x",
(unsigned int *)&map_args->offset);
if (ret != 1) {
goto out;
}
++arg_count;
break;
case 'f':
map_args->resource_file = optarg;
++arg_count;
break;
case '?':
/* ignore unknown arguments */
break;
}
}
/**
* It receives a single byte
* @param[in] vuart token from wr_vuart_open()
*
*
*/
int wr_vuart_rx(struct wr_vuart *vuart)
{
int rdr = vuart->base->RDR;
if (arg_count != REQUIRED_ARG_NB) {
goto out;
}
return (rdr & UART_HOST_RDR_RDY) ? UART_HOST_RDR_DATA_R(rdr) : -1;
}
/*
* getopts variable: reset argument index in case application needs to
* parse arguments lokkink for specific args.
*/
optind = 1;
return map_args;
out:
free(map_args);
return NULL;
}
/**
* It transmits a single byte
* @param[in] vuart token from wr_vuart_open()
*/
void wr_vuart_tx(struct wr_vuart *vuart, int data)
char *dev_mapping_help()
{
while(vuart->base->SR & UART_SR_RX_RDY)
static char help_msg[] =
"Device mapping options: \n"
"\t-f <file resource path> -o 0x<WR VUART address offset> \n"
#ifdef SUPPORT_CERN_VMEBRIDGE
"Device mapping options for CERN vmebus driver: \n"
"\t--cern-vmebridge -a 0x<VME base address> \n"
"\t-o 0x<WR VUART address offset> [-w <data-width[8,16,32] default=32>\n"
"\t-m 0x<address modifier default=0x39>]\n"
#endif
;
vuart->base->HOST_TDR = UART_HOST_TDR_DATA_W(data);
return help_msg;
}
......@@ -2,26 +2,43 @@
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#ifndef __LIBWHITERABBIT_H__
#define __LIBWHITERABBIT_H__
#ifndef __LIBDEVMAP_H__
#define __LIBDEVMAP_H__
struct wr_vuart {
#include <stdint.h>
struct mapping_args {
char *resource_file;
uint64_t offset;
#ifdef SUPPORT_CERN_VMEBRIDGE
/**
* VME memory map arguments
*/
uint32_t data_width; /**< default register size in bytes */
uint32_t am; /**< VME address modifier to use */
uint64_t addr; /**< physical base address */
#endif
};
/* device resource mapped into memory */
struct mapping_desc {
int fd;
void *mmap;
volatile struct UART_WB *base;
int map_pa_length; /* mapped length is page aligned */
volatile void *base; /* address of the concerned memory region */
int is_be; /* tells the device endianess */
struct mapping_args *args;
};
/**
* @defgroup vuart Virtual UART
* @defgroup device resource mapping
*@{
*/
extern struct wr_vuart *wr_vuart_open(char *resource_path, unsigned int offset);
extern void wr_vuart_close(struct wr_vuart *vuart);
extern size_t wr_vuart_read(struct wr_vuart *vuart, char *buf, size_t size);
extern size_t wr_vuart_write(struct wr_vuart *vuart, char *buf, size_t size);
extern int wr_vuart_rx(struct wr_vuart *vuart);
extern void wr_vuart_tx(struct wr_vuart *vuart, int data);
extern struct mapping_desc *dev_map(struct mapping_args *map_args,
uint32_t map_length);
extern void dev_unmap(struct mapping_desc *dev);
extern struct mapping_args *dev_parse_mapping_args(int argc, char *argv[]);
extern char *dev_mapping_help();
/** @}*/
#endif
#endif //__LIBDEVMAP_H__
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment