861 lines
31 KiB
C
Raw Normal View History

2024-12-26 11:10:17 +02:00
#pragma once
#include "dc_hle_types.h"
/** \defgroup maple Maple Bus
\brief Driver for the Dreamcast's Maple Peripheral Bus
\ingroup peripherals
*/
/** \brief Enable Maple DMA debugging.
\ingroup maple
Changing this to a 1 will add massive amounts of processing time to the
maple system in general, but it can help in verifying DMA errors. In
general, for most purposes this should stay disabled.
*/
#define MAPLE_DMA_DEBUG 0
/** \brief Enable Maple IRQ debugging.
\ingroup maple
Changing this to a 1 will turn on intra-interrupt debugging messages, which
may cause issues if you're using dcload rather than a raw serial debug
terminal. You probably will never have a good reason to enable this, so keep
it disabled for normal use.
*/
#define MAPLE_IRQ_DEBUG 0
/** \defgroup maple_regs Registers
\brief Addresses for various maple registers
\ingroup maple
These are various registers related to the Maple Bus. In general, you
probably won't ever need to mess with these directly.
@{
*/
#define MAPLE_BASE 0xa05f6c00 /**< \brief Maple register base */
#define MAPLE_DMAADDR (MAPLE_BASE+0x04) /**< \brief DMA address register */
#define MAPLE_RESET2 (MAPLE_BASE+0x10) /**< \brief Reset register #2 */
#define MAPLE_ENABLE (MAPLE_BASE+0x14) /**< \brief Enable register */
#define MAPLE_STATE (MAPLE_BASE+0x18) /**< \brief Status register */
#define MAPLE_SPEED (MAPLE_BASE+0x80) /**< \brief Speed register */
#define MAPLE_RESET1 (MAPLE_BASE+0x8c) /**< \brief Reset register #1 */
/** @} */
/** \defgroup maple_reg_values Register Values
\brief Values for various maple registers
\ingroup maple
These are the values that are written to registers to get them to do their
thing.
@{
*/
#define MAPLE_RESET2_MAGIC 0 /**< \brief 2nd reset value */
#define MAPLE_ENABLE_ENABLED 1 /**< \brief Enable Maple */
#define MAPLE_ENABLE_DISABLED 0 /**< \brief Disable Maple */
#define MAPLE_STATE_IDLE 0 /**< \brief Idle state */
#define MAPLE_STATE_DMA 1 /**< \brief DMA in-progress */
#define MAPLE_SPEED_2MBPS 0 /**< \brief 2Mbps bus speed */
#define MAPLE_SPEED_TIMEOUT(n) ((n) << 16) /**< \brief Bus timeout macro */
#ifndef _arch_sub_naomi
#define MAPLE_RESET1_MAGIC 0x6155404f /**< \brief First reset value */
#else
#define MAPLE_RESET1_MAGIC 0x6155405f
#endif
/** @} */
/** \defgroup maple_cmds Commands and Responses
\brief Maple command and response values
\ingroup maple
These are all either commands or responses to commands sent to or from Maple
in normal operation.
@{
*/
#define MAPLE_RESPONSE_FILEERR -5 /**< \brief File error */
#define MAPLE_RESPONSE_AGAIN -4 /**< \brief Try again later */
#define MAPLE_RESPONSE_BADCMD -3 /**< \brief Bad command sent */
#define MAPLE_RESPONSE_BADFUNC -2 /**< \brief Bad function code */
#define MAPLE_RESPONSE_NONE -1 /**< \brief No response */
#define MAPLE_COMMAND_DEVINFO 1 /**< \brief Device info request */
#define MAPLE_COMMAND_ALLINFO 2 /**< \brief All info request */
#define MAPLE_COMMAND_RESET 3 /**< \brief Reset device request */
#define MAPLE_COMMAND_KILL 4 /**< \brief Kill device request */
#define MAPLE_RESPONSE_DEVINFO 5 /**< \brief Device info response */
#define MAPLE_RESPONSE_ALLINFO 6 /**< \brief All info response */
#define MAPLE_RESPONSE_OK 7 /**< \brief Command completed ok */
#define MAPLE_RESPONSE_DATATRF 8 /**< \brief Data transfer */
#define MAPLE_COMMAND_GETCOND 9 /**< \brief Get condition request */
#define MAPLE_COMMAND_GETMINFO 10 /**< \brief Get memory information */
#define MAPLE_COMMAND_BREAD 11 /**< \brief Block read */
#define MAPLE_COMMAND_BWRITE 12 /**< \brief Block write */
#define MAPLE_COMMAND_BSYNC 13 /**< \brief Block sync */
#define MAPLE_COMMAND_SETCOND 14 /**< \brief Set condition request */
#define MAPLE_COMMAND_MICCONTROL 15 /**< \brief Microphone control */
#define MAPLE_COMMAND_CAMCONTROL 17 /**< \brief Camera control */
/** @} */
/** \defgroup maple_functions Function Codes
\brief Values of maple "function" codes
\ingroup maple
This is the list of maple device types (function codes). Each device must
have at least one function to actually do anything.
@{
*/
/* Function codes; most sources claim that these numbers are little
endian, and for all I know, they might be; but since it's a bitmask
it doesn't really make much different. We'll just reverse our constants
from the "big-endian" version. */
#define MAPLE_FUNC_PURUPURU 0x00010000 /**< \brief Jump pack */
#define MAPLE_FUNC_MOUSE 0x00020000 /**< \brief Mouse */
#define MAPLE_FUNC_CAMERA 0x00080000 /**< \brief Camera (Dreameye) */
#define MAPLE_FUNC_CONTROLLER 0x01000000 /**< \brief Controller */
#define MAPLE_FUNC_MEMCARD 0x02000000 /**< \brief Memory card */
#define MAPLE_FUNC_LCD 0x04000000 /**< \brief LCD screen */
#define MAPLE_FUNC_CLOCK 0x08000000 /**< \brief Clock */
#define MAPLE_FUNC_MICROPHONE 0x10000000 /**< \brief Microphone */
#define MAPLE_FUNC_ARGUN 0x20000000 /**< \brief AR gun? */
#define MAPLE_FUNC_KEYBOARD 0x40000000 /**< \brief Keyboard */
#define MAPLE_FUNC_LIGHTGUN 0x80000000 /**< \brief Lightgun */
/** @} */
/* \cond */
/* Pre-define list/queue types */
struct maple_frame;
// TAILQ_HEAD(maple_frame_queue, maple_frame);
struct maple_driver;
// LIST_HEAD(maple_driver_list, maple_driver);
struct maple_state_str;
/* \endcond */
/** \brief Maple frame to be queued for transport.
\ingroup maple
Internal representation of a frame to be queued up for sending.
\headerfile dc/maple.h
// */
typedef struct maple_frame {
/** \brief Send queue handle. NOT A FUNCTION! */
// TAILQ_ENTRY(maple_frame) frameq;
int cmd; /**< \brief Command (see \ref maple_cmds) */
int dst_port; /**< \brief Destination port */
int dst_unit; /**< \brief Destination unit */
int length; /**< \brief Data transfer length in 32-bit words */
volatile int state; /**< \brief Has this frame been sent / responded to? */
volatile int queued; /**< \brief Are we on the queue? */
void *send_buf; /**< \brief The data which will be sent (if any) */
uint8 *recv_buf; /**< \brief Points into recv_buf_arr, but 32-byte aligned */
struct maple_device *dev; /**< \brief Does this belong to a device? */
void (*callback)(struct maple_state_str *, struct maple_frame *); /**< \brief Response callback */
#if MAPLE_DMA_DEBUG
uint8 recv_buf_arr[1024 + 1024 + 32]; /**< \brief Response receive area */
#else
uint8 recv_buf_arr[1024 + 32]; /**< \brief Response receive area */
#endif
} maple_frame_t;
/** \defgroup maple_frame_states Frame States
\brief States for a maple frame
\ingroup maple
@{
*/
#define MAPLE_FRAME_VACANT 0 /**< \brief Ready to be used */
#define MAPLE_FRAME_UNSENT 1 /**< \brief Ready to be sent */
#define MAPLE_FRAME_SENT 2 /**< \brief Frame has been sent, but no response yet */
#define MAPLE_FRAME_RESPONDED 3 /**< \brief Frame has a response */
/** @} */
/** \brief Maple device info structure.
\ingroup maple
This structure is used by the hardware to deliver the response to the device
info request.
\note product_name and product_license are not guaranteed to be NULL terminated.
\headerfile dc/maple.h
*/
typedef struct maple_devinfo {
uint32 functions; /**< \brief Function codes supported */
uint32 function_data[3]; /**< \brief Additional data per function */
uint8 area_code; /**< \brief Region code */
uint8 connector_direction; /**< \brief 0: UP (most controllers), 1: DOWN (lightgun, microphones) */
char product_name[30]; /**< \brief Name of device */
char product_license[60]; /**< \brief License statement */
uint16 standby_power; /**< \brief Power consumption (standby) */
uint16 max_power; /**< \brief Power consumption (max) */
} maple_devinfo_t;
/** \brief Maple response frame structure.
\ingroup maple
This structure is used to deliver the actual response to a request placed.
The data field is where all the interesting stuff will be.
\headerfile dc/maple.h
*/
typedef struct maple_response {
int8 response; /**< \brief Response */
uint8 dst_addr; /**< \brief Destination address */
uint8 src_addr; /**< \brief Source address */
uint8 data_len; /**< \brief Data length (in 32-bit words) */
uint8 data[]; /**< \brief Data (if any) */
} maple_response_t;
/** \brief One maple device.
\ingroup maple
Note that we duplicate the port/unit info which is normally somewhat
implicit so that we can pass around a pointer to a particular device struct.
\headerfile dc/maple.h
*/
typedef struct maple_device {
/* Public */
int valid; /**< \brief Is this a valid device? */
int port; /**< \brief Maple bus port connected to */
int unit; /**< \brief Unit number, off of the port */
maple_devinfo_t info; /**< \brief Device info struct */
/* Private */
int dev_mask; /**< \brief Device-present mask for unit 0's */
maple_frame_t frame; /**< \brief One rx/tx frame */
struct maple_driver *drv; /**< \brief Driver which handles this device */
volatile int status_valid; /**< \brief Have we got our first status update? */
uint8 status[1024]; /**< \brief Status buffer (for pollable devices) */
} maple_device_t;
#define MAPLE_PORT_COUNT 4 /**< \brief Number of ports on the bus */
#define MAPLE_UNIT_COUNT 6 /**< \brief Max number of units per port */
/** \brief Internal representation of a Maple port.
\ingroup maple
Each maple port can contain up to 6 devices, the first one of which is
always the port itself.
\headerfile dc/maple.h
*/
typedef struct maple_port {
int port; /**< \brief Port ID */
maple_device_t units[MAPLE_UNIT_COUNT]; /**< \brief Pointers to active units */
} maple_port_t;
/** \brief A maple device driver.
\ingroup maple
Anything which is added to this list is capable of handling one or more
maple device types. When a device of the given type is connected (includes
startup "connection"), the driver is invoked. This same process happens for
disconnection, response receipt, and on a periodic interval (for normal
updates).
\headerfile dc/maple.h
*/
typedef struct maple_driver {
/** \brief Driver list handle. NOT A FUNCTION! */
// LIST_ENTRY(maple_driver) drv_list;
uint32 functions; /**< \brief One or more MAPLE_FUNCs ORed together */
const char *name; /**< \brief The driver name */
/* Callbacks, to be filled in by the driver */
/** \brief Periodic polling callback.
This callback will be called to update the status of connected devices
periodically.
\param drv This structure for the driver.
*/
void (*periodic)(struct maple_driver *drv);
/** \brief Device attached callback.
This callback will be called when a new device of this driver is
connected to the system.
\param drv This structure for the driver.
\param dev The device that was connected.
\return 0 on success, <0 on error.
*/
int (*attach)(struct maple_driver *drv, maple_device_t *dev);
/** \brief Device detached callback.
This callback will be called when a device of this driver is disconnected
from the system.
\param drv This structure for the driver.
\param dev The device that was detached.
*/
void (*detach)(struct maple_driver *drv, maple_device_t *dev);
} maple_driver_t;
/** \brief Maple state structure.
\ingroup maple
We put everything in here to keep from polluting the global namespace too
much.
\headerfile dc/maple.h
*/
typedef struct maple_state_str {
/** \brief Maple device driver list. Do not manipulate directly! */
// struct maple_driver_list driver_list;
/** \brief Maple frame submission queue. Do not manipulate directly! */
// struct maple_frame_queue frame_queue;
/** \brief Maple device info structure */
maple_port_t ports[MAPLE_PORT_COUNT];
/** \brief DMA interrupt counter */
volatile int dma_cntr;
/** \brief VBlank interrupt counter */
volatile int vbl_cntr;
/** \brief DMA send buffer */
uint8 *dma_buffer;
/** \brief Is a DMA running now? */
volatile int dma_in_progress;
/** \brief Next port that will be auto-detected */
int detect_port_next;
/** \brief Next unit which will be auto-detected */
int detect_unit_next;
/** \brief Did the detect wrap? */
volatile int detect_wrapped;
/** \brief Our vblank handler handle */
int vbl_handle;
/** \brief The port to read for lightgun status, if any. */
int gun_port;
/** \brief The horizontal position of the lightgun signal. */
int gun_x;
/** \brief The vertical position of the lightgun signal. */
int gun_y;
} maple_state_t;
/** \brief Maple DMA buffer size.
\ingroup maple
Increase if you do a _LOT_ of maple stuff on every periodic interrupt.
*/
#define MAPLE_DMA_SIZE 16384
/* Maple memory read/write functions; these are just hooks in case
we need to do something else later */
/** \brief Maple memory read macro.
\ingroup maple
*/
#define maple_read(A) ( *((vuint32*)(A)) )
/** \brief Maple memory write macro.
\ingroup maple
*/
#define maple_write(A, V) ( *((vuint32*)(A)) = (V) )
/** \defgroup maple_func_rvs Return Values
\brief Return codes from maple access functions
\ingroup maple
@{
*/
#define MAPLE_EOK 0 /**< \brief No error */
#define MAPLE_EFAIL -1 /**< \brief Command failed */
#define MAPLE_EAGAIN -2 /**< \brief Try again later */
#define MAPLE_EINVALID -3 /**< \brief Invalid command */
#define MAPLE_ENOTSUPP -4 /**< \brief Command not supported by device */
#define MAPLE_ETIMEOUT -5 /**< \brief Command timed out */
/** @} */
/**************************************************************************/
/* maple_globals.c */
/** \cond Global state info.
Do not manipulate this state yourself, as it will likely break things if you
do so.
*/
extern maple_state_t maple_state;
/** \endcond */
/**************************************************************************/
/* maple_utils.c */
/** \brief Enable the Maple bus.
\ingroup maple
This will be done for you automatically at init time, and there's probably
not many reasons to be doing this during runtime.
*/
void maple_bus_enable(void);
/** \brief Disable the Maple bus.
\ingroup maple
There's really not many good reasons to be mucking with this at runtime.
*/
void maple_bus_disable(void);
/** \brief Start a Maple DMA.
\ingroup maple
This stuff will all be handled internally, so there's probably no reason to
be doing this yourself.
*/
void maple_dma_start(void);
/** \brief Stop a Maple DMA.
\ingroup maple
This stuff will all be handled internally, so there's probably no reason to
be doing this yourself.
*/
void maple_dma_stop(void);
/** \brief Is a Maple DMA in progress?
\ingroup maple
\return Non-zero if a DMA is in progress.
*/
int maple_dma_in_progress(void);
/** \brief Set the Maple DMA address.
\ingroup maple
Once again, you should not muck around with this in your programs.
*/
void maple_dma_addr(void *ptr);
/** \brief Return a "maple address" for a port, unit pair.
\ingroup maple
\param port The port to build the address for.
\param unit The unit to build the address for.
\return The Maple address of the pair.
*/
uint8 maple_addr(int port, int unit);
/** \brief Decompose a "maple address" into a port, unit pair.
\ingroup maple
\warning
This function will not work with multi-cast addresses!
\param addr The input address.
\param port Output space for the port of the address.
\param unit Output space for the unit of the address.
*/
void maple_raddr(uint8 addr, int * port, int * unit);
/** \brief Return a string with the capabilities of a given function code.
\ingroup maple
This function is not re-entrant, and thus NOT THREAD SAFE.
\param functions The list of function codes.
\return A string containing the capabilities.
*/
const char * maple_pcaps(uint32 functions);
/** \brief Return a string representing the maple response code.
\ingroup maple
\param response The response code returned from the function.
\return A string containing a textual representation of the
response code.
*/
const char * maple_perror(int response);
/** \brief Determine if a given device is valid.
\ingroup maple
\param p The port to check.
\param u The unit to check.
\return Non-zero if the device is valid.
*/
int maple_dev_valid(int p, int u);
/** \brief Enable light gun mode for this frame.
\ingroup maple
This function enables light gun processing for the current frame of data.
Light gun mode will automatically be disabled when the data comes back for
this frame.
\param port The port to enable light gun mode on.
\return MAPLE_EOK on success, MAPLE_EFAIL on error.
*/
int maple_gun_enable(int port);
/** \brief Disable light gun mode.
\ingroup maple
There is probably very little reason to call this function. Light gun mode
is ordinarily disabled and is automatically disabled after the data has been
read from the device. The only reason to call this function is if you call
the maple_gun_enable() function, and then change your mind during the same
frame.
*/
void maple_gun_disable(void);
/** \brief Read the light gun position values.
\ingroup maple
This function fetches the gun position values from the video hardware and
returns them via the parameters. These values are not normalized before
returning.
\param x Storage for the horizontal position of the gun.
\param y Storage for the vertical position of the gun.
\note The values returned from this function are the raw H and V counter
values from the video hardware where the gun registered its
position. The values, however, need a bit of massaging before they
correspond nicely to screen values. The y value is particularly odd
in interlaced modes due to the fact that you really have half as
many physical lines on the screen as you might expect.
*/
void maple_gun_read_pos(int *x, int *y);
#if MAPLE_DMA_DEBUG
/* Debugging help */
/** \brief Setup a sentinel for debugging DMA issues.
\ingroup maple
\param buffer The buffer to add the sentinel to.
\param bufsize The size of the data in the buffer.
*/
void maple_sentinel_setup(void * buffer, int bufsize);
/** \brief Verify the presence of the sentine.
\ingroup maple
\param bufname A string to recognize the buffer by.
\param buffer The buffer to check.
\param bufsize The size of the buffer.
*/
void maple_sentinel_verify(const char * bufname, void * buffer, int bufsize);
#endif
/**************************************************************************/
/* maple_queue.c */
/** \brief Send all queued frames.
\ingroup maple
*/
void maple_queue_flush(void);
/** \brief Submit a frame for queueing.
\ingroup maple
This will generally be called inside the periodic interrupt; however, if you
need to do something asynchronously (e.g., VMU access) then it might cause
some problems. In this case, the function will automatically do locking by
disabling interrupts temporarily. In any case, the callback will be done
inside an IRQ context.
\param frame The frame to queue up.
\retval 0 On success.
\retval -1 If the frame is already queued.
*/
int maple_queue_frame(maple_frame_t *frame);
/** \brief Remove a used frame from the queue.
\ingroup maple
This will be done automatically when the frame is consumed.
\param frame The frame to remove from the queue.
\retval 0 On success.
\retval -1 If the frame is not queued.
*/
int maple_queue_remove(maple_frame_t *frame);
/** \brief Initialize a new frame to prepare it to be placed on the queue.
\ingroup maple
You should call this before you fill in the frame data.
\param frame The frame to initialize.
*/
void maple_frame_init(maple_frame_t *frame);
/** \brief Lock a frame so that someone else can't use it in the mean time.
\ingroup maple
\retval 0 On success.
\retval -1 If the frame is already locked.
*/
int maple_frame_lock(maple_frame_t *frame);
/** \brief Unlock a frame.
\ingroup maple
*/
void maple_frame_unlock(maple_frame_t *frame);
/**************************************************************************/
/* maple_driver.c */
/** \brief Register a maple device driver.
\ingroup maple
This should be done before calling maple_init().
\retval 0 On success (no error conditions defined).
*/
int maple_driver_reg(maple_driver_t *driver);
/** \brief Unregister a maple device driver.
\ingroup maple
\retval 0 On success (no error conditions defined).
*/
int maple_driver_unreg(maple_driver_t *driver);
/** \brief Attach a maple device to a driver, if possible.
\ingroup maple
\param det The detection frame.
\retval 0 On success.
\retval -1 If no driver is available.
*/
int maple_driver_attach(maple_frame_t *det);
/** \brief Detach an attached maple device.
\ingroup maple
\param p The port of the device to detach.
\param u The unit of the device to detach.
\retval 0 On success.
\retval -1 If the device wasn't valid.
*/
int maple_driver_detach(int p, int u);
/** \brief For each device which the given driver controls, call the callback.
\ingroup maple
\param drv The driver to loop through devices of.
\param callback The function to call. The parameter is the device
that it is being called on. It should return 0 on
success, and <0 on failure.
\retval 0 On success.
\retval -1 If any callbacks return <0.
*/
int maple_driver_foreach(maple_driver_t *drv, int (*callback)(maple_device_t *));
/** \brief Maple attach callback type.
\ingroup maple
Functions of this type can be set with maple_attach_callback() to respond
automatically to the attachment of a maple device that supports specified
functions.
*/
typedef void (*maple_attach_callback_t)(maple_device_t *dev);
/** \brief Set an automatic maple attach callback.
\ingroup maple
This function sets a callback function to be called when the specified
maple device that supports functions has been attached.
\param functions The functions maple device must support. Set to
0 to support all maple devices.
\param cb The callback to call when the maple is attached.
*/
void maple_attach_callback(uint32 functions, maple_attach_callback_t cb);
/** \brief Maple detach callback type.
\ingroup maple
Functions of this type can be set with maple_detach_callback() to respond
automatically to the detachment of a maple device that supports specified
functions.
*/
typedef void (*maple_detach_callback_t)(maple_device_t *dev);
/** \brief Set an automatic maple detach callback.
\ingroup maple
This function sets a callback function to be called when the specified
maple device that supports functions has been detached.
\param functions The functions maple device must support. Set to
0 to support all maple devices.
\param cb The callback to call when the maple is detached.
*/
void maple_detach_callback(uint32 functions, maple_detach_callback_t cb);
/**************************************************************************/
/* maple_irq.c */
/** \brief Called on every VBL (~60fps).
\ingroup maple
\param code The ASIC event code.
\param data The user pointer associated with this callback.
*/
void maple_vbl_irq_hnd(uint32 code, void *data);
/** \brief Called after a Maple DMA send / receive pair completes.
\ingroup maple
\param code The ASIC event code.
\param data The user pointer associated with this callback.
*/
void maple_dma_irq_hnd(uint32 code, void *data);
/**************************************************************************/
/* maple_enum.c */
/** \brief Return the number of connected devices.
\ingroup maple
\return The number of devices connected.
*/
int maple_enum_count(void);
/** \brief Get a raw device info struct for the given device.
\ingroup maple
\param p The port to look up.
\param u The unit to look up.
\return The device at that address, or NULL if no device is
there.
*/
maple_device_t * maple_enum_dev(int p, int u);
/** \brief Get the Nth device of the requested type (where N is zero-indexed).
\ingroup maple
\param n The index to look up.
\param func The function code to look for.
\return The device found, if any. NULL otherwise.
*/
maple_device_t * maple_enum_type(int n, uint32 func);
/** \brief Return the Nth device that is of the requested type and supports the
list of capabilities given.
\ingroup maple
Note, this only currently makes sense for controllers, since some devices
don't necessarily use the function data in the same manner that controllers
do (and controllers are the only devices where we have a list of what all
the bits mean at the moment).
\param n The index to look up.
\param func The function code to look for.
\param cap Capabilities bits to look for.
\return The device found, if any. NULL otherwise.
*/
maple_device_t * maple_enum_type_ex(int n, uint32 func, uint32 cap);
/** \brief Get the status struct for the requested maple device.
\ingroup maple
This function will wait until the status is valid before returning.
You should cast to the appropriate type you're expecting.
\param dev The device to look up.
\return The device's status.
*/
void * maple_dev_status(maple_device_t *dev);
/**************************************************************************/
/* maple_init.c */
/** \brief Initialize Maple.
\ingroup maple
*/
void maple_init(void);
/** \brief Shutdown Maple.
\ingroup maple
*/
void maple_shutdown(void);
/** \brief Wait for the initial bus scan to complete.
\ingroup maple
*/
void maple_wait_scan(void);
/**************************************************************************/
/* Convenience macros */
/* A "foreach" loop to scan all maple devices of a given type. It is used
like this:
MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st)
if(st->buttons & CONT_START)
return -1;
MAPLE_FOREACH_END()
The peripheral index can be obtained with __i, and the raw device struct
with __dev. The code inside the loop is guaranteed to be inside a block
(i.e., { code })
*/
/** \brief Begin a foreach loop over Maple devices.
\ingroup maple
This macro (along with the MAPLE_FOREACH_END() one) implements a simple
foreach-style loop over the given type of devices. Essentially, it grabs the
status of the device, and leaves it to you to figure out what to do with it.
The most common use of this would be to look for input on any controller.
\param TYPE The function code of devices to look at.
\param VARTYPE The type to cast the return value of
maple_dev_status() to.
\param VAR The name of the result of maple_dev_status().
*/
#define MAPLE_FOREACH_BEGIN(TYPE, VARTYPE, VAR) \
do { \
maple_device_t * __dev; \
VARTYPE * VAR; \
int __i; \
\
__i = 0; \
while( (__dev = maple_enum_type(__i, TYPE)) ) { \
VAR = (VARTYPE *)maple_dev_status(__dev); \
do {
/** \brief End a foreach loop over Maple devices.
\ingroup maple
Each MAPLE_FOREACH_BEGIN() must be paired with one of these after the loop
body.
*/
#define MAPLE_FOREACH_END() \
} while(0); \
__i++; \
} \
} while(0);