+/* val is 12 bits. Return the 16 bits value that includes the 4 bits
+ * cksum in MSB. */
+static uint16_t do_cksum(uint16_t val)
+{
+ uint16_t x, cksum;
+
+ x = (val & 0xfff);
+ /* add the three 4-bits blocks of x together */
+ cksum = x & 0xf;
+ x = x >> 4;
+ cksum += x & 0xf;
+ cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
+ x = x >> 4;
+ cksum += x & 0xf;
+ cksum = (cksum & 0xf) + ((cksum & 0xf0) >> 4);
+ cksum = (~cksum) & 0xf;
+ return (cksum << 12) + (val & 0xfff);
+}
+
+/* get the frame from laser time difference, return 0 on error (this
+ * frame cannot be sent). */
+static uint32_t get_frame(uint16_t laserdiff)
+{
+ uint32_t frame = 0;
+ uint16_t val, mid, min, max;
+
+ /* for calibration, return the time */
+#ifdef INC_FRAME
+ static uint16_t fr = 1;
+ fr += 2;
+ return fr;
+#endif
+#ifdef SEND_RAWTIME
+ return laserdiff | 1; /* frame must be odd */
+#endif
+
+ min = 0;
+ max = 511;
+ while (min != max) {
+ mid = (max + min + 1) / 2;
+ val = pgm_read_word(&framedist_table[mid]);
+
+ if (laserdiff > val)
+ max = mid - 1;
+ else
+ min = mid;
+ }
+
+ frame |= ((uint32_t)((min << FRAME_DATA_SHIFT)
+ & FRAME_DATA_MASK));
+ frame |= BEACON_ID;
+
+ /* process cksum and return */
+ return do_cksum(frame);
+}
+