fb2bc944bf6406bbd9191f7805561b063e389ecb
[packages.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 }