aboutsummaryrefslogtreecommitdiff
path: root/shared/pb/spec.adoc
blob: a99497b1385eda4dd615c556ba22df0e5b4ad025 (plain)
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
= Puzzle module specification

This folder contains an implementation of the puzzle bus protocol
specification, and is targeted at puzzle module developers. This document
describes the required implementation steps for integrating a new game into the
puzzle module framework.

== The bus

The puzzle bus carries data over a standard I^2^C bus. Additional details about
this bus can be found in the link:../../docs/design.adoc[Design document].

NOTE: Addresses influence the puzzle box's behavior, as the order of puzzles is
determined by the puzzle module address. Two puzzle modules may use the same
address, but this will mean that they cannot be used simultaniously in the same
puzzle box. Known addresses are documented in link:bus.h[].

== Puzzle bus driver (pbdrv)

The library in this folder is a partial implementation of the puzzle bus
specification *for puzzle modules*. Most functions in the driver are marked
with the 'weak' attribute, which allows you to override them by providing an
implementation.

In order to utilize this driver, the following must be done:

- The ``pbdrv_i2c_recv`` function must be *called* for every received *I^2^C
	read* frame
- The ``pbdrv_i2c_send`` function must be *implemented* with the
	platform-specific *I^2^C write* function

This is enough to get the puzzle module registered. You may also want to
implement some of the following integrations:

- If your game uses the global state variable, you should implement the
	<<sec:state-global,global state hooks>> to point the driver to your own
	global state variable, and be notified of reads/writes to it.
- If you want to expose additional game state variables over the puzzle bus,
	you should implement the <<sec:state-aux,auxiliary state hooks>>.
- If you want to implement custom puzzle bus commands, you can implement the
	<<sec:cmd,command hook>>.

All other kinds of integrations/hooks can likely be realized by overriding the
default implementations, but this is discouraged.

[[sec:state-global]]
== Global state

If your puzzle module defines its own ``pb_state_t``, you can tell the driver
to use it by implementing the ``pbdrv_hook_state_read`` and
``pbdrv_hook_state_write`` functions. These functions are also used by the
default implementation of the read/write commands to address 0 (global state).

Example:

```c
pb_state_t global_state = PB_GS_NOINIT;

pb_state_t pbdrv_hook_mod_state_read() {
	return global_state;
}

void pbdrv_hook_mod_state_write(pb_state_t state) {
	global_state = state;
}
```

[[sec:state-aux]]
== Auxiliary state

You can expose additional state variables by implementing the
``pbdrv_hook_read`` and ``pbdrv_hook_write`` functions. These functions should
return ``true`` for state addresses you want to override.

Example:

```c
#define CUSTOM_VAR_ADDR 0x01
uint8_t my_custom_variable = 10;

bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr) {
	switch (addr) {
		case CUSTOM_VAR_ADDR: {
			char res[] = { PB_CMD_READ, addr, my_custom_variable };
			pbdrv_i2c_send(i2c_addr, res, sizeof(res));
			break;
		}
		default: return false;
	}

	return true;
}

bool pbdrv_hook_write(uint16_t i2c_addr, uint8_t addr, const char * buf, size_t sz) {
	switch (addr) {
		case CUSTOM_VAR_ADDR: {
			if (sz != 1) return false;
			my_custom_variable = buf[0];
			break;
		}
		default: return false;
	}

	return true;
}
```

[[sec:cmd]]
== Custom commands

Similar to the auxiliary state, custom commands can be added by implementing
the ``pbdrv_hook_cmd`` function, which should return ``true`` for the
command(s) that you want to overwrite.

Example:

```c
bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) {
	if (cmd == 0x54) {
		printf("custom command received!\n");
		return true;
	}

	return false;
}
```