aboutsummaryrefslogtreecommitdiff
path: root/shared/pb/driver.c
blob: f43d5c1468f37037f9acc5527b6bcad51a356a7c (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
#include <memory.h>

#include "types.h"
#include "driver.h"

/** \brief [private] placeholder global state variable */
static pb_state_t _global_state = PB_GS_NOINIT;

/** \brief [private] main controller global state */
static pb_state_t _main_state = PB_GS_NOINIT;

__weak pb_state_t pbdrv_hook_mod_state_read() {
	return _global_state;
}

__weak void pbdrv_hook_mod_state_write(pb_state_t state) {
	_global_state = state;
}

__weak void pbdrv_hook_main_state_update(pb_state_t state) { }

__weak bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) {
	return false;
}

__weak void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz) {
	if (sz == 0) return;
	pb_cmd_t cmd = (enum pb_cmd) buf[0];

	// shift buffer pointer to only contain the puzzle bus message buf
	buf++;
	sz--;

	// allow user to implement custom commands
	if (pbdrv_hook_cmd(i2c_addr, cmd, buf, sz))
		return;

	switch (cmd) {
		case PB_CMD_READ: return pbdrv_handle_read(i2c_addr, buf, sz);
		case PB_CMD_WRITE: return pbdrv_handle_write(i2c_addr, buf, sz);
		case PB_CMD_MAGIC: return pbdrv_handle_magic(i2c_addr, buf, sz);
		case PB_CMD_SEX: return pbdrv_handle_sex(i2c_addr, buf, sz);
		default: return;
	}
}

__weak void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz) {
	if (sz == 0) return;
	pb_cmd_read_t * cmd = (pb_cmd_read_t *) buf;

	// allow user to addrimplement custom read handlers
	if (pbdrv_hook_read(i2c_addr, cmd->address))
		return;

	switch (cmd->address) {
		case PB_ADDR_GS: {
			char res[] = {
				PB_CMD_READ,
				PB_ADDR_GS,
				pbdrv_hook_mod_state_read(),
			};
			return pbdrv_i2c_send(i2c_addr, res, sizeof(res));
		}
		default: return;
	}
}

__weak void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz) {
	if (sz < 2) return; // must have address and at least 1 byte data
	pb_cmd_write_t * cmd = (pb_cmd_write_t *) buf;

	// allow user to implement custom read handlers
	if (pbdrv_hook_write(i2c_addr, cmd->address, (char *) cmd->data, sz - 1))
		return;

	switch (cmd->address) {
		case PB_ADDR_GS:
			pbdrv_hook_mod_state_write(cmd->data[0]);
			break;
		default: return;
	}
}

__weak void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz) {
	if (sz != sizeof(pb_magic_msg)) return;
	if (memcmp(buf, pb_magic_msg, sizeof(pb_magic_msg)) != 0) return;

	size_t res_size = sizeof(pb_cmd_t) + sizeof(pb_magic_res);
	char res[res_size];
	res[0] = PB_CMD_MAGIC;
	memcpy(res, pb_magic_res, sizeof(pb_magic_res));

	pbdrv_i2c_send(i2c_addr, res, res_size);
}

__weak void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz) {
	if (sz == 0) return;
	pb_cmd_sex_t * cmd = (pb_cmd_sex_t *) buf;

	// send own state
	char res[] = {
		PB_CMD_SEX,
		pbdrv_hook_mod_state_read(),
	};
	pbdrv_i2c_send(i2c_addr, res, sizeof(res));

	if (cmd->main_state == _main_state) return;
	// keep main controller state
	_main_state = cmd->main_state;
	// call update if main state changed
	pbdrv_hook_main_state_update(_main_state);
}