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 |