aboutsummaryrefslogtreecommitdiffstats
path: root/fw/usb.c
blob: 29928883d4e54f56192fa9322f116b02a57b93c6 (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
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <xc.h>

#include <stdbool.h>
#include <stdint.h>

#include <usb.h>
#include <usb_device.h>
#include <usb_device_cdc.h>

#include "usb.h"

// Line coding
// See struct USB_CDC_LINE_CODING
enum {
	DATA_RATE = 9600, // bps
	CHAR_FORMAT = NUM_STOP_BITS_1,
	PARITY_TYPE = PARITY_NONE,
	DATA_BITS = 8,
};

// State
typedef enum {
	IDLE, // waiting for command
	CMD_START, // received opcode
	ECHO,
	WRITE_EEPROM,
	READ_EEPROM,
} State;

static uint8_t readBuf[CDC_DATA_OUT_EP_SIZE];
static uint8_t writeBuf[CDC_DATA_IN_EP_SIZE];
static State state = IDLE;

// Handle "e" echo command.
static void
echo(void) {
	static uint8_t readLen = 0u;
	static uint8_t ri, wi;

	if (!USBUSARTIsTxTrfReady()) {
		return;
	}

	if (readLen == 0u) {
		readLen = getsUSBUSART(readBuf, sizeof(readBuf));
		ri = 0u;
		wi = 0u;
	}

	while (readLen--) {
		if (wi > sizeof(writeBuf)) {
			// Overflow; flush
			putUSBUSART(writeBuf, sizeof(writeBuf));
			wi = 0u;
			return; // continue on next call
		}

		writeBuf[wi] = readBuf[ri];
		if (readBuf[ri] == '\n') {
			// End of command
			putUSBUSART(writeBuf, wi);
			readLen = 0u;
			state = IDLE;
			return;
		}

		ri++;
		wi++;
	}
}

// Handle "w" write eeprom command.
static void
writeEeprom(void) {
	// TODO
}

// Handle "r" read eeprom command.
static void readEeprom(void) {
	// TODO
}

static void
setCmdState(uint8_t opcode) {
	switch (opcode) {
	case 'e': // echo
		state = ECHO;
		break;
	case 'w':
		state = WRITE_EEPROM;
		break;
	case 'r':
		state = READ_EEPROM;
		break;
	}
}

void
usbTask(void) {
	static uint8_t opcode = 0u;

	USBDeviceTasks();

	if (USBGetDeviceState() < CONFIGURED_STATE) {
		return;
	}
	if (USBIsDeviceSuspended()) {
		return;
	}

	switch (state) {
	case IDLE:
		if (getsUSBUSART(&opcode, 1u) > 0) {
			state = CMD_START;
		}
		break;
	case CMD_START:
		// Skip space char
		if (getsUSBUSART(readBuf, 1u) > 0) {
			setCmdState(opcode);
		}
	case ECHO: echo();
		break;
	case WRITE_EEPROM: writeEeprom();
		break;
	case READ_EEPROM: readEeprom();
		break;
	}

	CDCTxService();
}

static void
configure(void) {
	line_coding.dwDTERate = DATA_RATE;
	line_coding.bCharFormat = CHAR_FORMAT;
	line_coding.bParityType = PARITY_TYPE;
	line_coding.bDataBits = DATA_BITS;
}

bool
USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, uint16_t size) {
	switch ((int)event) {
	case EVENT_TRANSFER:
		break;
	case EVENT_SOF:
		break;
	case EVENT_SUSPEND:
		break;
	case EVENT_RESUME:
		break;
	case EVENT_CONFIGURED:
		CDCInitEP();
		configure();
		break;
	case EVENT_SET_DESCRIPTOR:
		break;
	case EVENT_EP0_REQUEST:
		USBCheckCDCRequest();
		break;
	case EVENT_BUS_ERROR:
		break;
	case EVENT_TRANSFER_TERMINATED:
		break;
	default:
		break;
	}

	return true;
}