*/
#include "ucmb.h"
-#include "util.h"
-#include "uart.h"
#include <stdint.h>
#include <avr/io.h>
#define __naked __attribute__((__naked__))
#undef __used
#define __used __attribute__((__used__))
+#undef __noret
+#define __noret __attribute__((__noreturn__))
+#undef offsetof
+#define offsetof(type, member) ((size_t)&((type *)0)->member)
+#undef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#undef mb
+#define mb() __asm__ __volatile__("" : : : "memory") /* memory barrier */
+#ifndef ucmb_errorlog
+# define ucmb_errorlog(message) do { /* nothing */ } while (0)
+#endif
struct ucmb_message_hdr {
static uint16_t ucmb_buf_ptr __used;
static struct ucmb_status status_buf __used;
static uint16_t ucmb_send_message_len __used;
+static uint8_t ucmb_received_message;
/* The current IRQ handler */
static void (*ucmb_interrupt_handler)(void) __used;
"st_listen_have_full_packet: \n"
" ; We have the full packet. Any SPI transfer is stopped \n"
" ; while we are processing the packet, so this \n"
-" ; is a slowpath. Branch to a C function. \n"
-" clr __zero_reg__ \n"
-" push r18 \n"
-" push r19 \n"
-" push r20 \n"
-" push r21 \n"
-" push r22 \n"
-" push r23 \n"
-" push r24 \n"
-" push r25 \n"
-" push r26 \n"
-" push r27 \n"
-" push r30 \n"
-" push r31 \n"
-" push __tmp_reg__ \n"
-" rcall ucmb_received_packet \n"
-" pop __tmp_reg__ \n"
-" pop r31 \n"
-" pop r30 \n"
-" pop r27 \n"
-" pop r26 \n"
-" pop r25 \n"
-" pop r24 \n"
-" pop r23 \n"
-" pop r22 \n"
-" pop r21 \n"
-" pop r20 \n"
-" pop r19 \n"
-" pop r18 \n"
+" ; is a slowpath. \n"
+" ; Disable SPI and pass control to ucmb_work to \n"
+" ; handle the message. \n"
+" cbi %[_SPCR], %[_SPIE] \n"
+" ldi r18, 1 \n"
+" sts ucmb_received_message, r18 \n"
" rjmp st_listen_out \n"
: /* none */
: [sizeof_buf] "i" (sizeof(ucmb_buf))
, [offsetof_hdr_magic] "M" (offsetof(struct ucmb_message_hdr, magic))
, [offsetof_hdr_len] "M" (offsetof(struct ucmb_message_hdr, len))
, [_SPDR] "M" (_SFR_IO_ADDR(SPDR))
+ , [_SPCR] "M" (_SFR_IO_ADDR(SPCR))
+ , [_SPIE] "i" (SPIE)
, [_UCMB_MAGIC] "i" (UCMB_MAGIC)
: "memory"
);
"invalid_status_magic: \n"
"faulty_status_code: \n"
" ; Branch to the C error handler \n"
+" ; The handler does not return, so we don't need to \n"
+" ; push/pop the registers. \n"
" clr __zero_reg__ \n"
-" push r18 \n"
-" push r19 \n"
-" push r20 \n"
-" push r21 \n"
-" push r22 \n"
-" push r23 \n"
-" push r24 \n"
-" push r25 \n"
-" push r26 \n"
-" push r27 \n"
-" push r30 \n"
-" push r31 \n"
-" push __tmp_reg__ \n"
-" rcall ucmb_received_faulty_status \n"
-" pop __tmp_reg__ \n"
-" pop r31 \n"
-" pop r30 \n"
-" pop r27 \n"
-" pop r26 \n"
-" pop r25 \n"
-" pop r24 \n"
-" pop r23 \n"
-" pop r22 \n"
-" pop r21 \n"
-" pop r20 \n"
-" pop r19 \n"
-" pop r18 \n"
-" rjmp st_retrstatus_out \n"
+" rjmp ucmb_received_faulty_status \n"
: /* none */
: [sizeof_ucmb_status] "M" (sizeof(struct ucmb_status))
, [offsetof_status_magic] "M" (offsetof(struct ucmb_status, magic))
);
}
-/* We received a full packet. This is called from assembly code. */
-static void __used ucmb_received_packet(void)
+/* We received a status report with an error condition.
+ * This is called from assembly code. The function does not return. */
+static void __used __noret ucmb_received_faulty_status(void)
+{
+ /* The master sent us a status report with an error code.
+ * Something's wrong with us. Print a status message and
+ * get caught by the watchdog, yummy.
+ */
+
+ cli();
+ wdt_disable();
+ wdt_enable(WDTO_15MS);
+ ucmb_errorlog("UCMB: Received faulty status report. Triggering reset.");
+ while (1) {
+ /* "It's Coming Right For Us!" */
+ }
+}
+
+void ucmb_work(void)
{
struct ucmb_message_hdr *hdr;
struct ucmb_message_footer *footer;
uint16_t payload_len;
+ if (!ucmb_received_message)
+ return;
+
hdr = (struct ucmb_message_hdr *)ucmb_buf;
payload_len = hdr->len;
ucmb_buf_ptr++;
if (unlikely(status_buf.code != UCMB_STAT_OK))
- return; /* Corrupt message. Don't pass it to user code. */
+ goto out; /* Corrupt message. Don't pass it to user code. */
ucmb_send_message_len = ucmb_rx_message(
ucmb_buf + sizeof(struct ucmb_message_hdr),
ucmb_send_message_len += sizeof(struct ucmb_message_hdr) +
sizeof(struct ucmb_message_footer);
}
-}
-
-/* We received a status report with an error condition.
- * This is called from assembly code. */
-static void __used ucmb_received_faulty_status(void)
-{
- /* The master sent us a status report with an error code.
- * Something's wrong with us. Print a status message and
- * get caught by the watchdog, yummy.
- */
- cli();
- wdt_disable();
- uart_logmsg("UCMB: Received faulty status report. Triggering reset.");
- wdt_enable(WDTO_15MS);
- while (1) {
- /* "It's Coming Right For Us!" */
- }
+out:
+ ucmb_received_message = 0;
+ mb();
+ /* Re-enable SPI */
+ SPCR |= (1 << SPIE);
}
void ucmb_init(void)
/* Max length of the message payload. */
#define UCMB_MAX_MSG_LEN (128 * 64 / 8 + 16)
+/* Error logs: If you want UCMB error log messages, define
+ * ucmb_errorlog(message_string_literal)
+ * somewhere. If you don't define it, UCMB will be compiled
+ * without error messages. */
+
/* ucmb_rx_message - Message receive callback.
* Define this elsewhere. It's called on successful retrieval
* of a new message.
* message payload into the "payload" buffer and return the number
* of bytes to transmit. If no reply message is needed, return 0.
* Note that the "payload" buffer always has a size of UCMB_MAX_MSG_LEN.
+ * This function is called with interrupts enabled.
*/
extern uint16_t ucmb_rx_message(uint8_t *payload,
uint16_t payload_length);
+/* ucmb_work - Frequently call this from the mainloop.
+ * Must not be called from interrupt context.
+ * Must not be called with interrupts disabled.
+ */
+void ucmb_work(void);
+
/* Initialize the UCMB subsystem. */
void ucmb_init(void);