OpenCores
URL https://opencores.org/ocsvn/spacewire_light/spacewire_light/trunk

Subversion Repositories spacewire_light

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /spacewire_light/trunk/sw
    from Rev 5 to Rev 6
    Reverse comparison

Rev 5 → Rev 6

/rtems_driver/spacewirelight.h
0,0 → 1,512
/*
* Copyright 2010 Joris van Rantwijk
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
 
/**
* @file spacewirelight.h
* @brief SpaceWire Light driver for RTEMS 4.10 on LEON3.
*
* == Resources ==
*
* The driver allocates resources during the call to spwl_open_XXX() and
* releases all resources in spwl_close().
*
* The driver allocates memory by calling malloc() and rtems_memalign().
* The amount of memory required per device handle depends on the configuration
* of the driver and the hardware:
* ~200 bytes for the device handle;
* (rxbufs * rxbufsize) bytes for receive buffers;
* (txbufs * txbufsize) bytes for transmit buffers;
* 2 * 8 * (2**desctablesize) bytes for descriptor tables (where
* desctablesize depends on the configuration of the SpaceWire Light core).
*
* The driver creates a binary semaphores for internal synchronization.
*
* == Data flow ==
*
* Data delivery is packet-oriented. Although partial SpaceWire packets
* may be sent/received, delivery of such data is not guaranteed until
* the packet has been completed with an end-of-packet marker.
*
* For example, it is ok to call spwl_send() without an EOP flag to send
* a partial SpaceWire packet, but in this case the data may linger in memory
* for an unlimited period. Data delivery is not guaranteed until after
* spwl_send() has been called again with the final part of the packet and
* the EOP flag.
*
* Similarly, an incoming partial SpaceWire packet is not guaranteed to
* be delivered to the application until after the packet's EOP marker
* has been received.
*
* == Concurrency ==
*
* All API functions are reentrant, i.e. may be called concurrently
* from multiple tasks for different device handles.
*
* Most API functions are thread-safe, i.e. may be called concurrently
* for the same device handle. The exceptions are
* o spwl_close() must not be called concurrently with any other
* API function for the same device handle;
* o spwl_recv() and spwl_recv_rxbuf() must not be called concurrently
* with themselves or with eachother by multiple tasks for the same
* device handle;
* o spwl_send() and spwl_send_txbuf() must not be called concurrently
* with themselves or with eachother by multiple tasks for the same
* device handle.
*
* The API functions disable interrupts for limited periods of time
* to ensure exclusive access to data structures and hardware registers.
*/
 
#ifndef _SPACEWIRELIGHT_H
#define _SPACEWIRELIGHT_H
 
#include <stdint.h>
#include <rtems.h>
 
 
/** Handle used to identify an open SpaceWire Light device. */
typedef struct spwl_context * spwl_handle;
 
 
/** Device ID used by SpaceWire Light in AMBA plug&play table. */
#define DEVICE_SPACEWIRELIGHT 0x131
 
 
/** Link mode. */
typedef enum {
SPWL_LINKMODE_NOP = 0, /**< Leave the SpaceWire link as it is;
don't try to (re)start or stop it. */
SPWL_LINKMODE_START = 1, /**< Enable the SpaceWire link. */
SPWL_LINKMODE_AUTOSTART = 2, /**< Enable autostart mode. */
SPWL_LINKMODE_DISABLE = 3 /**< Disable the SpaceWire link. */
} spwl_linkmode;
 
 
/** Link status. */
typedef enum {
SPWL_LINK_OFF = 0,
SPWL_LINK_STARTED = 1,
SPWL_LINK_CONNECTING = 2,
SPWL_LINK_RUN = 3
} spwl_linkstatus;
 
 
/* Error bits returned by spwl_get_linkstatus(). */
#define SPWL_ERR_DISCONNECT 0x04
#define SPWL_ERR_PARITY 0x08
#define SPWL_ERR_ESCAPE 0x10
#define SPWL_ERR_CREDIT 0x20
#define SPWL_ERR_AHB 0x100
 
/* Flag bits used by recv/send functions. */
#define SPWL_WAIT 0x00 /**< Block task until call can proceed. */
#define SPWL_NO_WAIT 0x01 /**< Do not block task. */
#define SPWL_EOP 0x10 /**< Send/received EOP. */
#define SPWL_EEP 0x20 /**< Send/received EEP. */
 
/* Condition bits used by spwl_wait function. */
#define SPWL_COND_RDYRECV 0x01 /**< Received data is ready. */
#define SPWL_COND_RDYSEND 0x02 /**< Ready for spwl_send(). */
#define SPWL_COND_RDYSENDBUF 0x04 /**< Ready for spwl_send_txbuf(). */
#define SPWL_COND_RECLAIM 0x08 /**< Ready for spwl_reclaim_txbuf(). */
#define SPWL_COND_TIMECODE 0x10 /**< Timecode received after the last call to spwl_get_timecode(). */
#define SPWL_COND_LINKUP 0x20 /**< Link is up. */
#define SPWL_COND_LINKDOWN 0x40 /**< Link is down. */
 
 
/** Optional parameters to be passed when opening the driver. */
struct spwl_options {
 
/** Number of receive buffers to allocate.
(Can not be larger than the hardware descriptor table). */
unsigned int rxbufs;
 
/** Number of transmit buffers to allocate.
(Can not be larger than the hardware descriptor table). */
unsigned int txbufs;
 
/** Size of each receive buffer in bytes (at most 65532). */
unsigned int rxbufsize;
 
/** Size of each allocated transmit buffer in bytes (at most 65532).
Does not affect the size of queued application buffers. */
unsigned int txbufsize;
};
 
 
/** Initializer for spwl_options (default values). */
#define SPWL_OPTIONS_DEFAULT { 32, 16, 2048, 2048 }
 
 
/** Description of a buffer holding data to be transmitted. */
typedef struct spwl_txbuf {
const void *data; /**< Pointer to buffer, 4-byte aligned. */
uint16_t nbytes; /**< Number of bytes to be transmitted. */
uint16_t eop; /**< SPWL_EOP or SPWL_EEP or 0. */
uint32_t tag; /**< Optional application-defined field. */
struct spwl_txbuf *next; /**< Used internally by library. */
} spwl_txbuf_t;
 
 
/**
* Open a SpaceWire Light device.
*
* Open the index'th SpaceWire Light device found in the AMBA plug&play map.
* Allocate receive/transmit buffers and reset the device core.
*
* A SpaceWire Light device which is already open, may not be opened again
* until the existing handle has been closed.
*
* @retval RTEMS_SUCCESSFUL Open successful.
* @retval RTEMS_INVALID_NUMBER Core not found in AMBA plug&play map.
* @retval RTEMS_INVALID_SIZE Invalid option value.
*
* @param[out] h Device handle supplied by the driver.
* @param[in] index Index of the SpaceWire Light core to use.
* @param[in] opt Pointer to a structure of optional device parameters,
* or NULL to use default parameters.
*/
rtems_status_code spwl_open(spwl_handle *h,
unsigned int index,
const struct spwl_options *opt);
 
 
/**
* Open a SpaceWire Light device.
*
* Open a SpaceWire Light device identified by its hardware base address
* and IRQ number. Allocate receive/transmit buffers and reset the device core.
*
* A SpaceWire Light device which is already open, may not be opened again
* until the existing handle has been closed.
*
* @retval RTEMS_SUCCESSFUL Open successful.
* @retval RTEMS_INVALID_SIZE Invalid option value.
*
* @param[out] h Device handle supplied by the driver.
* @param[in] addr Base address of the SpaceWire Light core.
* @param[in] irq Interrupt number of the SpaceWire Light core (hardware
* IRQ number, zero-based).
* @param[in] opt Pointer to a structure of optional device parameters,
* or NULL to use default parameters.
*/
rtems_status_code spwl_open_hwaddr(spwl_handle *h,
unsigned long addr, unsigned int irq,
const struct spwl_options *opt);
 
 
/**
* Close an open SpaceWire Light device.
* Reset the SpaceWire Light core and release all associated memory.
*
* This function must not be called concurrently with any other
* API function for the same device handle.
*
* @param[in] h Device handle to close; this handle will be invalid
* after this call.
*/
void spwl_close(spwl_handle h);
 
 
/**
* Set the TX clock scaler for the link.
*
* The link bit rate defaults to 10 Mbit/s.
* Link handshake is always done at 10 Mbit/s, regardless of this setting.
*
* This function sets the TX bit rate to
* (txclkfreq / (scaler + 1)) bits per second;
* where txclkfreq is determined by the hardware configuration of the core.
*
* If a link is up, this function immediately affects the current bit rate.
* Future links will perform the handshake at 10 Mbit/s and then switch
* to the rate programmed through this function.
*
* @param[in] h Device handle.
* @param[in] scaler Scale factor for TX clock minus 1 (0 <= scaler <= 255).
*/
rtems_status_code spwl_set_linkspeed(spwl_handle h, unsigned int scaler);
 
 
/**
* Return the currently configured TX clock scaler (minus 1).
*/
unsigned int spwl_get_linkspeed(spwl_handle h);
 
 
/**
* Return the default TX scaler value (i.e. approximately 10 Mbit).
*/
unsigned int spwl_get_default_linkspeed(spwl_handle h);
 
 
/**
* Change the mode of the SpaceWire link.
*
* @param[in] h Device handle.
* @param[in] mode New mode for SpaceWire link.
*/
rtems_status_code spwl_set_linkmode(spwl_handle h, spwl_linkmode mode);
 
 
/**
* Return the current status of the SpaceWire link.
* Also return any link errors that occurred since the previous call to
* this function.
*
* @param[in] h Device handle.
* @param[out] status Current status of SpaceWire link.
* @param[out] errors Pending errors as OR mask of SPWL_ERR_xxx values.
*/
rtems_status_code spwl_get_linkstatus(spwl_handle h,
spwl_linkstatus *linkstatus,
unsigned int *errors);
 
 
/**
* Wait for specified condition with timeout.
*
* Block the task until at least one of the specified conditions occurs
* or the specified timeout elapses.
*
* Wait conditions are specified as a bitwise OR of SPWL_COND_xxx constants:
* SPWL_COND_RDYRECV : return when data is ready to be received;
* SPWL_COND_RDYSEND : return when spwl_send() can proceed;
* SPWL_COND_RDYSENDBUF : return when spwl_send_txbuf() can proceed;
* SPWL_COND_RECLAIM : return when a tx buffer is ready to be reclaimed.
* SPWL_COND_TIMECODE : return when a timecode has been received after
* the last call to spwl_get_timecode;
* SPWL_COND_LINKUP : return when the link is up;
* SPWL_COND_LINKDOWN : return when the link is down.
*
* @retval RTEMS_SUCCESSFUL At least one of the specified conditions satisfied.
* @retval RTEMS_TIMEOUT Timeout elapsed before any condition is satisfied.
*
* @param[in] h Device handle.
* @param[in] cond Conditions to wait for (OR mask of SPWL_COND_xxx bits).
* @param[out] cond Conditions satisfied (OR mask of SPWL_COND_xxx bits).
* @param[in] timeout Maximum time to wait as a number of RTEMS clock ticks,
* or 0 to wait forever.
*/
rtems_status_code spwl_wait(spwl_handle h,
unsigned int *cond, rtems_interval timeout);
 
 
/**
* Transfer received data to the specified application buffer.
*
* If the end of a SpaceWire packet is reached, stop the transfer and
* report the end-of-packet marker in *eop.
*
* Stop the transfer when maxlen bytes have been transfered without
* reaching the end of a SpaceWire packet. The remainder of the packet
* will be transfered during the next call to spwl_recv().
*
* If SPWL_NO_WAIT is specified and there is not enough data available
* to satisfy the request, transfer as much data as is currently available.
* If SPWL_WAIT is specified, block the task until either maxlen bytes
* have been received or the end of a SpaceWire packet is reached.
*
* @retval RTEMS_SUCCESSFUL Request was at least partially successful.
* @retval RTEMS_UNSATISFIED No data available and SPWL_NO_WAIT specified.
*
* This function is more efficient if the application buffer is 4-byte aligned.
*
* This function must not be called concurrently with itself or with
* spwl_recv_rxbuf() from multiple tasks for the same device handle.
*
* @param[in] h Device handle.
* @param[out] buf Pointer to application buffer.
* @param[in] maxlen Maximum number of bytes to transfer.
* @param[out] ntrans Actual number of bytes transfered.
* @param[out] eop End-of-packet marker; either SPWL_EOP or SPWL_EEP or 0.
* @param[in] flags Blocking mode, either SPWL_WAIT or SPWL_NO_WAIT.
*/
rtems_status_code spwl_recv(spwl_handle h,
void *buf, size_t maxlen, size_t *ntrans,
unsigned int *eop, unsigned int flags);
 
 
/**
* Send data to the SpaceWire link.
*
* Send maxlen bytes from the application buffer to the SpaceWire link,
* optionally followed by an end-of-packet marker. If an end-of-packet
* marker is not specified, the following call to spwl_send() will
* add more data to the same packet.
*
* If SPWL_NO_WAIT is specified and there is not enough room in the
* transmit queue to satisfy the request, transfer as much data as can
* be immediately stored.
* If SPWL_WAIT is specified, block the task until exactly maxlen bytes
* have been transfered.
*
* @retval RTEMS_SUCCESSFUL Request at least partially succesful.
* @retval RTEMS_UNSATISFIED No data could be transfered (SPWL_NO_WAIT).
*
* This function is more efficient if the application buffer is 4-byte aligned.
*
* This function must not be called concurrently with itself or with
* spwl_send_txbuf() from multiple tasks for the same device handle.
*
* @param[in] h Device handle.
* @param[in] buf Pointer to application buffer.
* @param[in] maxlen Maximum number of bytes to transfer.
* @param[out] ntrans Actual number of bytes transferred.
* @param[in] flags Bitwise OR of blocking flags (SPWL_WAIT or SPWL_NO_WAIT)
* and end-of-packet marker (SPWL_EOP or SPWL_EEP or 0).
*/
rtems_status_code spwl_send(spwl_handle h,
const void *buf, size_t maxlen, size_t *ntrans,
unsigned int flags);
 
 
/**
* Receive data from the SpaceWire link without copying.
*
* Take one frame from the tail of the receive queue and pass
* its data pointer to the application.
*
* If SPWL_NO_WAIT is specified and the receive queue is empty, return
* RTEMS_UNSATISFIED. If SPWL_WAIT is specified an the receive queue
* is empty, block the task until a received frame becomes available.
*
* Data buffers returned by this function are still owned by the driver.
* When the application has finished processing the data, it must release
* the buffers back to the driver by calling spwl_release_rxbuf().
*
* @retval RTEMS_SUCCESS Request successful.
* @retval RTEMS_UNSATISFIED No received buffer available (SPWL_NO_WAIT).
*
* The driver owns a limited number of receive buffers (configurable through
* spwl_options.rxbufs). If the application holds on to too many received
* buffers, the driver may run out of buffers to work with.
*
* This function is more efficient than spwl_recv() because it does not
* copy received data. Depending on the hardware configuration, the contents
* of DMA buffers returned by this function may not be coherent with the
* data cache of the CPU. The LEON3 provides transparent cache coherency
* only when cache snooping is enabled in the CPU configuration. Otherwise
* the application must explicitly deal with cache coherency issues, either
* by using cache bypass instructions when accessing the data or by flushing
* the data cache before accessing the buffer.
*
* Mixing calls to spwl_recv() and spwl_recv_rxbuf() is not recommended.
* This function must not be called concurrently with itself or with
* spwl_recv() from multiple tasks for the same device handle.
*
* @param[in] h Device handle.
* @param[out] buf Pointer to data buffer.
* @param[out] nbytes Number of data bytes in returned data buffer.
* @param[out] eop End-of-packet flags associated with returned buffer.
* @param[in] flags Blocking mode, either SPWL_WAIT or SPWL_NO_WAIT.
*/
rtems_status_code spwl_recv_rxbuf(spwl_handle h,
void **buf,
uint16_t *nbytes, unsigned int *eop,
unsigned int flags);
 
 
/**
* Release a receive buffer back to the driver.
*
* Buffers obtained through spwl_recv_rxbuf() must be released to the
* driver through this function. The order in which buffers are released
* is not important.
*
* Each time a pointer is obtained through spwl_recv_rxbuf(), there must be
* exactly one release of that pointer through this function. Total chaos
* will ensue if other pointers than those obtained through spwl_recv_rxbuf()
* are passed to this function, or if a pointer acquired once is released
* multiple times.
*
* @param[in] h Device handle.
* @param[in] buf Data buffer to be returned to the driver.
*/
rtems_status_code spwl_release_rxbuf(spwl_handle h, void *buf);
 
 
/**
* Submit data for transmission to the SpaceWire link without copying.
*
* Add the buffer to the tail of the transmit queue. This function is more
* efficient than spwl_send() because it does not copy data.
*
* The data buffer pointer (buf->data) must be 4-byte aligned.
*
* If SPWL_NO_WAIT is specified and the transmit queue is full, return
* RTEMS_UNSATISFIED. If SPWL_WAIT is specified and the transmit queue
* is full, block the task until the buffer can be queued.
*
* The driver internally keeps a pointers to the submitted spwl_txbuf
* structure. The application must guarantee that this structure, and
* the buffer it points to, remain valid and unchanged after this call.
* After the driver finishes processing of the data, the buffer structure
* is returned to the application through spwl_reclaim_txbuf().
*
* @retval RTEMS_SUCCESSFUL Buffer queued for transmission.
* @retval RTEMS_UNSATISFIED No room in TX queue (SPWL_NO_WAIT).
*
* Mixing calls to spwl_send() and spwl_send_txbuf() is not recommended.
* This function must not be called concurrently with itself or with
* spwl_send() from multiple tasks for the same device handle.
*
* @param[in] h Device handle.
* @param[in] buf Structure describing frame to be queued.
* @param[in] flags Blocking mode, either SPWL_WAIT or SPWL_NO_WAIT.
*/
rtems_status_code spwl_send_txbuf(spwl_handle h,
struct spwl_txbuf *buf, unsigned int flags);
 
 
/**
* Reclaim transmit buffer after completion of transmission.
*
* Buffers queued through spwl_send_txbuf() are eventually passed back
* to the application through this function. The driver returns the same
* spwl_txbuf pointers that were originally submitted by the application.
* Buffers are passed back in the same order in which they were submitted
* by the application.
*
* Except for the "next" field, the contents of the spwl_txbuf structures
* is not changed by the driver. The "next" field is used internally by the
* driver and is set to NULL when the buffer is returned.
*
* If SPWL_NO_WAIT is specified and there are no buffers ready to be reclaimed,
* return RTEMS_UNSATISFIED. If SPWL_WAIT is specified, block the task until
* a buffer can be reclaimed.
*
* @retval RTEMS_SUCCESSFUL Successfully reclaimed a buffer.
* @retval RTEMS_UNSATISFIED No reclaimable buffer (SPWL_NO_WAIT).
*
* @param[in] h Device handle.
* @param[out] buf Completed spwl_txbuf structure.
* @param[in] flags Blocking mode, either SPWL_WAIT or SPWL_NO_WAIT.
*/
rtems_status_code spwl_reclaim_txbuf(spwl_handle h,
struct spwl_txbuf **buf,
unsigned int flags);
 
 
/**
* Return last received timecode.
*/
uint8_t spwl_get_timecode(spwl_handle h);
 
 
/**
* Send a timecode to the SpaceWire link.
*
* @param[in] h Device handle.
* @param[in] timecode Time code to transmit.
*/
rtems_status_code spwl_send_timecode(spwl_handle h, uint8_t timecode);
#endif
/* end */
/rtems_driver/spwltest.c
0,0 → 1,1306
/*
* Test program for SpaceWire Light RTEMS driver.
* Joris van Rantwijk, 2010.
*/
 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <rtems.h>
#include <rtems/error.h>
#include <bsp.h>
#include "spacewirelight.h"
 
void Init(rtems_task_argument);
 
#define CONFIGURE_INIT
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT_TASK_ENTRY_POINT Init
#define CONFIGURE_MAXIMUM_TASKS 10
#define CONFIGURE_MAXIMUM_SEMAPHORES 10
#include <rtems/confdefs.h>
 
 
struct data_test_context {
int queued;
unsigned int rxpackets;
unsigned int rxblocksize;
unsigned int txpackets;
unsigned int txpacketsize;
unsigned int txblocksize;
int verify;
rtems_id semaphore;
volatile unsigned int txdone_packets;
volatile unsigned int rxdone_packets;
volatile unsigned int rxdone_bytes;
volatile unsigned int rxminsize;
volatile unsigned int rxmaxsize;
volatile int mismatch;
};
 
static spwl_handle spwh;
static unsigned int spw_index;
static struct spwl_options spw_opt = SPWL_OPTIONS_DEFAULT;
static spwl_linkmode spw_mode;
 
#define MAX_BLOCK_SIZE 16384 /* do not change */
static unsigned char rxbuf[MAX_BLOCK_SIZE];
static unsigned char txpool[2*MAX_BLOCK_SIZE];
static spwl_txbuf_t txbuf_desc[16];
 
static const unsigned int autotest_blocksize[] = {
1, 101, 500, 2048, 4000, 0 };
static const unsigned int autotest_packetsize[] = {
1, 2, 3, 4, 100, 101, 1000, 4096, 10000, 100000, 1000000, 0 };
 
/* Report error and stop program. */
static void fatal_error(const char *s, rtems_status_code err)
{
fprintf(stderr, "ERROR: %s", s);
if (err)
fprintf(stderr, " (%s)", rtems_status_text(err));
fprintf(stderr, "\n");
rtems_shutdown_executive(1);
}
 
 
/* Wait for user to enter a string. */
static void get_string(char *buf, size_t maxlen)
{
unsigned int i;
int k;
char c;
 
i = strlen(buf);
printf("%s", buf);
fflush(stdout);
 
while (1) {
k = read(STDIN_FILENO, &c, 1);
if (k != 1)
fatal_error("read from console", 0);
if (c == '\b' && i > 0) {
i--;
printf("\b \b");
fflush(stdout);
} else if (c == '\n' || c == '\r') {
buf[i] = '\0';
printf("\n");
fflush(stdout);
return;
} else if (i + 1 < maxlen && c >= 32 && c < 127) {
buf[i++] = c;
printf("%c", c);
fflush(stdout);
}
}
}
 
 
/* Wait for user to enter a non-negative number. */
static int get_num(const char *prompt, int low, int high, int deflt)
{
char buf[20];
char *p;
unsigned long v;
 
while (1) {
printf("%s ", prompt);
fflush(stdout);
 
if (deflt >= 0)
sprintf(buf, "%d", deflt);
else
buf[0] = '\0';
get_string(buf, sizeof(buf));
 
v = strtoul(buf, &p, 10);
while (p != buf && *p == ' ')
p++;
 
if (p != buf && *p == '\0' && v >= low && v <= high)
return v;
}
}
 
 
/* Wait for user to enter an option index.
Return entered number, or -1 if an empty string was entered. */
static int get_opt(int high)
{
char buf[20];
char *p;
unsigned long v;
 
while (1) {
printf("Option (0 .. %d) ? ", high);
fflush(stdout);
 
buf[0] = '\0';
get_string(buf, sizeof(buf));
 
if (buf[0] == '\0')
return -1;
 
v = strtoul(buf, &p, 10);
while (p != buf && *p == ' ')
p++;
 
if (p != buf && *p == '\0' && v <= high)
return v;
}
}
 
 
/* Set console to non-blocking. */
void set_nonblocking(int fd, struct termios *tcattr_orig)
{
cc_t vmin_orig;
tcgetattr(STDIN_FILENO, tcattr_orig);
vmin_orig = tcattr_orig->c_cc[VMIN];
tcattr_orig->c_cc[VMIN] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, tcattr_orig);
tcattr_orig->c_cc[VMIN] = vmin_orig;
}
 
 
/* Open driver. */
void open_driver(void)
{
rtems_status_code ret;
 
printf("Opening driver for device index %u\n"
" options: rxbufs=%u, txbufs=%u, rxbufsize=%u, txbufsize=%u\n",
spw_index,
spw_opt.rxbufs, spw_opt.txbufs,
spw_opt.rxbufsize, spw_opt.txbufsize);
 
ret = spwl_open(&spwh, spw_index, &spw_opt);
if (ret != RTEMS_SUCCESSFUL) {
if (ret == RTEMS_INVALID_NUMBER)
fprintf(stderr, "ERROR: SpaceWire Light core not found\n");
fatal_error("spwl_open", ret);
}
 
spw_mode = SPWL_LINKMODE_NOP;
 
printf(" ok\n");
}
 
 
/* Show current link mode and status. */
void show_status(void)
{
spwl_linkstatus status;
unsigned int speed;
 
spwl_get_linkstatus(spwh, &status, NULL);
speed = 100 * (spwl_get_default_linkspeed(spwh) + 1) / (spwl_get_linkspeed(spwh) + 1);
printf("[ mode=%s, status=%s, speed=%u.%uMbit ]\n",
((spw_mode == SPWL_LINKMODE_START) ? "start" :
(spw_mode == SPWL_LINKMODE_AUTOSTART) ? "autostart" :
(spw_mode == SPWL_LINKMODE_DISABLE) ? "disable" : "nop"),
((status == SPWL_LINK_STARTED) ? "started" :
(status == SPWL_LINK_CONNECTING) ? "connecting" :
(status == SPWL_LINK_RUN) ? "RUN" : "off"),
speed/10, speed%10);
}
 
 
/* Re-initialize driver. */
void do_init_driver(void)
{
amba_apb_device apbdev;
unsigned int i;
 
printf("\n---- Re-initialize driver ----\n");
printf("Closing driver\n");
spwl_close(spwh);
 
printf("Detected SpaceWire Light devices:\n");
for (i = 0; ; i++) {
if (!amba_find_next_apbslv(&amba_conf, VENDOR_OPENCORES, DEVICE_SPACEWIRELIGHT, &apbdev, i))
break;
printf(" index=%u, addr=0x%08x, irq=%u\n", i, apbdev.start, apbdev.irq);
}
 
spw_index = get_num("Enter index of device to open:", 0, 100, -1);
spw_opt.rxbufs = get_num("Number of RX buffers (min 0, max 4096):",
0, 4096, spw_opt.rxbufs);
spw_opt.txbufs = get_num("Number of TX buffers (min 0, max 4096):",
0, 4096, spw_opt.txbufs);
spw_opt.rxbufsize = get_num("RX buffer size (bytes, min 32, max 16384):",
0, MAX_BLOCK_SIZE, spw_opt.rxbufsize);
spw_opt.txbufsize = get_num("TX buffer size (bytes, min 32, max 16384):",
0, MAX_BLOCK_SIZE, spw_opt.txbufsize);
 
open_driver();
}
 
 
/* Change link mode. */
void do_set_link_mode(void)
{
rtems_status_code ret;
int opt;
 
printf("\n---- Change link mode ----\n");
show_status();
printf(" 1. Start - start link; restart link after error\n");
printf(" 2. Autostart - wait for remote activity before starting the link\n");
printf(" 3. Disable - disable link\n");
printf(" 4. Nop - keep current link status; do not restart after error\n");
 
opt = get_opt(4);
if (opt > 0) {
printf("\n");
 
switch (opt) {
case 1:
printf("Setting link mode = start\n");
spw_mode = SPWL_LINKMODE_START;
break;
case 2:
printf("Setting link mode = autostart\n");
spw_mode = SPWL_LINKMODE_AUTOSTART;
break;
case 3:
printf("Setting link mode = disable\n");
spw_mode = SPWL_LINKMODE_DISABLE;
break;
case 4:
printf("Setting link mode = nop\n");
spw_mode = SPWL_LINKMODE_NOP;
break;
}
 
ret = spwl_set_linkmode(spwh, spw_mode);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_set_linkmode", ret);
printf(" ok\n");
}
}
 
 
/* Change link speed. */
void do_set_link_speed(void)
{
char prompt[80];
rtems_status_code ret;
int maxspeed, speed, scaler, scaler10;
 
printf("\n---- Change link speed ----\n");
 
scaler10 = spwl_get_default_linkspeed(spwh);
scaler = spwl_get_linkspeed(spwh);
speed = (10 * (scaler10 + 1) + scaler / 2) / (scaler + 1);
maxspeed = 10 * (scaler10 + 1);
 
sprintf(prompt, "New link speed in Mbit/s (2 .. %d) ?", maxspeed);
speed = get_num(prompt, 2, maxspeed, speed);
 
scaler = (10 * (scaler10 + 1) + speed / 2) / speed - 1;
if (scaler > 255)
scaler = 255;
 
speed = 100 * (scaler10 + 1) / (scaler + 1);
printf("Setting speed = %d.%d Mbit/s, scaler = %d\n", speed/10, speed%10, scaler);
ret = spwl_set_linkspeed(spwh, scaler);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_set_linkmode", ret);
printf(" ok\n");
}
 
 
/* RX/TX event loop. */
static int do_data_test_eventloop(struct data_test_context *ctx)
{
rtems_status_code ret;
unsigned int rxdone_packets = 0, rxdone_bytes = 0;
unsigned int rxminsize = 0, rxmaxsize = 0;
unsigned int txdone_packets = 0;
unsigned int rxpos = 0, txpos = 0, txbufp = 0, txbufn = 0;
unsigned int cond;
unsigned int f, offset;
void *buf;
size_t p;
int k;
char c;
 
/* Flush pending reclaimable buffers from previous test. */
{
spwl_txbuf_t *tmp;
while (spwl_reclaim_txbuf(spwh, &tmp, SPWL_NO_WAIT) == RTEMS_SUCCESSFUL)
;
}
 
/* Run until test completes or user aborts. */
while (txdone_packets < ctx->txpackets ||
rxdone_packets < ctx->rxpackets ||
txbufn > 0) {
 
/* Abort test when user hits Enter. */
do {
k = read(STDIN_FILENO, &c, 1);
} while (k == 1 && c != '\n' && c != '\r');
if (k == 1 && (c == '\n' || c == '\r'))
return -1;
 
/* Wait until progress can be made. */
cond = 0;
if (txbufn > 0)
cond |= SPWL_COND_RECLAIM;
if (ctx->queued && txdone_packets < ctx->txpackets && txbufn < 16)
cond |= SPWL_COND_RDYSENDBUF;
if (!ctx->queued && txdone_packets < ctx->txpackets)
cond |= SPWL_COND_RDYSEND;
if (rxdone_packets < ctx->rxpackets)
cond |= SPWL_COND_RDYRECV;
ret = spwl_wait(spwh, &cond, rtems_clock_get_ticks_per_second());
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_TIMEOUT)
fatal_error("spwl_wait", ret);
 
/* Send data. */
if ((cond & (SPWL_COND_RDYSEND | SPWL_COND_RDYSENDBUF)) != 0) {
if (((cond & SPWL_COND_RDYSEND) != 0 && ctx->queued) ||
((cond & SPWL_COND_RDYSENDBUF) != 0 && !ctx->queued) ||
txbufn == 16 ||
txdone_packets >= ctx->txpackets)
fatal_error("spwl_wait, unexpected condition", 0);
f = SPWL_EOP;
p = ctx->txpacketsize - txpos;
if (p > ctx->txblocksize) {
p = ctx->txblocksize;
f = 0;
}
offset = (txdone_packets * 4 + txpos) & (MAX_BLOCK_SIZE-1);
if (ctx->queued) {
txbuf_desc[txbufp].data = txpool + offset;
txbuf_desc[txbufp].nbytes = p;
txbuf_desc[txbufp].eop = f;
ret = spwl_send_txbuf(spwh, txbuf_desc + txbufp, SPWL_NO_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_send_txbuf", ret);
txbufp = (txbufp + 1) & 15;
txbufn++;
} else {
ret = spwl_send(spwh, txpool + offset, p, &p, f | SPWL_NO_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_send", ret);
}
txpos += p;
if (txpos >= ctx->txpacketsize) {
txdone_packets++;
txpos = 0;
}
}
 
/* Receive data. */
if ((cond & SPWL_COND_RDYRECV) != 0) {
if (rxdone_packets >= ctx->rxpackets)
fatal_error("spwl_wait, unexpected condition", 0);
if (ctx->queued) {
uint16_t nbytes;
ret = spwl_recv_rxbuf(spwh, &buf, &nbytes, &f, SPWL_NO_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_recv_rxbuf", ret);
p = nbytes;
} else {
ret = spwl_recv(spwh, rxbuf, ctx->rxblocksize, &p, &f, SPWL_NO_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_recv", ret);
buf = rxbuf;
}
if (ctx->verify) {
offset = (rxdone_packets * 4 + rxpos) & (MAX_BLOCK_SIZE-1);
if (memcmp(buf, txpool + offset, p) != 0)
ctx->mismatch = 1;
}
rxpos += p;
rxdone_bytes += p;
if (f == SPWL_EEP || f == SPWL_EOP) {
if (f == SPWL_EEP)
ctx->mismatch = 1;
if (ctx->verify && rxpos > 0 && rxpos != ctx->txpacketsize)
ctx->mismatch = 1;
if (rxpos > 0)
rxdone_packets++;
if (rxpos > 0 && (rxpos < rxminsize || rxminsize == 0))
rxminsize = rxpos;
if (rxpos > rxmaxsize)
rxmaxsize = rxpos;
rxpos = 0;
}
if (ctx->queued) {
ret = spwl_release_rxbuf(spwh, buf);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_release_rxbuf", ret);
}
}
 
/* Reclaim TX buffers (queued mode). */
if ((cond & SPWL_COND_RECLAIM) != 0) {
spwl_txbuf_t *tmp;
if (txbufn == 0)
fatal_error("spwl_wait, unexpeced condition", 0);
ret = spwl_reclaim_txbuf(spwh, &tmp, SPWL_NO_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_reclaim_txbuf", ret);
txbufn--;
}
 
/* Update results. */
ctx->txdone_packets = txdone_packets;
ctx->rxdone_packets = rxdone_packets;
ctx->rxdone_bytes = rxdone_bytes;
ctx->rxminsize = rxminsize;
ctx->rxmaxsize = rxmaxsize;
}
 
return 0;
}
 
 
/* RX worker thread. */
static void rxtask_main(uintptr_t arg)
{
struct data_test_context *ctx = (struct data_test_context *)arg;
rtems_status_code ret;
unsigned int rxdone_packets = 0, rxdone_bytes = 0;
unsigned int rxminsize = 0, rxmaxsize = 0;
unsigned int rxpos = 0;
unsigned int f, offset;
size_t p;
void *buf;
 
/* Receive data until test complete. */
while (rxdone_packets < ctx->rxpackets) {
 
if (ctx->queued) {
uint16_t nbytes;
ret = spwl_recv_rxbuf(spwh, &buf, &nbytes, &f, SPWL_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_recv_rxbuf", ret);
p = nbytes;
} else {
ret = spwl_recv(spwh, rxbuf, ctx->rxblocksize, &p, &f, SPWL_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_recv", ret);
buf = rxbuf;
}
 
if (ctx->verify) {
offset = (rxdone_packets * 4 + rxpos) & (MAX_BLOCK_SIZE-1);
if (memcmp(buf, txpool + offset, p) != 0)
ctx->mismatch = 1;
}
 
rxpos += p;
rxdone_bytes += p;
 
if (f == SPWL_EEP || f == SPWL_EOP) {
if (f == SPWL_EEP)
ctx->mismatch = 1;
if (ctx->verify && rxpos > 0 && rxpos != ctx->txpacketsize)
ctx->mismatch = 1;
if (rxpos > 0)
rxdone_packets++;
if (rxpos > 0 && (rxpos < rxminsize || rxminsize == 0))
rxminsize = rxpos;
if (rxpos > rxmaxsize)
rxmaxsize = rxpos;
rxpos = 0;
}
 
if (ctx->queued) {
ret = spwl_release_rxbuf(spwh, buf);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_release_rxbuf", ret);
}
 
/* Update results. */
ctx->rxdone_packets = rxdone_packets;
ctx->rxdone_bytes = rxdone_bytes;
ctx->rxminsize = rxminsize;
ctx->rxmaxsize = rxmaxsize;
}
 
/* Release semaphore, then sleep forever. */
rtems_semaphore_release(ctx->semaphore);
rtems_task_suspend(RTEMS_SELF);
}
 
 
/* TX worker thread. */
static void txtask_main(uintptr_t arg)
{
struct data_test_context *ctx = (struct data_test_context *)arg;
rtems_status_code ret;
unsigned int txdone_packets = 0;
unsigned int txpos = 0, txbufp = 0, txbufn = 0;
unsigned int f, offset;
size_t p;
 
/* Flush pending reclaimable buffers from previous test. */
{
spwl_txbuf_t *tmp;
while (spwl_reclaim_txbuf(spwh, &tmp, SPWL_NO_WAIT) == RTEMS_SUCCESSFUL)
;
}
 
/* Send data until test completes. */
while (txdone_packets < ctx->txpackets || txbufn > 0) {
 
/* Send data. */
if (txdone_packets < ctx->txpackets && txbufn < 16) {
f = SPWL_EOP;
p = ctx->txpacketsize - txpos;
if (p > ctx->txblocksize) {
p = ctx->txblocksize;
f = 0;
}
offset = (txdone_packets * 4 + txpos) & (MAX_BLOCK_SIZE-1);
if (ctx->queued) {
txbuf_desc[txbufp].data = txpool + offset;
txbuf_desc[txbufp].nbytes = p;
txbuf_desc[txbufp].eop = f;
ret = spwl_send_txbuf(spwh, txbuf_desc + txbufp, SPWL_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_send_txbuf", ret);
txbufp = (txbufp + 1) & 15;
txbufn++;
} else {
ret = spwl_send(spwh, txpool + offset, p, &p, f | SPWL_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_send", ret);
}
txpos += p;
if (txpos >= ctx->txpacketsize) {
txdone_packets++;
txpos = 0;
}
}
 
/* Reclaim TX buffers (queued mode). */
if (ctx->queued && (txbufn == 16 || txdone_packets == ctx->txpackets)) {
spwl_txbuf_t *tmp;
ret = spwl_reclaim_txbuf(spwh, &tmp, SPWL_WAIT);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_reclaim_txbuf", ret);
if (tmp != txbuf_desc + ((16+txbufp-txbufn)&15))
fatal_error("spwl_reclaim_txbuf returned unexpected pointer", 0);
txbufn--;
}
 
/* Update results. */
ctx->txdone_packets = txdone_packets;
}
 
/* Release semaphore, then sleep forever. */
rtems_semaphore_release(ctx->semaphore);
rtems_task_suspend(RTEMS_SELF);
}
 
 
/* Run a data send/receive test. Return 0 if ok, -1 if an error occurred. */
int do_data_test(int queued, int eventloop,
unsigned int rxpackets, unsigned int rxblocksize,
unsigned int txpackets, unsigned int txpacketsize,
unsigned int txblocksize, int verify)
{
rtems_status_code ret;
struct termios tcattr;
struct data_test_context ctx;
struct timespec tstart, tend;
rtems_id rxtask = 0, txtask = 0;
int aborted = 0;
int activethreads;
unsigned int elapsedms;
 
if (queued && txpackets > 0 && (txblocksize & 3) != 0)
fatal_error("invalid txblocksize in queued mode", 0);
 
printf("\nStarting data test:\n");
printf(" api: %s, %s\n", queued ? "queue" : "copy",
eventloop ? "eventloop" : "blocking");
if (txpackets > 0) {
printf(" send: %u packets of %u bytes, blocksize=%u\n",
txpackets, txpacketsize, txblocksize);
}
if (rxpackets > 0) {
printf(" receive: %u packets", rxpackets);
if (!queued)
printf(", blocksize=%u", rxblocksize);
printf(", verify=%s", verify ? "yes" : "no");
printf("\n");
}
 
set_nonblocking(STDIN_FILENO, &tcattr);
printf(" test started ... press Enter to abort\n");
 
/* Set up context structure. */
ctx.queued = queued;
ctx.rxpackets = rxpackets;
ctx.rxblocksize = rxblocksize;
ctx.txpackets = txpackets;
ctx.txpacketsize = txpacketsize;
ctx.txblocksize = txblocksize;
ctx.verify = verify;
ctx.semaphore = 0;
ctx.txdone_packets = 0;
ctx.rxdone_packets = 0;
ctx.rxdone_bytes = 0;
ctx.rxminsize = 0;
ctx.rxmaxsize = 0;
ctx.mismatch = 0;
 
/* Create worker threads and completion semaphore for multi-thread test. */
if (!eventloop) {
ret = rtems_semaphore_create(rtems_build_name('d','o','n','e'),
0,
RTEMS_COUNTING_SEMAPHORE,
RTEMS_NO_PRIORITY,
&ctx.semaphore);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("rtems_semaphore_create", ret);
if (rxpackets > 0) {
ret = rtems_task_create(rtems_build_name('r','x','t','s'),
200,
RTEMS_CONFIGURED_MINIMUM_STACK_SIZE,
RTEMS_PREEMPT,
RTEMS_NO_FLOATING_POINT,
&rxtask);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("rtems_task_create", ret);
}
if (txpackets > 0) {
ret = rtems_task_create(rtems_build_name('t','x','t','s'),
200,
RTEMS_CONFIGURED_MINIMUM_STACK_SIZE,
RTEMS_PREEMPT | RTEMS_NO_TIMESLICE,
RTEMS_LOCAL,
&txtask);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("rtems_task_create", ret);
}
}
 
/* Start timer. */
rtems_clock_get_uptime(&tstart);
 
/* Run test. */
if (eventloop) {
 
if (do_data_test_eventloop(&ctx) != 0)
aborted = 1;
 
} else {
 
/* Start worker threads. */
if (rxpackets > 0)
rtems_task_start(rxtask, rxtask_main, (uintptr_t)&ctx);
if (txpackets > 0)
rtems_task_start(txtask, txtask_main, (uintptr_t)&ctx);
 
/* Wait until test complete or test aborted. */
activethreads = (rxpackets > 0) + (txpackets > 0);
while (activethreads) {
int k;
char c;
 
/* Abort test when user hits Enter. */
do {
k = read(STDIN_FILENO, &c, 1);
} while (k == 1 && c != '\n' && c != '\r');
if (k == 1 && (c == '\n' || c == '\r')) {
aborted = 1;
break;
}
 
ret = rtems_semaphore_obtain(ctx.semaphore, RTEMS_WAIT,
rtems_clock_get_ticks_per_second());
if (ret == RTEMS_SUCCESSFUL)
activethreads--;
}
 
}
 
/* Stop timer. */
rtems_clock_get_uptime(&tend);
 
/* Clean up resources. */
if (!eventloop) {
if (rxpackets > 0)
rtems_task_delete(rxtask);
if (txpackets > 0)
rtems_task_delete(txtask);
rtems_semaphore_delete(ctx.semaphore);
}
 
/* Report result. */
if (aborted)
printf(" aborted, ");
else
printf(" done, ");
 
elapsedms = 1 + (tend.tv_sec - tstart.tv_sec) * 1000 +
tend.tv_nsec / 1000000 - tstart.tv_nsec / 1000000;
printf("%3d.%03d seconds elapsed\n", elapsedms / 1000, elapsedms % 1000);
if (txpackets > 0) {
uint64_t rate = (uint64_t)ctx.txdone_packets * (uint64_t)txpacketsize * 1000U / elapsedms;
printf(" sent %u packets (%u bytes/s)\n",
ctx.txdone_packets, (unsigned int)rate);
}
if (rxpackets > 0) {
uint64_t rate = (uint64_t)ctx.rxdone_bytes * 1000U / elapsedms;
printf(" received %u packets, %u bytes (%u bytes/s)\n",
ctx.rxdone_packets, ctx.rxdone_bytes, (unsigned int)rate);
if (ctx.rxdone_packets > 0)
printf(" received min packet size = %u, max packet size = %u\n",
ctx.rxminsize, ctx.rxmaxsize);
}
if (ctx.mismatch)
printf(" MISMATCH OR EEP DETECTED IN RECEIVED DATA\n");
 
/* Restore stdin mode. */
tcsetattr(STDIN_FILENO, TCSANOW, &tcattr);
 
return (aborted || ctx.mismatch) ? -1 : 0;
}
 
 
/* Run a series of loopback tests. */
int do_autotest(int verify)
{
int queued, eventloop;
unsigned int npackets, packetsize, rxblocksize, txblocksize;
int i, j, ret;
 
printf("\nStarting automatic test\n");
 
for (queued = 0; queued <= 1; queued++) {
for(eventloop = 0; eventloop <= 1; eventloop++) {
for (i = 0; autotest_packetsize[i] > 0; i++) {
for (j = 0; autotest_blocksize[j] > 0; j++) {
 
packetsize = autotest_packetsize[i];
txblocksize = autotest_blocksize[j];
 
if (queued && (txblocksize & 3) != 0)
continue;
 
npackets = 10000000 / packetsize;
if (npackets > 100000)
npackets = 100000;
if (npackets * packetsize / txblocksize > 100000)
npackets = 100000 * txblocksize / packetsize;
if (npackets < 1)
continue;
 
rxblocksize = 4000;
ret = do_data_test(queued, eventloop,
npackets, rxblocksize,
npackets, packetsize, txblocksize,
verify);
if (ret < 0)
return ret;
 
if (!queued && autotest_blocksize[j] != 4000) {
txblocksize = 4000;
rxblocksize = autotest_blocksize[j];
ret = do_data_test(queued, eventloop,
npackets, rxblocksize,
npackets, packetsize, txblocksize,
verify);
if (ret < 0)
return ret;
}
 
}
}
}
}
 
printf("\nAutomatic test completed\n");
return 0;
}
 
 
/* Put system in passive loopback mode. */
void do_passive_loopback(int queued, int blocksize)
{
rtems_status_code ret;
struct termios tcattr;
struct timespec tstart, tend;
unsigned int done_packets = 0, done_bytes = 0;
unsigned int txpos = 0, txbufp = 0, txbufn = 0;
size_t rxlen = 0;
unsigned int rxeop = 0;
void *rxbufp;
unsigned int cond;
int k;
char c;
unsigned int elapsedms;
uint64_t rate;
 
printf("\nStarting passive loopback mode:\n");
printf(" api: %s\n", queued ? "queue" : "copy");
if (!queued)
printf(" blocksize: %u\n", blocksize);
 
set_nonblocking(STDIN_FILENO, &tcattr);
printf(" started ... press Enter to stop\n");
 
/* Flush pending reclaimable buffers from previous test. */
{
spwl_txbuf_t *tmp;
while (spwl_reclaim_txbuf(spwh, &tmp, SPWL_NO_WAIT) == RTEMS_SUCCESSFUL)
;
}
 
/* Start timer. */
rtems_clock_get_uptime(&tstart);
 
/* Run in passive loopback. */
while (1) {
 
/* Check if user pressed Enter. */
do {
k = read(STDIN_FILENO, &c, 1);
} while (k == 1 && c != '\n' && c != '\r');
if (k == 1 && (c == '\n' || c == '\r'))
break;
 
/* Receive data. */
if (rxlen == 0 && rxeop == 0) {
if (queued) {
uint16_t rxlen16;
ret = spwl_recv_rxbuf(spwh, &rxbufp, &rxlen16, &rxeop, SPWL_NO_WAIT);
rxlen = rxlen16;
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_UNSATISFIED)
fatal_error("spwl_recv_rxbuf", ret);
} else {
ret = spwl_recv(spwh, rxbuf, blocksize, &rxlen, &rxeop, SPWL_NO_WAIT);
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_UNSATISFIED)
fatal_error("spwl_recv", ret);
}
done_bytes += rxlen;
if (rxeop)
done_packets++;
}
 
/* Send data. */
if ((rxlen > 0 || rxeop != 0) && txbufn < 16) {
if (queued) {
txbuf_desc[txbufp].data = rxbufp;
txbuf_desc[txbufp].nbytes = rxlen;
txbuf_desc[txbufp].eop = rxeop;
ret = spwl_send_txbuf(spwh, txbuf_desc + txbufp, SPWL_NO_WAIT);
if (ret == RTEMS_SUCCESSFUL) {
ret = spwl_release_rxbuf(spwh, rxbufp);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_release_rxbuf", ret);
rxlen = rxeop = 0;
txbufp = (txbufp + 1) & 15;
txbufn++;
}
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_UNSATISFIED)
fatal_error("spwl_send_txbuf", ret);
} else {
size_t ntrans;
ret = spwl_send(spwh, rxbuf + txpos, rxlen - txpos, &ntrans, rxeop | SPWL_NO_WAIT);
if (ret == RTEMS_SUCCESSFUL) {
txpos += ntrans;
if (txpos == rxlen)
rxlen = rxeop = txpos = 0;
}
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_UNSATISFIED)
fatal_error("spwl_send", ret);
}
}
 
/* Reclaim buffers. */
if (txbufn > 0) {
struct spwl_txbuf *p;
ret = spwl_reclaim_txbuf(spwh, &p, SPWL_NO_WAIT);
if (ret == RTEMS_SUCCESSFUL) {
if (p != txbuf_desc + ((txbufp + 16 - txbufn) & 15))
fatal_error("spwl_reclaim_txbuf returned unexpected buffer", 0);
txbufn--;
}
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_UNSATISFIED)
fatal_error("spwl_reclaim_txbuf", ret);
}
 
/* Wait until ready. */
cond = 0;
if (rxlen == 0 && rxeop == 0)
cond |= SPWL_COND_RDYRECV;
if (!queued && (rxlen > 0 || rxeop != 0))
cond |= SPWL_COND_RDYSEND;
if (queued && (rxlen > 0 || rxeop != 0) && txbufn < 16)
cond |= SPWL_COND_RDYSENDBUF;
if (txbufn > 0)
cond |= SPWL_COND_RECLAIM;
ret = spwl_wait(spwh, &cond, rtems_clock_get_ticks_per_second());
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_TIMEOUT)
fatal_error("spwl_wait", ret);
}
 
/* Stop timer. */
rtems_clock_get_uptime(&tend);
 
/* Report result. */
elapsedms = 1 + (tend.tv_sec - tstart.tv_sec) * 1000 +
tend.tv_nsec / 1000000 - tstart.tv_nsec / 1000000;
printf(" done, %3d.%03d seconds elapsed\n",
elapsedms / 1000, elapsedms % 1000);
rate = (uint64_t)done_bytes * 1000U / elapsedms;
printf(" sent %u packets, %u bytes (%u bytes/s)\n",
done_packets, done_bytes, (unsigned int)rate);
 
/* Restore stdin mode. */
tcsetattr(STDIN_FILENO, TCSANOW, &tcattr);
}
 
 
/* Send time codes. */
void do_send_timecode(void)
{
rtems_status_code ret;
uint8_t timecode;
int value;
 
printf("\n");
value = get_num("Enter timecode value to send (0 .. 63):", 0, 63, -1);
if (value >= 0) {
timecode = spwl_get_timecode(spwh);
printf("Last received timecode value: %u\n", timecode);
printf("Sending timecode value %d\n", value);
ret = spwl_send_timecode(spwh, value);
if (ret != RTEMS_SUCCESSFUL)
fatal_error("spwl_send_timecode", ret);
timecode = spwl_get_timecode(spwh);
printf("Last received timecode value: %u\n", timecode);
rtems_task_wake_after(rtems_clock_get_ticks_per_second() / 10);
timecode = spwl_get_timecode(spwh);
printf("Last received timecode value (after 0.1s): %u\n", timecode);
}
}
 
 
/* Show time codes and link events. */
void do_recv_timecode(void)
{
rtems_status_code ret;
struct termios tcattr;
struct timespec tstart, ts;
spwl_linkstatus status;
int link_is_up;
unsigned int errors;
unsigned int cond;
unsigned int millis;
uint8_t timecode;
int k;
char c;
 
printf("\n---- Show time codes and link events ----\n");
 
rtems_clock_get_uptime(&tstart);
 
spwl_get_linkstatus(spwh, &status, &errors);
link_is_up = (status == SPWL_LINK_RUN);
printf(" link %s, errors = %s%s%s%s%s%s\n",
link_is_up ? "up" : "down",
(errors == 0) ? "none" : "",
(errors & SPWL_ERR_DISCONNECT) ? "disconnect " : "",
(errors & SPWL_ERR_PARITY) ? "parity " : "",
(errors & SPWL_ERR_ESCAPE) ? "escape " : "",
(errors & SPWL_ERR_CREDIT) ? "credit " : "",
(errors & SPWL_ERR_AHB) ? "AHB " : "");
 
timecode = spwl_get_timecode(spwh);
printf(" last timecode = %u\n", timecode);
 
set_nonblocking(STDIN_FILENO, &tcattr);
printf(" waiting for events ... press Enter to stop\n");
 
while (1) {
 
/* Abort test when user hits Enter. */
do {
k = read(STDIN_FILENO, &c, 1);
} while (k == 1 && c != '\n' && c != '\r');
if (k == 1 && (c == '\n' || c == '\r'))
break;
 
/* Wait for event. */
cond = SPWL_COND_TIMECODE;
if (link_is_up)
cond |= SPWL_COND_LINKDOWN;
else
cond |= SPWL_COND_LINKUP;
ret = spwl_wait(spwh, &cond, rtems_clock_get_ticks_per_second());
if (ret != RTEMS_SUCCESSFUL && ret != RTEMS_TIMEOUT)
fatal_error("spwl_wait", ret);
 
/* Report event. */
rtems_clock_get_uptime(&ts);
millis = (ts.tv_sec - tstart.tv_sec) * 1000 +
ts.tv_nsec / 1000000 - tstart.tv_nsec / 1000000;
 
if ((cond & SPWL_COND_LINKUP) != 0) {
printf(" %4u.%03u: link up\n", millis / 1000, millis % 1000);
link_is_up = 1;
}
 
if ((cond & SPWL_COND_LINKDOWN) != 0) {
spwl_get_linkstatus(spwh, &status, &errors);
printf(" %4u.%03u: link down, errors = %s%s%s%s%s%s\n",
millis / 1000, millis % 1000,
(errors == 0) ? "none" : "",
(errors & SPWL_ERR_DISCONNECT) ? "disconnect " : "",
(errors & SPWL_ERR_PARITY) ? "parity " : "",
(errors & SPWL_ERR_ESCAPE) ? "escape " : "",
(errors & SPWL_ERR_CREDIT) ? "credit " : "",
(errors & SPWL_ERR_AHB) ? "AHB " : "");
link_is_up = 0;
}
 
if ((cond & SPWL_COND_TIMECODE) != 0) {
timecode = spwl_get_timecode(spwh);
printf(" %4u.%03u: got tick, timecode = %d\n",
millis / 1000, millis % 1000, timecode);
}
}
 
/* Restore console mode. */
tcsetattr(STDIN_FILENO, TCSANOW, &tcattr);
 
printf(" done\n");
}
 
 
/* Receive/send menu. */
void menu_recvsend(int send)
{
int opt;
int queued, eventloop;
int npacket, packetsize, blocksize;
 
do {
 
printf("\n---- %s packets ----\n", send ? "Send" : "Receive");
show_status();
printf(" 1. Copy API; blocking calls\n");
printf(" 2. Copy API; event loop\n");
printf(" 3. Queue API; blocking calls\n");
printf(" 4. Queue API; event loop\n");
printf(" 0. Back to main menu\n");
 
opt = get_opt(4);
if (opt > 0 && opt <= 4) {
 
queued = (opt == 3 || opt == 4);
eventloop = (opt == 2 || opt == 4);
 
npacket = get_num("Number of packets ?", 0, 1000000, -1);
 
if (send)
packetsize = get_num("Packet size in bytes (1 .. 1000000) ?", 1, 1000000, -1);
else
packetsize = 0;
 
blocksize = 0;
while (send || !queued) {
blocksize = get_num("Block size in bytes (32 .. 16384) ?", 32, MAX_BLOCK_SIZE, 4096);
if ((blocksize & 3) == 0 || !queued)
break;
printf("INVALID: block size must be a multiple of 4 in queued mode\n");
}
 
if (npacket > 0) {
if (send) {
do_data_test(queued, eventloop,
0, 0,
npacket, packetsize, blocksize,
0);
} else {
do_data_test(queued, eventloop,
npacket, blocksize,
0, 0, 0,
0);
}
}
}
} while (opt != 0);
}
 
 
/* Loopback test menu. */
void menu_loopback(int verify)
{
int opt;
int queued, eventloop;
int npacket, packetsize, blocksize;
 
do {
 
printf("\n---- Loopback test %s ----\n",
verify ? "with data compare" : "(no compare)");
show_status();
printf(" 1. Copy API; blocking calls; multi-threaded\n");
printf(" 2. Copy API; event loop\n");
printf(" 3. Queue API; blocking calls; multi-threaded\n");
printf(" 4. Queue API; event loop\n");
printf(" 5. Automatic test\n");
printf(" 0. Back to main menu\n");
 
opt = get_opt(5);
if (opt > 0 && opt <= 4) {
 
queued = (opt == 3 || opt == 4);
eventloop = (opt == 2 || opt == 4);
 
npacket = get_num("Number of packets ?", 0, 1000000, -1);
packetsize = get_num("Packet size in bytes (1 .. 1000000) ?", 1, 1000000, -1);
while (1) {
blocksize = get_num("Block size in bytes (32 .. 16384) ?", 32, MAX_BLOCK_SIZE, 4096);
if ((blocksize & 3) == 0 || !queued)
break;
printf("INVALID: block size must be a multiple of 4 in queued mode\n");
}
 
if (npacket > 0) {
do_data_test(queued, eventloop,
npacket, blocksize,
npacket, packetsize, blocksize,
verify);
}
 
} else if (opt == 5) {
 
do_autotest(verify);
 
}
 
} while (opt != 0);
}
 
 
/* Passive loopback menu. */
void menu_passiveloop(void)
{
int opt;
int queued, blocksize = 0;
 
printf("\n---- Passive loopback mode----\n");
show_status();
printf(" 1. Copy API\n");
printf(" 2. Queue API\n");
printf(" 0. Back to main menu\n");
 
opt = get_opt(2);
if (opt > 0 && opt <= 2) {
 
queued = (opt == 2);
 
while (!queued) {
blocksize = get_num("Block size in bytes (32 .. 16384) ?", 32, MAX_BLOCK_SIZE, 4096);
if ((blocksize & 3) == 0 || !queued)
break;
printf("INVALID: block size must be a multiple of 4 in queued mode\n");
}
 
do_passive_loopback(queued, blocksize);
}
}
 
 
/* Main menu. */
void menu_main(void)
{
int opt;
 
do {
printf("\n==== SpaceWire Light Test ====\n");
show_status();
printf(" 1. Re-initialize driver\n");
printf(" 2. Set link mode\n");
printf(" 3. Set link speed\n");
printf(" 4. Receive packets\n");
printf(" 5. Send packets\n");
printf(" 6. Loopback test (no compare)\n");
printf(" 7. Loopback test with data compare\n");
printf(" 8. Passive loopback\n");
printf(" 9. Send time code\n");
printf("10. Show time codes and link events\n");
printf(" 0. Exit\n");
 
opt = get_opt(10);
switch (opt) {
case 1: do_init_driver(); break;
case 2: do_set_link_mode(); break;
case 3: do_set_link_speed(); break;
case 4: menu_recvsend(0); break;
case 5: menu_recvsend(1); break;
case 6: menu_loopback(0); break;
case 7: menu_loopback(1); break;
case 8: menu_passiveloop(); break;
case 9: do_send_timecode(); break;
case 10: do_recv_timecode(); break;
}
 
} while (opt != 0);
}
 
 
/* Main program. */
void Init(rtems_task_argument arg)
{
int i;
 
printf("\nSpaceWire Light test program for RTEMS\n\n");
 
/* Put stdin in raw mode. */
{
struct termios tcattr;
tcgetattr(STDIN_FILENO, &tcattr);
tcattr.c_iflag &= ~IGNCR;
tcattr.c_lflag &= ~(ICANON | ECHO);
tcattr.c_cc[VMIN] = 1;
tcattr.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &tcattr);
}
 
/* Create TX data pool. */
for (i = 0; i < MAX_BLOCK_SIZE; i++) {
int v = ((i & 1) << 7) | ((i & 2) << 5) |
((i & 4) << 3) | ((i & 8) << 1) |
((i & 16) >> 1) | ((i & 32) >> 3) |
((i & 64) >> 5) | ((i & 128) >> 7);
v += (i >> 8);
txpool[i] = v;
}
memcpy(txpool + MAX_BLOCK_SIZE, txpool, MAX_BLOCK_SIZE);
 
/* Open SpaceWire core */
spw_index = 0;
open_driver();
 
/* Main menu. */
menu_main();
 
/* Clean up. */
spwl_close(spwh);
 
printf("\nExit.\n");
rtems_shutdown_executive(0);
}
 
/* vim: expandtab softtabstop=4
*/
/* end */
/rtems_driver/spacewirelight.c
0,0 → 1,1339
/*
* Copyright 2010 Joris van Rantwijk.
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
 
/**
* @file spacewirelight.c
* @brief SpaceWire Light driver for RTEMS 4.10 on LEON3.
*
* See spacewirelight.h for a description of the API.
*/
 
#include <stdlib.h>
#include <stdint.h>
#include <bsp.h>
#include <rtems/malloc.h>
 
#include "spacewirelight.h"
 
 
/**
* Define SPWL_CHECK_CONCURRENT_CALL to explicitly guard against
* invalid concurrent calls from multiple tasks.
*/
/* #define SPWL_CHECK_CONCURRENT_CALL */
 
/* Tell GCC that type-incompatible pointers may be aliases. */
#define __may_alias __attribute__((may_alias))
 
/* Register addresses, relative to APB base address. */
#define SPWL_REG_CONTROL 0x00
#define SPWL_REG_STATUS 0x04
#define SPWL_REG_TXSCALER 0x08
#define SPWL_REG_TIMECODE 0x0c
#define SPWL_REG_RXDMA 0x10
#define SPWL_REG_TXDMA 0x14
 
/* Bit masks in registers */
#define SPWL_CONTROL_RESET 0x0001
#define SPWL_CONTROL_START 0x0004
#define SPWL_CONTROL_AUTOSTART 0x0008
#define SPWL_CONTROL_DISABLE 0x0010
#define SPWL_CONTROL_RXDMA 0x0040
#define SPWL_CONTROL_TXDMA 0x0080
#define SPWL_CONTROL_IESTATUS 0x0200
#define SPWL_CONTROL_IETICK 0x0400
#define SPWL_CONTROL_IERXDESC 0x0800
#define SPWL_CONTROL_IETXDESC 0x1000
#define SPWL_STATUS_TICK 0x0400
#define SPWL_STATUS_RXDESC 0x0800
#define SPWL_STATUS_TXDESC 0x1000
 
#define SPWL_ERROR_MASK ((SPWL_ERR_DISCONNECT) | (SPWL_ERR_PARITY) | \
(SPWL_ERR_ESCAPE) | (SPWL_ERR_CREDIT))
 
/* Descriptor flag bits */
#define SPWL_DESC_LENMASK 0x0000ffff
#define SPWL_DESC_EN 0x00010000
#define SPWL_DESC_IE 0x00040000
#define SPWL_DESC_DONE 0x00080000
#define SPWL_DESC_EOP 0x00100000
#define SPWL_DESC_EEP 0x00200000
 
/* Convert EOP bits from descriptor flags to library API.
This depends on the specific values of the EOP flags. */
#define SPWL_EOP_DESC_TO_FLAG(f) (((f) & (SPWL_DESC_EOP | SPWL_DESC_EEP)) >> 16)
#define SPWL_EOP_FLAG_TO_DESC(f) (((f) & (SPWL_EOP | SPWL_EEP)) << 16)
 
 
/* Frame descriptor. */
struct descriptor_struct {
volatile uint32_t flags;
volatile uint32_t ptr;
};
 
 
/* Structure describing an open SpaceWire Light device. */
struct spwl_context {
struct spwl_context *next; /* Link to next context */
unsigned long devaddr; /* Base address of APB registers */
unsigned int devirq; /* Device IRQ */
unsigned int ndesc; /* Size of descriptor tables */
unsigned int rxbufs; /* Number of RX buffers */
unsigned int txbufs; /* Number of allocatex TX buffers */
unsigned int rxbufsize; /* Size of each receive buffer */
unsigned int txbufsize; /* Size of each transmit buffer */
volatile struct descriptor_struct *rxdesc; /* RX descriptor table */
volatile struct descriptor_struct *txdesc; /* TX descriptor table */
unsigned char *rxdata; /* RX data buffers */
unsigned char *txdata; /* Internal TX data buffers */
unsigned int rxdescqh, rxdescqlen; /* RX descriptor ring */
unsigned int txdescqh, txdescqlen; /* TX descriptor ring */
unsigned int txdataqh; /* Next internal TX buffer to use */
spwl_txbuf_t *txappqh, *txappqt; /* List of application TX buffers */
unsigned int txappnact; /* Nr of active app buffers */
unsigned int txappnrcl; /* Nr of reclaimable app buffers */
unsigned int currxpos; /* Position in partial RX frame */
unsigned int curtxpos; /* Position in partial TX frame */
unsigned int deftxscaler; /* Default (10 Mbit) TX scaler */
unsigned int pendingerrors; /* Pending error bits */
unsigned int errorcnt; /* Count link error detections */
rtems_id seminterrupt; /* Semaphore to wait for interrupt */
rtems_isr_entry saved_isr; /* Old interrupt handler */
#ifdef SPWL_CHECK_CONCURRENT_CALL
int recvbusy; /* Inside receive function */
int sendbusy; /* Inside send function */
#endif
};
 
 
/* Global linked list of spwl_context structures. */
static struct spwl_context *spwl_context_list = NULL;
 
/* Default options used by spwl_open_xxx() functions. */
static const struct spwl_options spwl_default_options = SPWL_OPTIONS_DEFAULT;
 
 
/*
* == Locking ==
*
* The "spwl_context" structure may be accessed concurrently by one or more
* tasks through the API, as well as by the interrupt handler. The following
* synchronization rules apply:
*
* o Atomic access to hardware registers (i.e. read-modify-write control reg)
* is done with interrupts disabled. The only exception is inside the
* interrupt handler itself.
*
* o Exclusive access to the context structure is ensured by disabling
* interrupts. Also the global linked list of context structures is
* accessed with interrupts disabled.
*
* o During data copying in spwl_recv() and spwl_send(), interrupts are
* restored to the previous setting to avoid keeping interrupts disabled
* for unlimited periods of time.
*
* o A binary semaphore is used to let tasks wait until an interrupt occurs.
* One issue here is to avoid races where the interrupt occurs before
* the task starts waiting. Another issue is that multiple tasks may be
* waiting for the same interrupt, in which case the interrupt should
* wake them all up.
*/
 
 
/* Read from a 32-bit register. */
static inline uint32_t readreg(unsigned long addr)
{
uint32_t ret;
asm volatile (
"lda [%1] 1, %0"
: "=r" (ret)
: "r" (addr) );
return ret;
}
 
 
/* Write to a 32-bit register. */
static inline void writereg(unsigned long addr, uint32_t v)
{
*((volatile uint32_t *)addr) = v;
}
 
 
/* Read a 32-bit word from memory, bypassing the data cache. */
static inline uint32_t readmem_nocache(const volatile uint32_t *addr)
{
uint32_t ret;
asm volatile (
"lda [%1] 1, %0"
: "=r" (ret)
: "r" (addr) );
return ret;
}
 
 
/* Read a byte from memory, bypassing the data cache. */
static inline char readmem_byte_nocache(const volatile char *addr)
{
char ret;
asm volatile (
"lduba [%1] 1, %0"
: "=r" (ret)
: "r" (addr) );
return ret;
}
 
 
/* Write a 32-bit word to memory. */
static inline void writemem(volatile uint32_t *addr, uint32_t v)
{
*addr = v;
}
 
 
/* Copy data, bypassing the CPU data cache. */
static void memcpy_nocache(void *dest, const volatile void *src, size_t n)
{
char __may_alias *cdst = dest;
const volatile char __may_alias *csrc = src;
 
/* Copy word-at-a-time if both pointers are word-aligned. */
if (((((unsigned long)dest) | ((unsigned long)src)) & 3) == 0) {
 
/* Copy words. */
uint32_t __may_alias *wdst = (uint32_t *)dest;
const volatile uint32_t __may_alias *wsrc = (const volatile uint32_t *)src;
while (n >= 4) {
*wdst = readmem_nocache(wsrc);
wdst++;
wsrc++;
n -= 4;
}
 
/* Copy any remaining bytes with the byte-loop below. */
cdst = (char *)wdst;
csrc = (const volatile char *)wsrc;
}
 
/* Copy bytes. */
while (n > 0) {
*cdst = readmem_byte_nocache(csrc);
cdst++;
csrc++;
n--;
}
 
}
 
 
/* Enable bits in the control register. Called with interrupts disabled. */
static inline void spwl_ctrl_setbits(spwl_handle h, uint32_t setbits)
{
uint32_t value;
value = readreg(h->devaddr + SPWL_REG_CONTROL);
value |= setbits;
writereg(h->devaddr + SPWL_REG_CONTROL, value);
}
 
 
/*
* Wait until the interrupt handler releases the specified semaphore.
* Called with interrupts disabled but returns with interrupts enabled.
*/
static rtems_status_code wait_for_interrupt(rtems_id sem,
rtems_interrupt_level level,
rtems_interval timeout)
{
rtems_status_code ret;
 
/*
* The interrupt has been enabled in the SpaceWire core, but interrupts
* are disabled at the CPU level. Therefore nothing can happen until
* we are safely sleeping inside rtems_semaphore_obtain().
*
* Blocking the task with interrupts disabled is apparently handled
* correctly by RTEMS, i.e. the kernel updates the CPU interrupt status
* as part of the context switch.
*/
ret = rtems_semaphore_obtain(sem, RTEMS_WAIT, timeout);
 
/* Restore interrupts. */
rtems_interrupt_enable(level);
 
/* If we got the semaphore, flush it to wake the other waiting tasks.
rtems_semaphore_flush() can be pretty slow, which is why we call it
with interrupts enabled. */
if (ret == RTEMS_SUCCESSFUL)
rtems_semaphore_flush(sem);
 
return ret;
}
 
 
/* Reap (some) completed TX descriptors. Called with interrupts disabled. */
static void reap_tx_descriptors(spwl_handle h)
{
unsigned int txdescqt, txdescqp, nreap;
uint32_t descf;
 
/* Stop if the TX ring is empty. */
if (h->txdescqlen == 0)
return;
 
/* Stop if the tail descriptor in the TX ring is not yet complete. */
txdescqt = (h->txdescqh - h->txdescqlen) & (h->ndesc - 1);
descf = readmem_nocache(&h->txdesc[txdescqt].flags);
if ((descf & SPWL_DESC_DONE) == 0)
return;
 
/* Check if the entire TX ring is complete;
in that case reap everything, otherwise reap just one buffer. */
txdescqp = (h->txdescqh - 1) & (h->ndesc - 1);
descf = readmem_nocache(&h->txdesc[txdescqp].flags);
if ((descf & SPWL_DESC_DONE) != 0)
nreap = h->txdescqlen;
else
nreap = 1;
 
/* Remove reaped buffers from TX ring. */
h->txdescqlen -= nreap;
 
/* If the reaped buffers are application buffers, move them to the
list of reclaimable buffers. */
if (h->txappnact > 0) {
h->txappnact -= nreap;
h->txappnrcl += nreap;
}
}
 
 
/*
* Interrupt handler.
*
* The interrupt handler does not do any data handling itself.
* It simple releases a semaphore to wake up tasks that do the actual work.
*/
static void spwl_interrupt_handler(rtems_vector_number vec)
{
struct spwl_context *ctx;
uint32_t ctrl;
 
/* Scan list of device contexts for a matching IRQ vector. */
for (ctx = spwl_context_list; ctx != NULL; ctx = ctx->next) {
if (ctx->devirq + 0x10 == vec) {
 
/* Disable device interrupts. */
ctrl = readreg(ctx->devaddr + SPWL_REG_CONTROL);
ctrl &= ~ (SPWL_CONTROL_IERXDESC | SPWL_CONTROL_IETXDESC |
SPWL_CONTROL_IETICK | SPWL_CONTROL_IESTATUS);
writereg(ctx->devaddr + SPWL_REG_CONTROL, ctrl);
 
/* Notify waiting tasks. */
rtems_semaphore_release(ctx->seminterrupt);
}
}
}
 
 
#ifdef LEON3
/* Open a SpaceWire Light device. */
rtems_status_code spwl_open(spwl_handle *h,
unsigned int index,
const struct spwl_options *opt)
{
amba_apb_device apbdev;
 
/* Find device in APB plug&play configuration. */
if (!amba_find_next_apbslv(&amba_conf,
VENDOR_OPENCORES, DEVICE_SPACEWIRELIGHT,
&apbdev, index)) {
return RTEMS_INVALID_NUMBER;
}
 
return spwl_open_hwaddr(h, apbdev.start, apbdev.irq, opt);
}
#endif
 
 
/* Open a SpaceWire Light device. */
rtems_status_code spwl_open_hwaddr(spwl_handle *h,
unsigned long addr, unsigned int irq,
const struct spwl_options *opt)
{
struct spwl_context *ctx;
uint32_t t, desctablesize;
rtems_status_code ret;
rtems_interrupt_level level;
unsigned int i;
void *vp;
 
/* Use default options if no options specified. */
if (opt == NULL) {
opt = &spwl_default_options;
}
 
/* Read configuration of SpaceWire Light core. */
t = readreg(addr + SPWL_REG_CONTROL);
desctablesize = (t >> 24);
if (desctablesize < 4 || desctablesize > 14) {
ret = RTEMS_IO_ERROR;
goto errout;
}
 
if (opt->rxbufsize < 32 || opt->rxbufsize > 65532 ||
opt->txbufsize < 32 || opt->txbufsize > 65532) {
ret = RTEMS_INVALID_SIZE;
goto errout;
}
 
/* Allocate context structure. */
ctx = malloc(sizeof(struct spwl_context));
if (ctx == NULL) {
ret = RTEMS_NO_MEMORY;
goto errout;
}
 
/* Initialize context structure. */
ctx->devaddr = addr;
ctx->devirq = irq;
ctx->ndesc = 1 << desctablesize;
ctx->rxbufs = opt->rxbufs;
ctx->txbufs = opt->txbufs;
ctx->rxbufsize = (opt->rxbufsize + 3) & (~3U);
ctx->txbufsize = (opt->txbufsize + 3) & (~3U);
ctx->rxdescqh = ctx->rxdescqlen = 0;
ctx->txdescqh = ctx->txdescqlen = 0;
ctx->txdataqh = 0;
ctx->txappqt = ctx->txappqh = NULL;
ctx->txappnact = 0;
ctx->txappnrcl = 0;
ctx->currxpos = 0;
ctx->curtxpos = 0;
#ifdef SPWL_CHECK_CONCURRENT_CALL
ctx->recvbusy = 0;
ctx->sendbusy = 0;
#endif
ctx->pendingerrors = 0;
ctx->errorcnt = 0;
 
/* Do not allocate more buffers than the size of the descriptor table. */
if (ctx->rxbufs > ctx->ndesc)
ctx->rxbufs = ctx->ndesc;
if (ctx->txbufs > ctx->ndesc)
ctx->txbufs = ctx->ndesc;
 
/* Allocate RX/TX descriptor tables. */
if (rtems_memalign(&vp, 8 * ctx->ndesc, 2 * 8 * ctx->ndesc)) {
ret = RTEMS_NO_MEMORY;
goto errout_desc;
}
ctx->rxdesc = ((struct descriptor_struct *)vp);
ctx->txdesc = ((struct descriptor_struct *)vp) + ctx->ndesc;
 
/* Allocate RX/TX data buffers. */
if (rtems_memalign(&vp, 32, ctx->rxbufs * ctx->rxbufsize)) {
ret = RTEMS_NO_MEMORY;
goto errout_rxdata;
}
ctx->rxdata = vp;
if (rtems_memalign(&vp, 32, ctx->txbufs * ctx->txbufsize)) {
ret = RTEMS_NO_MEMORY;
goto errout_txdata;
}
ctx->txdata = vp;
 
/* Initialize semaphore. */
ret = rtems_semaphore_create(
rtems_build_name('S','P','W','L'),
0,
RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE,
RTEMS_NO_PRIORITY,
&ctx->seminterrupt);
if (ret != RTEMS_SUCCESSFUL)
goto errout_sem;
 
/* Clear descriptor tables. */
for (i = 0; i < ctx->ndesc; i++) {
writemem(&ctx->rxdesc[i].flags, 0);
writemem(&ctx->txdesc[i].flags, 0);
}
 
/* Fill RX descriptor table. */
for (i = 0; i < ctx->rxbufs; i++) {
unsigned char *pbuf = ctx->rxdata + i * ctx->rxbufsize;
writemem(&ctx->rxdesc[i].ptr, (uint32_t)pbuf);
writemem(&ctx->rxdesc[i].flags,
ctx->rxbufsize | SPWL_DESC_EN | SPWL_DESC_IE);
}
ctx->rxdescqh = ctx->rxbufs & (ctx->ndesc - 1);
ctx->rxdescqlen = ctx->rxbufs;
 
/* Reset device. */
writereg(ctx->devaddr + SPWL_REG_CONTROL, SPWL_CONTROL_RESET);
 
/* Store initial TX scaler. */
ctx->deftxscaler = readreg(ctx->devaddr + SPWL_REG_TXSCALER);
 
/* Add context structure to linked list. */
rtems_interrupt_disable(level);
ctx->next = spwl_context_list;
spwl_context_list = ctx;
rtems_interrupt_enable(level);
 
/* Register interrupt handler. */
rtems_interrupt_catch(spwl_interrupt_handler, ctx->devirq + 0x10,
&ctx->saved_isr);
LEON_Clear_interrupt(ctx->devirq);
LEON_Unmask_interrupt(ctx->devirq);
 
/* Initialize descriptor pointers. */
writereg(ctx->devaddr + SPWL_REG_RXDMA, (uint32_t)(ctx->rxdesc));
writereg(ctx->devaddr + SPWL_REG_TXDMA, (uint32_t)(ctx->txdesc));
 
/* Start RX DMA. */
writereg(ctx->devaddr + SPWL_REG_CONTROL, SPWL_CONTROL_RXDMA);
 
*h = ctx;
return RTEMS_SUCCESSFUL;
 
/* Cleanup after error. */
errout_sem:
free(ctx->txdata);
errout_txdata:
free(ctx->rxdata);
errout_rxdata:
free((void*)ctx->rxdesc);
errout_desc:
free(ctx);
errout:
return ret;
}
 
 
/* Close an open SpaceWire Light device. */
void spwl_close(spwl_handle h)
{
struct spwl_context **ctxp;
rtems_interrupt_level level;
 
/* Reset device. */
writereg(h->devaddr + SPWL_REG_CONTROL, SPWL_CONTROL_RESET);
 
/* Unregister interrupt handler.
NOTE: This is incorrect in case of shared interrupts. */
LEON_Mask_interrupt(h->devirq);
LEON_Clear_interrupt(h->devirq);
rtems_interrupt_catch(h->saved_isr, h->devirq + 0x10, &h->saved_isr);
 
/* Unlink context structure. */
rtems_interrupt_disable(level);
ctxp = &spwl_context_list;
for (ctxp = &spwl_context_list; *ctxp != NULL; ctxp = &(*ctxp)->next) {
if (*ctxp == h) {
*ctxp = h->next;
break;
}
}
rtems_interrupt_enable(level);
 
/* Delete semaphore. */
rtems_semaphore_delete(h->seminterrupt);
 
/* Release memory. */
free(h->txdata);
free(h->rxdata);
free((void*)h->rxdesc);
free(h);
}
 
 
/* Set the TX clock scaler for the link. */
rtems_status_code spwl_set_linkspeed(spwl_handle h, unsigned int scaler)
{
writereg(h->devaddr + SPWL_REG_TXSCALER, scaler);
return RTEMS_SUCCESSFUL;
}
 
 
/* Return the currently configured TX clock scaler. */
unsigned int spwl_get_linkspeed(spwl_handle h)
{
return readreg(h->devaddr + SPWL_REG_TXSCALER);
}
 
 
/* Return the default TX scaler value. */
unsigned int spwl_get_default_linkspeed(spwl_handle h)
{
return h->deftxscaler;
}
 
 
/* Change the mode of the SpaceWire link. */
rtems_status_code spwl_set_linkmode(spwl_handle h, spwl_linkmode mode)
{
rtems_interrupt_level level;
uint32_t ctrl, m;
 
/* Convert link mode to bits in control register. */
switch (mode) {
case SPWL_LINKMODE_START:
m = SPWL_CONTROL_START;
break;
case SPWL_LINKMODE_AUTOSTART:
m = SPWL_CONTROL_AUTOSTART;
break;
case SPWL_LINKMODE_DISABLE:
m = SPWL_CONTROL_DISABLE;
break;
default:
m = 0;
break;
}
 
/* Update control register. */
rtems_interrupt_disable(level);
ctrl = readreg(h->devaddr + SPWL_REG_CONTROL);
ctrl &= ~ (SPWL_CONTROL_START |
SPWL_CONTROL_AUTOSTART |
SPWL_CONTROL_DISABLE);
ctrl |= m;
writereg(h->devaddr + SPWL_REG_CONTROL, ctrl);
rtems_interrupt_enable(level);
 
return RTEMS_SUCCESSFUL;
}
 
 
/* Get status and pending errors of SpaceWire link. */
rtems_status_code spwl_get_linkstatus(spwl_handle h,
spwl_linkstatus *linkstatus,
unsigned int *errors)
{
rtems_interrupt_level level;
uint32_t status;
 
rtems_interrupt_disable(level);
 
/* Read status word and clear error flags. */
status = readreg(h->devaddr + SPWL_REG_STATUS);
writereg(h->devaddr + SPWL_REG_STATUS, status & SPWL_ERROR_MASK);
 
/* Update error counter (needed in case spwl_wait() is in progress). */
if ((status & SPWL_ERROR_MASK) != 0)
h->errorcnt++;
 
/* Accumulate error flags. */
h->pendingerrors |= status;
 
/* Clear pending errors if error status is requested. */
if (errors) {
status |= h->pendingerrors & SPWL_ERROR_MASK;
h->pendingerrors = 0;
}
 
rtems_interrupt_enable(level);
 
if (linkstatus)
*linkstatus = status & 3;
if (errors)
*errors = status & (SPWL_ERROR_MASK | SPWL_ERR_AHB);
 
return RTEMS_SUCCESSFUL;
}
 
 
/* Wait for specified condition with timeout. */
rtems_status_code spwl_wait(spwl_handle h,
unsigned int *cond, rtems_interval timeout)
{
rtems_status_code ret;
rtems_interrupt_level level;
unsigned int i_cond = *cond;
unsigned int r_cond = 0;
unsigned int rxdescqt;
unsigned int prev_errorcnt = 0;
rtems_interval endtime = RTEMS_NO_TIMEOUT;
rtems_interval timeleft = timeout;
uint32_t status, ctrl, descf;
int first = 1;
 
/* Determine maximum wait time. */
if (timeout != RTEMS_NO_TIMEOUT)
endtime = rtems_clock_get_ticks_since_boot() + timeout;
 
/* Wait until condition satisfied or timeout. */
do {
 
/* Disable global interrupts. */
rtems_interrupt_disable(level);
 
/* Store initial link error count to detect upcoming link events. */
if (first)
prev_errorcnt = h->errorcnt;
first = 0;
 
/* Enable relevant device interrupts. */
ctrl = readreg(h->devaddr + SPWL_REG_CONTROL);
if ((i_cond & SPWL_COND_RDYRECV) != 0)
ctrl |= SPWL_CONTROL_IERXDESC;
if ((i_cond & (SPWL_COND_RDYSEND |
SPWL_COND_RDYSENDBUF |
SPWL_COND_RECLAIM)) != 0)
ctrl |= SPWL_CONTROL_IETXDESC;
if ((i_cond & SPWL_COND_TIMECODE) != 0)
ctrl |= SPWL_CONTROL_IETICK;
if ((i_cond & (SPWL_COND_LINKUP | SPWL_COND_LINKDOWN)) != 0)
ctrl |= SPWL_CONTROL_IESTATUS;
writereg(h->devaddr + SPWL_REG_CONTROL, ctrl);
 
/* Read status register and clear error flags. */
status = readreg(h->devaddr + SPWL_REG_STATUS);
writereg(h->devaddr + SPWL_REG_STATUS, status & SPWL_ERROR_MASK);
 
/* Update error counter. */
if ((status & SPWL_ERROR_MASK) != 0)
h->errorcnt++;
 
/* Accumulate error flags for spwl_get_linkstatus(). */
h->pendingerrors |= status;
 
/* Check for link up condition. */
if ((i_cond & SPWL_COND_LINKUP) != 0 &&
((status & 3) == 3 || h->errorcnt > prev_errorcnt)) {
/* Either the link is currently up, or a link error occurred
since entering spwl_wait(), indicating that the link has
been up even if it is already down again. */
r_cond |= SPWL_COND_LINKUP;
}
 
/* Check for link down condition. */
if ((i_cond & SPWL_COND_LINKDOWN) != 0 &&
((status & 3) != 3 || h->errorcnt > prev_errorcnt)) {
/* Either the link is currently down, or a link error occured
since entering spwl_wait(), indicating that the link has
been down even if it is already up again. */
r_cond |= SPWL_COND_LINKDOWN;
}
 
/* Check receive condition. */
if ((i_cond & SPWL_COND_RDYRECV) != 0) {
/* Check for received data in RX ring. */
rxdescqt = (h->rxdescqh - h->rxdescqlen) & (h->ndesc - 1);
descf = readmem_nocache(&h->rxdesc[rxdescqt].flags);
if ((descf & SPWL_DESC_DONE) != 0)
r_cond |= SPWL_COND_RDYRECV;
}
 
/* Check send/reclaim conditions. */
if ((i_cond & (SPWL_COND_RDYSEND |
SPWL_COND_RDYSENDBUF |
SPWL_COND_RECLAIM)) != 0) {
 
/* Reap completed TX descriptors. */
reap_tx_descriptors(h);
 
/* Check for room in TX ring and room in TX internal buffers
and no application buffers in TX ring. */
if ((i_cond & SPWL_COND_RDYSEND) != 0 &&
h->txdescqlen < h->ndesc &&
h->txdescqlen < h->txbufs &&
h->txappnact == 0)
r_cond |= SPWL_COND_RDYSEND;
 
/* Check for room in TX ring and no internal buffers in TX ring. */
if ((i_cond & SPWL_COND_RDYSENDBUF) != 0 &&
h->txdescqlen < h->ndesc &&
(h->txdescqlen == 0 || h->txappnact > 0))
r_cond |= SPWL_COND_RDYSENDBUF;
 
/* Check for non-empty reclaim list. */
if ((i_cond & SPWL_COND_RECLAIM) != 0 &&
h->txappnrcl > 0)
r_cond |= SPWL_COND_RECLAIM;
}
 
/* Check for received time code. */
if ((i_cond & SPWL_COND_TIMECODE) != 0 &&
(status & SPWL_STATUS_TICK) != 0) {
/* There is a pending timecode. */
r_cond |= SPWL_COND_TIMECODE;
}
 
/* Stop waiting if any of the conditions has been satisfied. */
if (r_cond != 0) {
rtems_interrupt_enable(level);
ret = RTEMS_SUCCESSFUL;
break;
}
 
/* Wait for interrupt (returns with interrupts enabled). */
ret = wait_for_interrupt(h->seminterrupt, level, timeleft);
 
/* Recalculate the time left to wait. */
if (timeout != RTEMS_NO_TIMEOUT) {
rtems_interval tnow = rtems_clock_get_ticks_since_boot();
if (tnow >= endtime) {
ret = RTEMS_TIMEOUT;
break;
}
timeleft = endtime - tnow;
}
 
/* Stop if the interrupt timed out. */
} while (ret != RTEMS_TIMEOUT);
 
/* Return */
*cond = r_cond;
return ret;
}
 
 
/* Transfer received data to the specified application buffer. */
rtems_status_code spwl_recv(spwl_handle h,
void *buf, size_t maxlen, size_t *ntrans,
unsigned int *eop, unsigned int flags)
{
rtems_status_code ret;
rtems_interrupt_level level;
size_t r_ntrans = 0;
unsigned int r_eop = 0;
unsigned int rxdescqt;
uint32_t descf, descp, framelen, ncopy;
 
/* Disable interrupts. */
rtems_interrupt_disable(level);
 
#ifdef SPWL_CHECK_CONCURRENT_CALL
/* Limit damage in case of concurrent calls. */
if (h->recvbusy) {
rtems_interrupt_enable(level);
return RTEMS_RESOURCE_IN_USE;
}
h->recvbusy = 1;
#endif
 
/* Transfer data until request satisfied. */
while (1) {
 
/* Transfer data until request satisfied or no more data available. */
while (r_ntrans < maxlen && r_eop == 0) {
 
/* Check that the RX ring is nonempty. */
if (h->rxdescqlen == 0)
break;
 
/* Check that the frame at the tail of the RX ring is ready. */
rxdescqt = (h->rxdescqh - h->rxdescqlen) & (h->ndesc - 1);
descf = readmem_nocache(&h->rxdesc[rxdescqt].flags);
descp = h->rxdesc[rxdescqt].ptr;
if ((descf & SPWL_DESC_DONE) == 0) {
/* No more received frames available. */
break;
}
 
/* Re-enable interrupts during copying. */
rtems_interrupt_enable(level);
 
/* Copy data from current frame to application buffer. */
framelen = descf & SPWL_DESC_LENMASK;
ncopy = framelen - h->currxpos;
if (ncopy > maxlen - r_ntrans)
ncopy = maxlen - r_ntrans;
memcpy_nocache((unsigned char *)buf + r_ntrans,
(unsigned char *)descp + h->currxpos,
ncopy);
r_ntrans += ncopy;
h->currxpos += ncopy;
 
/* Re-disable interrupts. */
rtems_interrupt_disable(level);
 
/* Handle end of frame. */
if (h->currxpos >= framelen) {
 
/* Pass EOP flags to application. */
r_eop = SPWL_EOP_DESC_TO_FLAG(descf);
 
/* Reset partial frame position. */
h->currxpos = 0;
 
/* Resubmit buffer to head of RX ring. */
writemem(&h->rxdesc[h->rxdescqh].ptr, descp);
writemem(&h->rxdesc[h->rxdescqh].flags,
h->rxbufsize | SPWL_DESC_EN | SPWL_DESC_IE);
h->rxdescqh = (h->rxdescqh + 1) & (h->ndesc - 1);
 
/* Restart RX DMA. */
spwl_ctrl_setbits(h, SPWL_CONTROL_RXDMA);
}
}
 
/* Stop if request satisfied. */
if (r_ntrans == maxlen || r_eop != 0) {
ret = RTEMS_SUCCESSFUL;
break;
}
 
/* No more received frames available.
Stop if application does not want to wait. */
if ((flags & SPWL_NO_WAIT) != 0) {
ret = (r_ntrans > 0) ? RTEMS_SUCCESSFUL : RTEMS_UNSATISFIED;
break;
}
 
/* Enable interrupt on data received. */
spwl_ctrl_setbits(h, SPWL_CONTROL_IERXDESC);
 
/* Final check for received data (avoid race condition). */
rxdescqt = (h->rxdescqh - h->rxdescqlen) & (h->ndesc - 1);
if (h->rxdescqlen == 0 ||
(readmem_nocache(&h->rxdesc[rxdescqt].flags) & SPWL_DESC_DONE) == 0) {
/* Wait until RX interrupt. */
wait_for_interrupt(h->seminterrupt, level, RTEMS_NO_TIMEOUT);
rtems_interrupt_disable(level);
}
}
 
/* Restore interrupts. */
#ifdef SPWL_CHECK_CONCURRENT_CALL
h->recvbusy = 0;
#endif
rtems_interrupt_enable(level);
 
/* Return */
*ntrans = r_ntrans;
*eop = r_eop;
return ret;
}
 
 
/* Send data to the SpaceWire link. */
rtems_status_code spwl_send(spwl_handle h,
const void *buf, size_t maxlen, size_t *ntrans,
unsigned int flags)
{
rtems_status_code ret;
rtems_interrupt_level level;
size_t r_ntrans = 0;
unsigned char * bufp;
unsigned int txdescqp;
uint32_t descf, ncopy;
 
/* Disable interrupts. */
rtems_interrupt_disable(level);
 
#ifdef SPWL_CHECK_CONCURRENT_CALL
/* Limit damage in case of concurrent calls. */
if (h->sendbusy) {
rtems_interrupt_enable(level);
return RTEMS_RESOURCE_IN_USE;
}
h->sendbusy = 1;
#endif
 
/* Transfer data until request satisfied. */
while (1) {
 
/* Reap completed TX descriptors if possible. */
reap_tx_descriptors(h);
 
/* Transfer data until request satisfied or no more room in TX bufs. */
do {
 
/* Check that there is a buffer available and that
there are no application buffers in the TX ring. */
if (h->txdescqlen >= h->ndesc ||
h->txdescqlen >= h->txbufs ||
h->txappnact > 0)
break;
 
/* Re-enable interrupts during copying. */
rtems_interrupt_enable(level);
 
/* Copy data from application buffer to internal TX buffer. */
bufp = h->txdata + h->txdataqh * h->txbufsize;
ncopy = h->txbufsize - h->curtxpos;
if (ncopy > maxlen - r_ntrans)
ncopy = maxlen - r_ntrans;
memcpy(bufp + h->curtxpos, (unsigned char *)buf + r_ntrans, ncopy);
r_ntrans += ncopy;
h->curtxpos += ncopy;
 
/* Re-disable interrupts. */
rtems_interrupt_disable(level);
 
/* Handle end of frame. */
if (h->curtxpos >= h->txbufsize ||
(flags & (SPWL_EOP | SPWL_EEP)) != 0) {
 
/* Insert buffer in TX descriptor ring. */
descf = h->curtxpos | SPWL_DESC_EN | SPWL_DESC_IE;
if (r_ntrans == maxlen) {
/* Handle EOP. */
descf |= SPWL_EOP_FLAG_TO_DESC(flags);
flags &= ~(SPWL_EOP | SPWL_EEP);
}
writemem(&h->txdesc[h->txdescqh].ptr, (uint32_t)bufp);
writemem(&h->txdesc[h->txdescqh].flags, descf);
h->txdescqh = (h->txdescqh + 1) & (h->ndesc - 1);
h->txdescqlen++;
 
/* Advance internal TX buffer pointer. */
h->curtxpos = 0;
h->txdataqh++;
if (h->txdataqh == h->txbufs)
h->txdataqh = 0;
 
/* Restart TX DMA. */
spwl_ctrl_setbits(h, SPWL_CONTROL_TXDMA);
}
 
} while (r_ntrans < maxlen);
 
/* Stop when request satisfied. */
if (r_ntrans == maxlen && (flags & (SPWL_EOP | SPWL_EEP)) == 0) {
ret = RTEMS_SUCCESSFUL;
break;
}
 
/* No more room in TX queue, but application wants to send more.
Stop if application does not want to wait. */
if ((flags & SPWL_NO_WAIT) != 0) {
ret = (r_ntrans > 0) ? RTEMS_SUCCESSFUL : RTEMS_UNSATISFIED;
break;
}
 
/* Enable interrupt on frame transmitted */
spwl_ctrl_setbits(h, SPWL_CONTROL_IETXDESC);
 
/* Final check for TX room (avoid race condition). */
if (h->txappnact > 0) {
/* Wait until all app buffers can be removed from the TX ring. */
txdescqp = (h->txdescqh - 1) & (h->ndesc - 1);
} else {
/* Wait until one buffer can be removed from the TX ring. */
txdescqp = (h->txdescqh - h->txdescqlen) & (h->ndesc - 1);
}
descf = readmem_nocache(&h->txdesc[txdescqp].flags);
if ((descf & SPWL_DESC_DONE) == 0) {
/* Wait until TX interrupt. */
wait_for_interrupt(h->seminterrupt, level, RTEMS_NO_TIMEOUT);
rtems_interrupt_disable(level);
}
}
 
/* Restore interrupts. */
#ifdef SPWL_CHECK_CONCURRENT_CALL
h->sendbusy = 0;
#endif
rtems_interrupt_enable(level);
 
/* Return */
*ntrans = r_ntrans;
return ret;
}
 
 
/* Receive data from the SpaceWire link without copying. */
rtems_status_code spwl_recv_rxbuf(spwl_handle h,
void **buf,
uint16_t *nbytes, unsigned int *eop,
unsigned int flags)
{
rtems_status_code ret;
rtems_interrupt_level level;
unsigned int rxdescqt;
uint32_t descf;
void *r_buf = NULL;
uint16_t r_nbytes = 0;
unsigned int r_eop = 0;
 
/* Disable interrupts. */
rtems_interrupt_disable(level);
 
#ifdef SPWL_CHECK_CONCURRENT_CALL
/* Limit damage in case of concurrent calls. */
if (h->recvbusy) {
rtems_interrupt_enable(level);
return RTEMS_RESOURCE_IN_USE;
}
h->recvbusy = 1;
#endif
 
/* Make sure there is received data available. */
while (1) {
 
/* Determine tail of RX ring. */
rxdescqt = (h->rxdescqh - h->rxdescqlen) & (h->ndesc - 1);
 
/* Check if there is at least one received frame available. */
if (h->rxdescqlen > 0) {
descf = readmem_nocache(&h->rxdesc[rxdescqt].flags);
if ((descf & SPWL_DESC_DONE) != 0)
break;
}
 
/* There is no data available.
Stop if the application does not want to wait. */
if ((flags & SPWL_NO_WAIT) != 0) {
ret = RTEMS_UNSATISFIED;
goto out_unlock;
}
 
/* Enable interrupt on data received. */
spwl_ctrl_setbits(h, SPWL_CONTROL_IERXDESC);
 
/* Final check for received data (avoid race condition). */
if (h->rxdescqlen > 0) {
descf = readmem_nocache(&h->rxdesc[rxdescqt].flags);
if ((descf & SPWL_DESC_DONE) != 0)
break;
}
 
/* Wait until RX interrupt. */
wait_for_interrupt(h->seminterrupt, level, RTEMS_NO_TIMEOUT);
rtems_interrupt_disable(level);
}
 
/* At least one received frame is available.
Remove buffer from RX ring and give it to the application. */
r_buf = (void *)(h->rxdesc[rxdescqt].ptr);
r_nbytes = descf & SPWL_DESC_LENMASK;
r_eop = SPWL_EOP_DESC_TO_FLAG(descf);
h->rxdescqlen--;
 
/* Reset partial frame position.
Mixing calls to spwl_recv() and spwl_recv_rxbuf() is not supported. */
h->currxpos = 0;
 
ret = RTEMS_SUCCESSFUL;
 
out_unlock:
/* Restore interrupts. */
#ifdef SPWL_CHECK_CONCURRENT_CALL
h->recvbusy = 0;
#endif
rtems_interrupt_enable(level);
 
*buf = r_buf;
*nbytes = r_nbytes;
*eop = r_eop;
return ret;
}
 
 
/* Release receive buffers back to the driver. */
rtems_status_code spwl_release_rxbuf(spwl_handle h, void *buf)
{
rtems_interrupt_level level;
 
/* Disable interrupts. */
rtems_interrupt_disable(level);
 
/* Insert buffer at head of RX ring. */
writemem(&h->rxdesc[h->rxdescqh].ptr, (uint32_t)buf);
writemem(&h->rxdesc[h->rxdescqh].flags,
h->rxbufsize | SPWL_DESC_EN | SPWL_DESC_IE);
h->rxdescqh = (h->rxdescqh + 1) & (h->ndesc - 1);
h->rxdescqlen++;
 
/* Restart RX DMA. */
spwl_ctrl_setbits(h, SPWL_CONTROL_RXDMA);
 
/* Restore interrupts. */
rtems_interrupt_enable(level);
 
return RTEMS_SUCCESSFUL;
}
 
 
/* Submit data for transmission to the SpaceWire link without copying. */
rtems_status_code spwl_send_txbuf(spwl_handle h,
struct spwl_txbuf *buf, unsigned int flags)
{
rtems_status_code ret;
rtems_interrupt_level level;
unsigned int txdescqp;
uint32_t descf;
 
/* Disable interrupts. */
rtems_interrupt_disable(level);
 
#ifdef SPWL_CHECK_CONCURRENT_CALL
/* Limit damage in case of concurrent calls. */
if (h->sendbusy) {
rtems_interrupt_enable(level);
return RTEMS_RESOURCE_IN_USE;
}
h->sendbusy = 1;
#endif
 
/* Make sure there is room in the TX ring. */
while (1) {
 
/* Reap completed TX descriptors if possible. */
reap_tx_descriptors(h);
 
if (h->txdescqlen > 0 && h->txappnact == 0) {
/* Internal buffers in the TX ring; wait until they are gone. */
txdescqp = (h->txdescqh - 1) & (h->ndesc - 1);
} else if (h->txdescqlen >= h->ndesc) {
/* TX ring is full; wait until at least one buffer is gone. */
txdescqp = (h->txdescqh - h->txdescqlen) & (h->ndesc - 1);
} else {
/* Good to go. */
break;
}
 
/* There is currently no room.
Stop if the application does not want to wait. */
if ((flags & SPWL_NO_WAIT) != 0) {
ret = RTEMS_UNSATISFIED;
goto out_unlock;
}
 
/* Enable interrupt on data transmitted. */
spwl_ctrl_setbits(h, SPWL_CONTROL_IETXDESC);
 
/* Final check for completed TX descriptor (avoid race condition). */
descf = readmem_nocache(&h->txdesc[txdescqp].flags);
if ((descf & SPWL_DESC_DONE) == 0) {
/* Wait until TX interrupt. */
wait_for_interrupt(h->seminterrupt, level, RTEMS_NO_TIMEOUT);
rtems_interrupt_disable(level);
}
}
 
/* There is room for at least one frame.
Insert buffer at head of application-buffer list. */
buf->next = NULL;
if (h->txappqh != NULL)
h->txappqh->next = buf;
else
h->txappqt = buf;
h->txappqh = buf;
h->txappnact++;
 
/* Insert buffer at head of TX descriptor ring. */
descf = buf->nbytes |
SPWL_EOP_FLAG_TO_DESC(buf->eop) | SPWL_DESC_EN | SPWL_DESC_IE;
writemem(&h->txdesc[h->txdescqh].ptr, (uint32_t)(buf->data));
writemem(&h->txdesc[h->txdescqh].flags, descf);
h->txdescqh = (h->txdescqh + 1) & (h->ndesc - 1);
h->txdescqlen++;
 
/* Restart TX DMA. */
spwl_ctrl_setbits(h, SPWL_CONTROL_TXDMA);
 
ret = RTEMS_SUCCESSFUL;
 
out_unlock:
/* Restore interrupts. */
#ifdef SPWL_CHECK_CONCURRENT_CALL
h->sendbusy = 0;
#endif
rtems_interrupt_enable(level);
 
return ret;
}
 
 
/* Reclaim transmit buffers after completion of transmission. */
rtems_status_code spwl_reclaim_txbuf(spwl_handle h,
struct spwl_txbuf **buf, unsigned flags)
{
rtems_status_code ret;
rtems_interrupt_level level;
struct spwl_txbuf *r_buf = NULL;
unsigned int txdescqt;
uint32_t descf;
 
/* Disable interrupts. */
rtems_interrupt_disable(level);
 
/* Make sure the reclaim list is not empty. */
while (1) {
 
/* Reap completed TX descriptors if possible. */
reap_tx_descriptors(h);
 
/* Check that the reclaim list is non-empty. */
if (h->txappnrcl > 0)
break;
 
/* No buffers ready to reclaim.
Stop if the application does not want to wait. */
if ((flags & SPWL_NO_WAIT) != 0) {
ret = RTEMS_UNSATISFIED;
goto out_unlock;
}
 
/* Enable interrupt on data transmitted. */
spwl_ctrl_setbits(h, SPWL_CONTROL_IETXDESC);
 
/* Final check for completed TX descriptors (avoid race condition). */
if (h->txappnact > 0) {
/* There are application buffers in the TX ring.
Maybe one has completed in the mean time. */
txdescqt = (h->txdescqh - h->txdescqlen) & (h->ndesc - 1);
descf = readmem_nocache(&h->txdesc[txdescqt].flags);
if ((descf & SPWL_DESC_DONE) != 0)
continue;
}
 
/* Wait until TX interrupt. */
wait_for_interrupt(h->seminterrupt, level, RTEMS_NO_TIMEOUT);
rtems_interrupt_disable(level);
}
 
/* The reclaim list is non-empty.
Pass one reclaimable buffer to the application. */
r_buf = h->txappqt;
h->txappqt = h->txappqt->next;
if (h->txappqt == NULL)
h->txappqh = NULL;
h->txappnrcl--;
r_buf->next = NULL;
 
ret = RTEMS_SUCCESSFUL;
 
out_unlock:
/* Restore interrupts. */
rtems_interrupt_enable(level);
 
/* Return */
*buf = r_buf;
return ret;
}
 
 
/* Return last received timecode. */
uint8_t spwl_get_timecode(spwl_handle h)
{
uint32_t v;
 
/* Clear "tick" bit in status register. */
writereg(h->devaddr + SPWL_REG_STATUS, SPWL_STATUS_TICK);
 
/* Read last received timecode. */
v = readreg(h->devaddr + SPWL_REG_TIMECODE);
return v & 0xff;
}
 
 
/* Send a timecode to the SpaceWire link. */
rtems_status_code spwl_send_timecode(spwl_handle h, uint8_t timecode)
{
writereg(h->devaddr + SPWL_REG_TIMECODE, 0x10000 | (timecode << 8));
return RTEMS_SUCCESSFUL;
}
/* vim: expandtab softtabstop=4
*/
/* end */
/rtems_driver/Makefile
0,0 → 1,29
#
# Build SpaceWire Light driver and test program for RTEMS on LEON3.
#
# A different configuration of this test program is used in a simulation.
# In that case the program is built by sim/spwamba_leon3/Makefile.
#
 
TARGETDIR = /opt/rtems-4.10/sparc-rtems4.10/leon3
GCCSPECS = -B$(TARGETDIR)/lib/ -specs bsp_specs -qrtems
CC = sparc-rtems4.10-gcc
CFLAGS = $(GCCSPECS) -msoft-float -Wall -Os
LDFLAGS = $(GCCSPECS) -msoft-float
 
.PHONY: all
all: spwltest.dsu
 
spwltest.dsu: spwltest.o spacewirelight.o
$(CC) $(LDFLAGS) $^ -o $@
 
spwltest.o: spwltest.c spacewirelight.h
$(CC) $(CFLAGS) -c $<
 
spacewirelight.o: spacewirelight.c spacewirelight.h
$(CC) $(CFLAGS) -c $<
 
.PHONY: clean
clean:
$(RM) spwltest.dsu spwltest.o spacewirelight.o
 
/spwamba_test/spwamba_test.dsu Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
/spwamba_test/spwamba_test.c
11,7 → 11,7
* The following defines are set in the Makefile:
* TXCLKFREQ TX base clock frequency in MHz
* DESCTABLESIZE Size of descriptor table as 2-log of nr of descriptors
* QUEUEFILL Number of bytes to almost fill up TX and RX queues
* QUEUEFILL Number of bytes needed to ALMOST fill up TX and RX queues
* LOOPBACKSWITCH 1 if the spacewire loopback can be switched through UART RX enable
*/
 
/spwamba_test/Makefile
8,7 → 8,7
# In that case the program is built by sim/spwamba_leon3/Makefile.
#
 
SWDEFS = -DTXCLKFREQ=200 -DDESCTABLESIZE=10 -DQUEUEFILL=1760 -DLOOPBACKSWITCH=0
SWDEFS = -DTXCLKFREQ=200 -DDESCTABLESIZE=10 -DQUEUEFILL=1980 -DLOOPBACKSWITCH=0
 
CC = sparc-elf-gcc
CFLAGS = -msoft-float -Wall -Os

powered by: WebSVN 2.1.0

© copyright 1999-2023 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.