aboutsummaryrefslogtreecommitdiff
path: root/examples/io/pads/spi.h
blob: 72a2f99b09019bef31a575c75b8abaded4c66ad2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
 * PSn00bSDK controller polling example (SPI driver)
 * (C) 2021 spicyjpeg - MPL licensed
 */

/**
 * @file spi.h
 * @brief Asynchronous SPI controller driver
 *
 * @details This is a fairly complete timer driven, asynchronous high-speed SPI
 * driver, with support for sending custom commands (including memory card
 * access), in about 200 lines of code. Feel free to copy and adapt it.
 *
 * The way this works is by maintaining a queue of requests to send, each with
 * its own payload and callback. Timer 2 is configured to trigger an IRQ at
 * regular intervals. On each tick, the next request in the queue (or a poll
 * command if no request is pending) is prepared and the first byte is
 * sent; if the controller asks for more data by pulling /ACK low, the next
 * byte is sent and the received byte is placed into a buffer. This goes on
 * until the last byte is exchanged and the controller stops asserting /ACK.
 *
 * On the next tick, the response buffer is passed to the request's callback
 * and reset, and the next request in the queue is sent. This blindly assumes
 * it only takes one tick for a request/response to be sent, which is the case
 * for controllers' very small packets but not for memory cards. It is
 * advisable to call spi_set_poll_rate() to temporarily reduce poll rate while
 * accessing memory cards.
 *
 * Note that this driver completely takes over the SPI bus, so you won't be
 * able to use any BIOS functions that rely on SPI access (i.e. pad and memory
 * card APIs) alongside it.
 */

#pragma once

#include <stdint.h>
#include <stddef.h>
#include <psxpad.h>

// Maximum request/response length (34 bytes for pads, 140 for memory cards).
// Must be a multiple of 4 to avoid memory alignment issues.
//#define SPI_BUFF_LEN 36
#define SPI_BUFF_LEN 140

/* Request structures */

typedef void (*SPI_Callback)(uint32_t port, const volatile uint8_t *buff, size_t rx_len);

typedef struct _SPI_Request {
	union {
		uint8_t			data[SPI_BUFF_LEN];
		PadRequest		pad_req;
		MemCardRequest	mcd_req;
	};
	uint32_t			len, port;
	SPI_Callback		callback;
	struct _SPI_Request	*next;
} SPI_Request;

/* Public API */

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Allocates a new request object and adds it to the request queue. The
 * object must be populated afterwards by setting the length, callback and
 * filling in the TX data buffer.
 */
SPI_Request *SPI_CreateRequest(void);

/**
 * @brief Changes the controller polling rate. The lowest supported rate is 65
 * Hz (requests sent every 1/65th of a second, each port polled at 32.5 Hz when
 * no request is pending).
 *
 * @param value
 */
void SPI_SetPollRate(uint32_t value);

/**
 * @brief Installs the SPI and timer 2 interrupt handlers and starts the poll
 * timer. By default the polling rate is set to 250 Hz (125 Hz per port),
 * however it can be changed at any time by calling SPI_SetPollRate().
 *
 * The provided callback (if any) is called to report the result of poll
 * requests, which are issued automatically when no other request is in the
 * queue. Passing NULL as callback does not disable auto-polling.
 *
 * @param callback
 */
void SPI_Init(SPI_Callback callback);

#ifdef __cplusplus
}
#endif