+/* val is 16 bits, including 4 bits-cksum in MSB, return 0xFFFF is
+ * cksum is wrong, or the 12 bits value on success. */
+static uint16_t verify_cksum(uint16_t val)
+{
+ uint16_t x, cksum;
+
+ x = (val & 0xfff);
+ /* add the four 4-bits blocks of val together */
+ cksum = val & 0xf;
+ val = val >> 4;
+ cksum += val & 0xf;
+ cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
+ val = val >> 4;
+ cksum += val & 0xf;
+ cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
+ val = val >> 4;
+ cksum += val & 0xf;
+ cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
+ if (cksum == 0xf)
+ return x;
+ return 0xffff; /* wrong cksum */
+}
+
+static inline void decode_frame(struct frame_status *status,
+ uint16_t ref_time, uint16_t cur_time, uint8_t cur_tsop)
+{
+ uint16_t diff_time = cur_time - status->prev_time;
+
+ /* first rising edge */
+ if (status->len == 0 && cur_tsop && diff_time > TSOP_TIME_LONG) {
+ status->len = 1;
+ status->val = 1;
+ status->frame = 0;
+ status->start_angle_time = cur_time - ref_time;
+ status->mask = 1;
+ }
+ /* any short edge */
+ else if (status->len != 0 && diff_time < TSOP_TIME_SHORT) {
+ if (status->len & 1) {
+ if (status->val)
+ status->frame |= status->mask;
+ status->mask <<= 1;
+ }
+ status->len ++;
+ }
+ /* any long edge */
+ else if (status->len != 0 && diff_time < TSOP_TIME_LONG) {
+ status->val = !status->val;
+ if (status->val)
+ status->frame |= status->mask;
+ status->mask <<= 1;
+ status->len += 2;
+ }
+ /* error case, reset */
+ else {
+ status->len = 0;
+ }
+
+ /* end of frame */
+ if (status->len == FRAME_LEN*2) {
+ uint8_t tail_next = (status->tail+1) & FRAME_RING_MASK;
+
+ if (tail_next != status->head) {
+ status->ring[status->tail].frame = (status->frame & FRAME_MASK);
+ status->ring[status->tail].time = status->start_angle_time;
+ status->tail = tail_next;
+ if ((status->led_cpt & 0x7) == 0)
+ LED3_TOGGLE();
+ status->led_cpt ++;
+ }
+ status->len = 0;
+ }
+
+ status->prev_time = cur_time;
+ status->prev_tsop = cur_tsop;
+}
+