[package] fix bemused compilation failure (#6980)
[openwrt/svn-archive/archive.git] / utils / ucmb / microcontroller_examples / atmel_avr8 / ucmb.c
1 /*
2 * Microcontroller message bus
3 * uc-side slave implementation for Atmel AVR8
4 *
5 * The gcc compiler always treats multi-byte variables as litte-endian.
6 * So no explicit endianness conversions are done on the message header,
7 * footer and status data structures.
8 *
9 * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 */
21
22 #include "ucmb.h"
23
24 #include <stdint.h>
25 #include <avr/io.h>
26 #include <avr/interrupt.h>
27 #include <util/crc16.h>
28
29
30 struct ucmb_message_hdr {
31 uint16_t magic; /* UCMB_MAGIC */
32 uint16_t len; /* Payload length (excluding header and footer) */
33 } __attribute__((packed));
34
35 struct ucmb_message_footer {
36 uint16_t crc; /* CRC of the header + payload. */
37 } __attribute__((packed));
38
39 struct ucmb_status {
40 uint16_t magic; /* UCMB_MAGIC */
41 uint16_t code; /* enum ucmb_status_code */
42 } __attribute__((packed));
43
44 #define UCMB_MAGIC 0x1337
45
46 enum ucmb_status_code {
47 UCMB_STAT_OK = 0,
48 UCMB_STAT_EPROTO, /* Protocol format error */
49 UCMB_STAT_ENOMEM, /* Out of memory */
50 UCMB_STAT_E2BIG, /* Message too big */
51 UCMB_STAT_ECRC, /* CRC error */
52 };
53
54
55 static uint8_t ucmb_buf[sizeof(struct ucmb_message_hdr) +
56 UCMB_MAX_MSG_LEN +
57 sizeof(struct ucmb_message_footer)];
58 static uint16_t ucmb_buf_ptr;
59 static struct ucmb_status status_buf;
60 static uint16_t ucmb_send_message_len;
61
62 /* Statemachine */
63 static uint8_t ucmb_state;
64 enum {
65 UCMB_ST_LISTEN, /* Listen for incoming messages. */
66 UCMB_ST_SENDSTATUS, /* Send the status report. */
67 UCMB_ST_SENDMESSAGE, /* Send the message. */
68 UCMB_ST_RETRSTATUS, /* Retrieve the status report. */
69 };
70
71 #define TRAILING 1
72
73
74 static void ucmb_send_next_byte(void)
75 {
76 switch (ucmb_state) {
77 case UCMB_ST_SENDSTATUS: {
78 const uint8_t *st = (const uint8_t *)&status_buf;
79
80 if (ucmb_buf_ptr < sizeof(struct ucmb_status))
81 SPDR = st[ucmb_buf_ptr];
82 ucmb_buf_ptr++;
83 if (ucmb_buf_ptr == sizeof(struct ucmb_status) + TRAILING) {
84 ucmb_buf_ptr = 0;
85 if (ucmb_send_message_len) {
86 ucmb_state = UCMB_ST_SENDMESSAGE;
87 goto st_sendmessage;
88 } else
89 ucmb_state = UCMB_ST_LISTEN;
90 }
91 break;
92 }
93 case UCMB_ST_SENDMESSAGE: {
94 st_sendmessage:;
95 uint16_t full_length = sizeof(struct ucmb_message_hdr) +
96 ucmb_send_message_len +
97 sizeof(struct ucmb_message_footer);
98 if (ucmb_buf_ptr < full_length)
99 SPDR = ucmb_buf[ucmb_buf_ptr];
100 ucmb_buf_ptr++;
101 if (ucmb_buf_ptr == full_length + TRAILING) {
102 ucmb_send_message_len = 0;
103 ucmb_buf_ptr = 0;
104 ucmb_state = UCMB_ST_RETRSTATUS;
105 }
106 break;
107 } }
108 }
109
110 static uint16_t crc16_block_update(uint16_t crc, const void *_data, uint16_t size)
111 {
112 const uint8_t *data = _data;
113
114 while (size) {
115 crc = _crc16_update(crc, *data);
116 data++;
117 size--;
118 }
119
120 return crc;
121 }
122
123 static uint16_t ucmb_calc_msg_buffer_crc(void)
124 {
125 const struct ucmb_message_hdr *hdr;
126 uint16_t crc = 0xFFFF;
127
128 hdr = (const struct ucmb_message_hdr *)ucmb_buf;
129 crc = crc16_block_update(crc, ucmb_buf,
130 sizeof(struct ucmb_message_hdr) + hdr->len);
131 crc ^= 0xFFFF;
132
133 return crc;
134 }
135
136 /* SPI data transfer interrupt. */
137 ISR(SPI_STC_vect)
138 {
139 uint8_t data;
140
141 data = SPDR;
142 SPDR = 0;
143
144 switch (ucmb_state) {
145 case UCMB_ST_LISTEN: {
146 struct ucmb_message_hdr *hdr;
147 struct ucmb_message_footer *footer;
148
149 if (ucmb_buf_ptr < sizeof(ucmb_buf))
150 ucmb_buf[ucmb_buf_ptr] = data;
151 ucmb_buf_ptr++;
152 if (ucmb_buf_ptr < sizeof(struct ucmb_message_hdr))
153 return; /* Header RX not complete. */
154 hdr = (struct ucmb_message_hdr *)ucmb_buf;
155 if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr)) {
156 if (hdr->magic != UCMB_MAGIC) {
157 /* Invalid magic! Reset. */
158 ucmb_buf_ptr = 0;
159 return;
160 }
161 if (hdr->len > 0x8000) {
162 /* Completely bogus length! Reset. */
163 ucmb_buf_ptr = 0;
164 return;
165 }
166 return;
167 }
168
169 if (ucmb_buf_ptr == sizeof(struct ucmb_message_hdr) +
170 sizeof(struct ucmb_message_footer) +
171 hdr->len) {
172 status_buf.magic = UCMB_MAGIC;
173 status_buf.code = UCMB_STAT_OK;
174 if (ucmb_buf_ptr > sizeof(ucmb_buf)) {
175 /* Message is way too big and was truncated. */
176 status_buf.code = UCMB_STAT_E2BIG;
177 } else {
178 footer = (struct ucmb_message_footer *)(
179 ucmb_buf + sizeof(struct ucmb_message_hdr) +
180 hdr->len);
181 if (ucmb_calc_msg_buffer_crc() != footer->crc)
182 status_buf.code = UCMB_STAT_ECRC;
183 }
184 ucmb_state = UCMB_ST_SENDSTATUS;
185 ucmb_buf_ptr = 0;
186 ucmb_send_next_byte();
187
188 if (status_buf.code != UCMB_STAT_OK)
189 return; /* Corrupt message. Don't pass it to user code. */
190
191 ucmb_send_message_len = ucmb_rx_message(
192 ucmb_buf + sizeof(struct ucmb_message_hdr),
193 hdr->len);
194 if (ucmb_send_message_len) {
195 footer = (struct ucmb_message_footer *)(
196 ucmb_buf + sizeof(struct ucmb_message_hdr) +
197 ucmb_send_message_len);
198
199 hdr->magic = UCMB_MAGIC;
200 hdr->len = ucmb_send_message_len;
201 footer->crc = ucmb_calc_msg_buffer_crc();
202 }
203 }
204 break;
205 }
206 case UCMB_ST_SENDSTATUS:
207 case UCMB_ST_SENDMESSAGE:
208 ucmb_send_next_byte();
209 break;
210 case UCMB_ST_RETRSTATUS: {
211 uint8_t *st = (uint8_t *)&status_buf;
212
213 st[ucmb_buf_ptr++] = data;
214 if (ucmb_buf_ptr == sizeof(struct ucmb_status)) {
215 /* We could possibly handle the status report here... */
216 ucmb_buf_ptr = 0;
217 ucmb_state = UCMB_ST_LISTEN;
218 }
219 break;
220 } }
221 }
222
223 void ucmb_init(void)
224 {
225 ucmb_state = UCMB_ST_LISTEN;
226
227 /* SPI slave mode 0 with IRQ enabled. */
228 DDRB |= (1 << 4/*MISO*/);
229 DDRB &= ~((1 << 5/*SCK*/) | (1 << 3/*MOSI*/) | (1 << 2/*SS*/));
230 SPCR = (1 << SPE) | (1 << SPIE) /*| (1 << CPOL) | (1 << CPHA)*/;
231 (void)SPSR; /* clear state */
232 (void)SPDR; /* clear state */
233 }