ucmb: Add an optimized hotpath-assembly example implementation for AVR8. It's about...
[openwrt/svn-archive/archive.git] / utils / ucmb / microcontroller_examples / atmel_avr8_optimized / 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 * This hotpath-assembly implementation is about twice as fast
10 * as the C implementation.
11 *
12 * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 */
24
25 #include "ucmb.h"
26 #include "util.h"
27 #include "uart.h"
28
29 #include <stdint.h>
30 #include <avr/io.h>
31 #include <avr/interrupt.h>
32 #include <avr/pgmspace.h>
33 #include <avr/wdt.h>
34
35
36 #ifndef __GNUC__
37 # error "Need the GNU C compiler"
38 #endif
39
40 #undef __naked
41 #define __naked __attribute__((__naked__))
42 #undef __used
43 #define __used __attribute__((__used__))
44
45
46 struct ucmb_message_hdr {
47 uint16_t magic; /* UCMB_MAGIC */
48 uint16_t len; /* Payload length (excluding header and footer) */
49 } __attribute__((packed));
50
51 struct ucmb_message_footer {
52 uint16_t crc; /* CRC of the header + payload. */
53 } __attribute__((packed));
54
55 struct ucmb_status {
56 uint16_t magic; /* UCMB_MAGIC */
57 uint16_t code; /* enum ucmb_status_code */
58 } __attribute__((packed));
59
60 #define UCMB_MAGIC 0x1337
61
62 enum ucmb_status_code {
63 UCMB_STAT_OK = 0,
64 UCMB_STAT_EPROTO, /* Protocol format error */
65 UCMB_STAT_ENOMEM, /* Out of memory */
66 UCMB_STAT_E2BIG, /* Message too big */
67 UCMB_STAT_ECRC, /* CRC error */
68 };
69
70
71 static uint8_t ucmb_buf[sizeof(struct ucmb_message_hdr) +
72 UCMB_MAX_MSG_LEN +
73 sizeof(struct ucmb_message_footer)] __used;
74 static uint16_t ucmb_buf_ptr __used;
75 static struct ucmb_status status_buf __used;
76 static uint16_t ucmb_send_message_len __used;
77
78 /* The current IRQ handler */
79 static void (*ucmb_interrupt_handler)(void) __used;
80
81
82 /* Polynomial: x^16 + x^15 + x^2 + 1 */
83 static const prog_uint16_t crc16_table[256] = {
84 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
85 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
86 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
87 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
88 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
89 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
90 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
91 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
92 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
93 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
94 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
95 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
96 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
97 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
98 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
99 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
100 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
101 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
102 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
103 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
104 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
105 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
106 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
107 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
108 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
109 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
110 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
111 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
112 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
113 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
114 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
115 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
116 };
117
118 static inline uint16_t crc16_block_update(uint16_t crc, const void *_data, uint16_t size)
119 {
120 const uint8_t *data = _data;
121 uint8_t offset;
122
123 while (size--) {
124 wdt_reset();
125 offset = crc ^ *data;
126 crc = (crc >> 8) ^ pgm_read_word(&crc16_table[offset]);
127 data++;
128 }
129
130 return crc;
131 }
132
133 static inline uint16_t ucmb_calc_msg_buffer_crc(void)
134 {
135 const struct ucmb_message_hdr *hdr;
136 uint16_t crc = 0xFFFF;
137
138 hdr = (const struct ucmb_message_hdr *)ucmb_buf;
139 crc = crc16_block_update(crc, ucmb_buf,
140 sizeof(struct ucmb_message_hdr) + hdr->len);
141 crc ^= 0xFFFF;
142
143 return crc;
144 }
145
146 /* The generic interrupt handler.
147 * This just branches to the state specific handler.
148 */
149 ISR(SPI_STC_vect) __naked;
150 ISR(SPI_STC_vect)
151 {
152 __asm__ __volatile__(
153 " ; UCMB generic interrupt handler \n"
154 " push __tmp_reg__ \n"
155 " in __tmp_reg__, __SREG__ \n"
156 " push r30 \n"
157 " push r31 \n"
158 " lds r30, ucmb_interrupt_handler + 0 \n"
159 " lds r31, ucmb_interrupt_handler + 1 \n"
160 " ijmp ; Jump to the real handler \n"
161 );
162 }
163 #define UCMB_IRQ_EPILOGUE \
164 " ; UCMB interrupt epilogue (start) \n"\
165 " pop r31 \n"\
166 " pop r30 \n"\
167 " out __SREG__, __tmp_reg__ \n"\
168 " pop __tmp_reg__ \n"\
169 " reti \n"\
170 " ; UCMB interrupt epilogue (end) \n"
171
172 static void __naked __used ucmb_handler_LISTEN(void)
173 {
174 __asm__ __volatile__(
175 " push r16 \n"
176 " push r17 \n"
177 " push r18 \n"
178 " lds r16, ucmb_buf_ptr + 0 \n"
179 " lds r17, ucmb_buf_ptr + 1 \n"
180 " ldi r18, hi8(%[sizeof_buf]) \n"
181 " cpi r16, lo8(%[sizeof_buf]) \n"
182 " cpc r17, r18 \n"
183 " in r18, %[_SPDR] \n"
184 " brsh 1f ; overflow \n"
185 " ; Store SPDR in the buffer \n"
186 " movw r30, r16 \n"
187 " subi r30, lo8(-(ucmb_buf)) \n"
188 " sbci r31, hi8(-(ucmb_buf)) \n"
189 " st Z, r18 \n"
190 "1: \n"
191 " ; Increment the buffer pointer \n"
192 " subi r16, lo8(-1) \n"
193 " sbci r17, hi8(-1) \n"
194 " sts ucmb_buf_ptr + 0, r16 \n"
195 " sts ucmb_buf_ptr + 1, r17 \n"
196 " cpi r17, 0 \n"
197 " brne 1f \n"
198 " cpi r16, %[sizeof_msg_hdr] \n"
199 " breq hdr_sanity_check ; buf_ptr == hdrlen \n"
200 " brlo st_listen_out ; buf_ptr < hdrlen \n"
201 "1: \n"
202 " ; Get payload length from header \n"
203 " lds r30, (ucmb_buf + %[offsetof_hdr_len] + 0) \n"
204 " lds r31, (ucmb_buf + %[offsetof_hdr_len] + 1) \n"
205 " ; Add header and footer length to get full length \n"
206 " subi r30, lo8(-(%[sizeof_msg_hdr] + %[sizeof_msg_footer])) \n"
207 " sbci r31, hi8(-(%[sizeof_msg_hdr] + %[sizeof_msg_footer])) \n"
208 " ; Check if we have the full packet \n"
209 " cp r30, r16 \n"
210 " cpc r31, r17 \n"
211 " breq st_listen_have_full_packet \n"
212 "st_listen_out: \n"
213 " pop r18 \n"
214 " pop r17 \n"
215 " pop r16 \n"
216 UCMB_IRQ_EPILOGUE /* reti */
217 " \n"
218 "hdr_sanity_check: \n"
219 " lds r30, (ucmb_buf + %[offsetof_hdr_magic] + 0) \n"
220 " lds r31, (ucmb_buf + %[offsetof_hdr_magic] + 1) \n"
221 " ldi r18, hi8(%[_UCMB_MAGIC]) \n"
222 " cpi r30, lo8(%[_UCMB_MAGIC]) \n"
223 " cpc r31, r18 \n"
224 " brne invalid_hdr_magic \n"
225 " lds r30, (ucmb_buf + %[offsetof_hdr_len] + 0) \n"
226 " lds r31, (ucmb_buf + %[offsetof_hdr_len] + 1) \n"
227 " ldi r18, hi8(0x8001) \n"
228 " cpi r30, lo8(0x8001) \n"
229 " cpc r31, r18 \n"
230 " brsh bogus_payload_len \n"
231 " rjmp st_listen_out \n"
232 " \n"
233 "invalid_hdr_magic: \n"
234 " ; Invalid magic number in the packet header. Reset. \n"
235 "bogus_payload_len: \n"
236 " ; Bogus payload length in packet header. Reset. \n"
237 " clr r18 \n"
238 " sts ucmb_buf_ptr + 0, r18 \n"
239 " sts ucmb_buf_ptr + 1, r18 \n"
240 " rjmp st_listen_out \n"
241 " \n"
242 "st_listen_have_full_packet: \n"
243 " ; We have the full packet. Any SPI transfer is stopped \n"
244 " ; while we are processing the packet, so this \n"
245 " ; is a slowpath. Branch to a C function. \n"
246 " clr __zero_reg__ \n"
247 " push r18 \n"
248 " push r19 \n"
249 " push r20 \n"
250 " push r21 \n"
251 " push r22 \n"
252 " push r23 \n"
253 " push r24 \n"
254 " push r25 \n"
255 " push r26 \n"
256 " push r27 \n"
257 " push r30 \n"
258 " push r31 \n"
259 " push __tmp_reg__ \n"
260 " rcall ucmb_received_packet \n"
261 " pop __tmp_reg__ \n"
262 " pop r31 \n"
263 " pop r30 \n"
264 " pop r27 \n"
265 " pop r26 \n"
266 " pop r25 \n"
267 " pop r24 \n"
268 " pop r23 \n"
269 " pop r22 \n"
270 " pop r21 \n"
271 " pop r20 \n"
272 " pop r19 \n"
273 " pop r18 \n"
274 " rjmp st_listen_out \n"
275 : /* none */
276 : [sizeof_buf] "i" (sizeof(ucmb_buf))
277 , [sizeof_msg_hdr] "M" (sizeof(struct ucmb_message_hdr))
278 , [sizeof_msg_footer] "M" (sizeof(struct ucmb_message_footer))
279 , [offsetof_hdr_magic] "M" (offsetof(struct ucmb_message_hdr, magic))
280 , [offsetof_hdr_len] "M" (offsetof(struct ucmb_message_hdr, len))
281 , [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
282 , [_UCMB_MAGIC] "i" (UCMB_MAGIC)
283 : "memory"
284 );
285 }
286
287 static void __naked __used ucmb_handler_SENDSTATUS(void)
288 {
289 __asm__ __volatile__(
290 " push r16 \n"
291 /*" push r17 \n" */
292 " push r18 \n"
293 " lds r16, ucmb_buf_ptr + 0 \n"
294 " cpi r16, %[sizeof_ucmb_status] \n"
295 " brsh 1f ; This is the trailing byte \n"
296 " ; Write the next byte from status_buf to SPDR \n"
297 " mov r30, r16 \n"
298 " clr r31 \n"
299 " subi r30, lo8(-(status_buf)) \n"
300 " sbci r31, hi8(-(status_buf)) \n"
301 " ld r18, Z \n"
302 " out %[_SPDR], r18 \n"
303 "1: \n"
304 " subi r16, lo8(-1) \n"
305 " sts ucmb_buf_ptr + 0, r16 \n"
306 " cpi r16, (%[sizeof_ucmb_status] + 1) \n"
307 " brne st_sendstatus_out \n"
308 " ; Finished. Sent all status_buf bytes + trailing byte. \n"
309 " clr r18 \n"
310 " sts ucmb_buf_ptr + 0, r18 \n"
311 " ; Switch back to listening state... \n"
312 " ldi r18, lo8(gs(ucmb_handler_LISTEN)) \n"
313 " sts ucmb_interrupt_handler + 0, r18 \n"
314 " ldi r18, hi8(gs(ucmb_handler_LISTEN)) \n"
315 " sts ucmb_interrupt_handler + 1, r18 \n"
316 " ; ...if we have no pending transmission \n"
317 " lds r30, ucmb_send_message_len + 0 \n"
318 " lds r31, ucmb_send_message_len + 1 \n"
319 " clr r18 \n"
320 " cpi r30, 0 \n"
321 " cpc r31, r18 \n"
322 " breq st_sendstatus_out \n"
323 " ; Switch status to SENDMESSAGE and send the first byte. \n"
324 " ldi r18, lo8(gs(ucmb_handler_SENDMESSAGE)) \n"
325 " sts ucmb_interrupt_handler + 0, r18 \n"
326 " ldi r18, hi8(gs(ucmb_handler_SENDMESSAGE)) \n"
327 " sts ucmb_interrupt_handler + 1, r18 \n"
328 " ; Send the first byte \n"
329 " lds r18, ucmb_buf + 0 \n"
330 " out %[_SPDR], r18 \n"
331 " ldi r18, 1 \n"
332 " sts ucmb_buf_ptr + 0, r18 \n"
333 "st_sendstatus_out: \n"
334 " pop r18 \n"
335 /*" pop r17 \n"*/
336 " pop r16 \n"
337 UCMB_IRQ_EPILOGUE /* reti */
338 : /* none */
339 : [sizeof_ucmb_status] "M" (sizeof(struct ucmb_status))
340 , [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
341 : "memory"
342 );
343 }
344
345 static void __naked __used ucmb_handler_SENDMESSAGE(void)
346 {
347 __asm__ __volatile__(
348 " push r16 \n"
349 " push r17 \n"
350 " push r18 \n"
351 " lds r16, ucmb_buf_ptr + 0 \n"
352 " lds r17, ucmb_buf_ptr + 1 \n"
353 " lds r30, ucmb_send_message_len + 0 \n"
354 " lds r31, ucmb_send_message_len + 1 \n"
355 " cp r16, r30 \n"
356 " cpc r17, r31 \n"
357 " brsh 1f ; This is the trailing byte \n"
358 " movw r30, r16 \n"
359 " subi r30, lo8(-(ucmb_buf)) \n"
360 " sbci r31, hi8(-(ucmb_buf)) \n"
361 " ld r18, Z \n"
362 " out %[_SPDR], r18 \n"
363 "1: \n"
364 " subi r16, lo8(-1) \n"
365 " sbci r17, hi8(-1) \n"
366 " sts ucmb_buf_ptr + 0, r16 \n"
367 " sts ucmb_buf_ptr + 1, r17 \n"
368 " lds r30, ucmb_send_message_len + 0 \n"
369 " lds r31, ucmb_send_message_len + 1 \n"
370 " subi r30, lo8(-1) \n"
371 " sbci r31, hi8(-1) \n"
372 " cp r16, r30 \n"
373 " cpc r17, r31 \n"
374 " brne st_sendmessage_out \n"
375 " ; Message + trailing byte processed. Retrieve status. \n"
376 " clr r18 \n"
377 " sts ucmb_buf_ptr + 0, r18 \n"
378 " sts ucmb_buf_ptr + 1, r18 \n"
379 " ldi r18, lo8(gs(ucmb_handler_RETRSTATUS)) \n"
380 " sts ucmb_interrupt_handler + 0, r18 \n"
381 " ldi r18, hi8(gs(ucmb_handler_RETRSTATUS)) \n"
382 " sts ucmb_interrupt_handler + 1, r18 \n"
383 "st_sendmessage_out: \n"
384 " pop r18 \n"
385 " pop r17 \n"
386 " pop r16 \n"
387 UCMB_IRQ_EPILOGUE /* reti */
388 : /* none */
389 : [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
390 : "memory"
391 );
392 }
393
394 static void __naked __used ucmb_handler_RETRSTATUS(void)
395 {
396 __asm__ __volatile__(
397 " push r16 \n"
398 /*" push r17 \n"*/
399 " push r18 \n"
400 " in r18, %[_SPDR] \n"
401 " lds r16, ucmb_buf_ptr + 0 \n"
402 " mov r30, r16 \n"
403 " clr r31 \n"
404 " subi r30, lo8(-(status_buf)) \n"
405 " sbci r31, hi8(-(status_buf)) \n"
406 " st Z, r18 \n"
407 " subi r16, -1 \n"
408 " sts ucmb_buf_ptr + 0, r16 \n"
409 " cpi r16, %[sizeof_ucmb_status] \n"
410 " brne st_retrstatus_out \n"
411 " ; Completely received the status \n"
412 " clr r16 \n"
413 " sts ucmb_buf_ptr + 0, r16 \n"
414 " ; Switch back to listening state... \n"
415 " ldi r18, lo8(gs(ucmb_handler_LISTEN)) \n"
416 " sts ucmb_interrupt_handler + 0, r18 \n"
417 " ldi r18, hi8(gs(ucmb_handler_LISTEN)) \n"
418 " sts ucmb_interrupt_handler + 1, r18 \n"
419 " ; Check status-report magic value \n"
420 " lds r30, (status_buf + %[offsetof_status_magic] + 0) \n"
421 " lds r31, (status_buf + %[offsetof_status_magic] + 1) \n"
422 " ldi r18, hi8(%[_UCMB_MAGIC]) \n"
423 " cpi r30, lo8(%[_UCMB_MAGIC]) \n"
424 " cpc r31, r18 \n"
425 " brne invalid_status_magic \n"
426 " ; Check status-report error code \n"
427 " lds r30, (status_buf + %[offsetof_status_code] + 0) \n"
428 " lds r31, (status_buf + %[offsetof_status_code] + 1) \n"
429 " ldi r18, hi8(%[_UCMB_STAT_OK]) \n"
430 " cpi r30, lo8(%[_UCMB_STAT_OK]) \n"
431 " cpc r31, r18 \n"
432 " brne faulty_status_code \n"
433 "st_retrstatus_out: \n"
434 " pop r18 \n"
435 /*" pop r17 \n"*/
436 " pop r16 \n"
437 UCMB_IRQ_EPILOGUE /* reti */
438 " \n"
439 "invalid_status_magic: \n"
440 "faulty_status_code: \n"
441 " ; Branch to the C error handler \n"
442 " clr __zero_reg__ \n"
443 " push r18 \n"
444 " push r19 \n"
445 " push r20 \n"
446 " push r21 \n"
447 " push r22 \n"
448 " push r23 \n"
449 " push r24 \n"
450 " push r25 \n"
451 " push r26 \n"
452 " push r27 \n"
453 " push r30 \n"
454 " push r31 \n"
455 " push __tmp_reg__ \n"
456 " rcall ucmb_received_faulty_status \n"
457 " pop __tmp_reg__ \n"
458 " pop r31 \n"
459 " pop r30 \n"
460 " pop r27 \n"
461 " pop r26 \n"
462 " pop r25 \n"
463 " pop r24 \n"
464 " pop r23 \n"
465 " pop r22 \n"
466 " pop r21 \n"
467 " pop r20 \n"
468 " pop r19 \n"
469 " pop r18 \n"
470 " rjmp st_retrstatus_out \n"
471 : /* none */
472 : [sizeof_ucmb_status] "M" (sizeof(struct ucmb_status))
473 , [offsetof_status_magic] "M" (offsetof(struct ucmb_status, magic))
474 , [offsetof_status_code] "M" (offsetof(struct ucmb_status, code))
475 , [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
476 , [_UCMB_MAGIC] "i" (UCMB_MAGIC)
477 , [_UCMB_STAT_OK] "i" (UCMB_STAT_OK)
478 : "memory"
479 );
480 }
481
482 /* We received a full packet. This is called from assembly code. */
483 static void __used ucmb_received_packet(void)
484 {
485 struct ucmb_message_hdr *hdr;
486 struct ucmb_message_footer *footer;
487 uint16_t payload_len;
488
489 hdr = (struct ucmb_message_hdr *)ucmb_buf;
490 payload_len = hdr->len;
491
492 status_buf.magic = UCMB_MAGIC;
493 status_buf.code = UCMB_STAT_OK;
494 if (unlikely(ucmb_buf_ptr > sizeof(ucmb_buf))) {
495 /* Message is way too big and was truncated. */
496 status_buf.code = UCMB_STAT_E2BIG;
497 } else {
498 footer = (struct ucmb_message_footer *)(
499 ucmb_buf + sizeof(struct ucmb_message_hdr) +
500 payload_len);
501 if (ucmb_calc_msg_buffer_crc() != footer->crc)
502 status_buf.code = UCMB_STAT_ECRC;
503 }
504 ucmb_interrupt_handler = ucmb_handler_SENDSTATUS;
505 ucmb_buf_ptr = 0;
506 /* Send the first byte */
507 SPDR = ((uint8_t *)&status_buf)[ucmb_buf_ptr];
508 ucmb_buf_ptr++;
509
510 if (unlikely(status_buf.code != UCMB_STAT_OK))
511 return; /* Corrupt message. Don't pass it to user code. */
512
513 ucmb_send_message_len = ucmb_rx_message(
514 ucmb_buf + sizeof(struct ucmb_message_hdr),
515 payload_len);
516 if (ucmb_send_message_len) {
517 footer = (struct ucmb_message_footer *)(
518 ucmb_buf + sizeof(struct ucmb_message_hdr) +
519 ucmb_send_message_len);
520
521 hdr->magic = UCMB_MAGIC;
522 hdr->len = ucmb_send_message_len;
523 footer->crc = ucmb_calc_msg_buffer_crc();
524
525 ucmb_send_message_len += sizeof(struct ucmb_message_hdr) +
526 sizeof(struct ucmb_message_footer);
527 }
528 }
529
530 /* We received a status report with an error condition.
531 * This is called from assembly code. */
532 static void __used ucmb_received_faulty_status(void)
533 {
534 /* The master sent us a status report with an error code.
535 * Something's wrong with us. Print a status message and
536 * get caught by the watchdog, yummy.
537 */
538
539 cli();
540 wdt_disable();
541 uart_logmsg("UCMB: Received faulty status report. Triggering reset.");
542 wdt_enable(WDTO_15MS);
543 while (1) {
544 /* "It's Coming Right For Us!" */
545 }
546 }
547
548 void ucmb_init(void)
549 {
550 ucmb_interrupt_handler = ucmb_handler_LISTEN;
551
552 /* SPI slave mode 0 with IRQ enabled. */
553 DDRB |= (1 << 6/*MISO*/);
554 DDRB &= ~((1 << 7/*SCK*/) | (1 << 5/*MOSI*/) | (1 << 4/*SS*/));
555 SPCR = (1 << SPE) | (1 << SPIE) /*| (1 << CPOL) | (1 << CPHA)*/;
556 (void)SPSR; /* clear state */
557 (void)SPDR; /* clear state */
558 }