]> the.earth.li Git - temper-clone.git/blob - main.c
de54471813640c91a8b5cb4d476ecd4c614e1789
[temper-clone.git] / main.c
1 /*
2  * Basic firmware to emulate a USB TEMPer device (which uses an FM75 I2C
3  * sensor internally) using a Digispark + a DS18B20 1-wire sensor.
4  *
5  * Copyright 2018 Jonathan McDowell <noodles@earth.li>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 #include <stdbool.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <avr/eeprom.h>
24 #include <avr/interrupt.h>
25 #include <avr/io.h>
26 #include <avr/wdt.h>
27 #include <util/delay.h>
28
29 #include <avr/pgmspace.h>
30 #include "usbdrv.h"
31 #include "libs-device/osccal.h"
32
33 #include "timer.h"
34 #include "w1.h"
35
36 typedef struct {
37         uint8_t modifier;
38         uint8_t reserved;
39         uint8_t keycode[6];
40 } keyboard_report_t;
41 keyboard_report_t keyboard_report;
42
43 uint8_t temp_state = 0;
44 uint16_t last_temp = 0xFFFD;
45 #define TEMP_INTERVAL (10 * 1000)
46
47 uint8_t temp_report[8];
48 bool have_temp_int = false;
49
50 #define NUM_LOCK        1
51 #define CAPS_LOCK       2
52 #define SCROLL_LOCK     4
53 volatile static uchar ledstate = 0xff;
54 static uchar idlerate;
55
56 /* We populate this with the 1-wire device ROM ID when we get it */
57 int serno_str[] = {
58         USB_STRING_DESCRIPTOR_HEADER(16),
59         'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
60         'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
61 };
62
63 #define USB_INDEX_KEYBOARD      0
64 #define USB_INDEX_MOUSE         1
65
66 PROGMEM const char usbHidKeyboardReportDescriptor[] = {
67         0x05, 0x01,             // USAGE_PAGE (Generic Desktop)
68         0x09, 0x06,             // USAGE (Keyboard)
69         0xa1, 0x01,             // COLLECTION (Application)
70         0x85, 0x01,             //   REPORT_ID (1)
71         0x05, 0x07,             //   USAGE_PAGE (Keyboard) (Key Codes)
72         0x19, 0xe0,             //   USAGE_MINIMUM (Keyboard LeftControl)(224)
73         0x29, 0xe7,             //   USAGE_MAXIMUM (Keyboard Right GUI)(231)
74         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
75         0x25, 0x01,             //   LOGICAL_MAXIMUM (1)
76         0x75, 0x01,             //   REPORT_SIZE (1)
77         0x95, 0x08,             //   REPORT_COUNT (8)
78         0x81, 0x02,             //   INPUT (Data,Var,Abs) ; Modifier byte
79         0x95, 0x01,             //   REPORT_COUNT (1)
80         0x75, 0x08,             //   REPORT_SIZE (8)
81         0x81, 0x01,             //   INPUT (Cnst,Arr,Abs) ; Reserved byte
82         0x95, 0x03,             //   REPORT_COUNT (3)
83         0x75, 0x01,             //   REPORT_SIZE (1)
84         0x05, 0x08,             //   USAGE_PAGE (LEDs)
85         0x19, 0x01,             //   USAGE_MINIMUM (Num Lock)
86         0x29, 0x03,             //   USAGE_MAXIMUM (Scroll Lock)
87         0x91, 0x02,             //   OUTPUT (Data,Var,Abs) ; LED report
88         0x95, 0x05,             //   REPORT_COUNT (5)
89         0x75, 0x01,             //   REPORT_SIZE (1)
90         0x91, 0x01,             //   OUTPUT (Cnst,Arr,Abs)
91         0x95, 0x05,             //   REPORT_COUNT (5)
92         0x75, 0x08,             //   REPORT_SIZE (8)
93         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
94         0x25, 0xff,             //   LOGICAL_MAXIMUM (255)
95         0x05, 0x07,             //   USAGE_PAGE (Keyboard)(Key Codes)
96         0x19, 0x00,             //   USAGE_MINIMUM (Reserved (no event))(0)
97         0x29, 0xff,             //   USAGE_MAXIMUM (Keyboard Application)(null)
98         0x81, 0x00,             //   INPUT (Data,Ary,Abs)
99         0xc0                    // END_COLLECTION
100 };
101
102 PROGMEM const char usbHidMouseReportDescriptor[] = {
103         0x06, 0x00, 0xff,       // USAGE_PAGE (65280)
104         0x09, 0x01,             // USAGE (1)
105         0xa1, 0x01,             // COLLECTION (Application)
106         0x09, 0x01,             // USAGE (1)
107         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
108         0x26, 0xff, 0x00,       //   LOGICAL_MAXIMUM (255)
109         0x75, 0x08,             //   REPORT_SIZE (8)
110         0x95, 0x08,             //   REPORT_COUNT (8)
111         0x81, 0x02,             //   INPUT (Data,Var,Abs) ; Modifier byte
112         0x09, 0x01,             // USAGE (Pointer)
113         0x95, 0x08,             //   REPORT_COUNT (8)
114         0x91, 0x02,             //   OUTPUT (Data,Var,Abs) ; Modifier byte
115         0x05, 0x0c,             // USAGE_PAGE (Consumer)
116         0x09, 0x00,             //   USAGE (0)
117         0x15, 0x80,             //   LOGICAL_MINIMUM (128)
118         0x25, 0x7f,             //   LOGICAL_MAXIMUM (127)
119         0x75, 0x08,             //   REPORT_SIZE (8)
120         0x95, 0x08,             //   REPORT_COUNT (8)
121         0xb1, 0x02,             //   FEATURE (Data,Var,Abs)
122         0xc0                    // END_COLLECTION
123 };
124
125 /* USB configuration descriptor */
126 PROGMEM const char usbDescriptorConfiguration[] = {
127         9,                              /* sizeof(usbDescriptorConfiguration) */
128         USBDESCR_CONFIG,                /* descriptor type */
129         /* total length of data returned (including inlined descriptors) */
130         18 + 9 + 9 + 7 + 9 + 7, 0,
131         2,                              /* number of interfaces in this cfg */
132         1,                              /* index of this configuration */
133         0,                              /* configuration name string index */
134         (1 << 7) | USBATTR_REMOTEWAKE,  /* attributes */
135         USB_CFG_MAX_BUS_POWER/2,        /* max USB current in 2mA units */
136
137         /* Keyboard interface descriptor follows inline: */
138
139         /* sizeof(usbDescrInterface): length of descriptor in bytes */
140         9,
141         USBDESCR_INTERFACE,     /* descriptor type */
142         0,                      /* index of this interface */
143         0,                      /* alternate setting for this interface */
144         /* endpoints excl 0: number of endpoint descriptors to follow */
145         1,
146         3,                      /* Interface class: HID */
147         1,                      /* SubClass: Boot Interface */
148         1,                      /* Interface protocol: Keyboard */
149         0,                      /* string index for interface */
150         9,                      /* sizeof(usbDescrHID) */
151         USBDESCR_HID,           /* descriptor type: HID */
152         0x10, 0x01,             /* BCD representation of HID version */
153         0x00,                   /* target country code */
154         /* number of HID Report (or other HID class) Descriptors to follow */
155         0x01,
156         0x22,                   /* descriptor type: report */
157         sizeof(usbHidKeyboardReportDescriptor),
158         0,              /* total length of report descriptor */
159
160         /* endpoint descriptor for endpoint 1 */
161         7,                              /* sizeof(usbDescrEndpoint) */
162         USBDESCR_ENDPOINT,              /* descriptor type = endpoint */
163         (char)0x81,                     /* IN endpoint number 1 */
164         0x03,                           /* attrib: Interrupt endpoint */
165         8, 0,                           /* maximum packet size */
166         USB_CFG_INTR_POLL_INTERVAL,     /* in ms */
167
168         /* Mouse interface descriptor follows inline: */
169
170         /* sizeof(usbDescrInterface): length of descriptor in bytes */
171         9,
172         USBDESCR_INTERFACE,     /* descriptor type */
173         1,                      /* index of this interface */
174         0,                      /* alternate setting for this interface */
175         /* endpoints excl 0: number of endpoint descriptors to follow */
176         1,
177         3,                      /* Interface class: HID */
178         1,                      /* SubClass: Boot Interface */
179         2,                      /* Interface protocol: Mouse */
180         0,                      /* string index for interface */
181         9,                      /* sizeof(usbDescrHID) */
182         USBDESCR_HID,           /* descriptor type: HID */
183         0x10, 0x01,             /* BCD representation of HID version */
184         0x00,                   /* target country code */
185         /* number of HID Report (or other HID class) Descriptors to follow */
186         0x01,
187         0x22,                   /* descriptor type: report */
188         /* total length of report descriptor */
189         sizeof(usbHidMouseReportDescriptor), 0,
190
191         /* endpoint descriptor for endpoint 2 */
192         7,                              /* sizeof(usbDescrEndpoint) */
193         USBDESCR_ENDPOINT,              /* descriptor type = endpoint */
194         (char)0x82,                     /* IN endpoint number 2 */
195         0x03,                           /* attrib: Interrupt endpoint */
196         8, 0,                           /* maximum packet size */
197         USB_CFG_INTR_POLL_INTERVAL,     /* in ms */
198 };
199
200 inline char hexdigit(unsigned int i)
201 {
202         return (i < 10) ? ('0' + i) : ('A' - 10 + i);
203 }
204
205 /* Look for a 1-Wire device and use its ROMID to set the serial ID */
206 void set_serial(void)
207 {
208         uint8_t buf[8];
209         uint8_t i;
210
211         if (!w1_reset()) {
212                 return;
213         }
214
215         w1_write(0x33);         /* READ ROM */
216         w1_read(buf, 8);
217
218         for (i = 0; i < 8; i++) {
219                 serno_str[i * 2 + 1] = hexdigit(buf[i] >> 4);
220                 serno_str[i * 2 + 2] = hexdigit(buf[i] & 0xF);
221         }
222 }
223
224 uint16_t read_temp(void)
225 {
226         uint8_t buf[9];
227
228         cli();
229         if (!w1_reset()) {
230                 return 0xFFFF;
231                 sei();
232         }
233
234         w1_write(0xCC);         /* SKIP ROM */
235         w1_write(0x44);         /* Convert T */
236
237         do {
238                 w1_read(buf, 1);
239         } while (buf[0] != 0xFF);
240
241         if (!w1_reset()) {
242                 return 0xFFFF;
243                 sei();
244         }
245
246         w1_write(0xCC);         /* SKIP ROM */
247         w1_write(0xBE);         /* Read Scratchpad */
248         w1_read(buf, 9);
249         sei();
250
251         return buf[2] << 8 | buf[1];
252 }
253
254 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
255 {
256         if (rq->wValue.bytes[1] == USBDESCR_STRING &&
257                         rq->wValue.bytes[0] == 3) {
258                 usbMsgPtr = (usbMsgPtr_t) serno_str;
259                 return sizeof(serno_str);
260         } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
261                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
262                         /* "Mouse" */
263                         usbMsgPtr = (usbMsgPtr_t)
264                                 &usbDescriptorConfiguration[43];
265                 } else {
266                         /* "Keyboard" */
267                         usbMsgPtr = (usbMsgPtr_t)
268                                 &usbDescriptorConfiguration[18];
269                 }
270         } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
271                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
272                         usbMsgPtr = (usbMsgPtr_t)
273                                 &usbHidMouseReportDescriptor[0];
274                         return sizeof(usbHidMouseReportDescriptor);
275                 } else {
276                         usbMsgPtr = (usbMsgPtr_t)
277                                 &usbHidKeyboardReportDescriptor[0];
278                         return sizeof(usbHidKeyboardReportDescriptor);
279                 }
280         }
281
282         return 0;
283 }
284
285 usbMsgLen_t usbFunctionSetup(uchar data[8])
286 {
287         usbRequest_t *rq = (void *) data;
288
289         if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
290                 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
291                         /* No keys pressed, no mouse event */
292                         usbMsgPtr = (void *) &keyboard_report;
293                         keyboard_report.modifier = 0;
294                         keyboard_report.reserved = 0;
295                         keyboard_report.keycode[0] = 0;
296                         return sizeof(keyboard_report);
297                 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
298                         if (rq->wIndex.word == USB_INDEX_MOUSE) {
299                                 return (rq->wLength.word == 8) ?
300                                                 USB_NO_MSG : 0;
301                         } else {
302                                 /* Keyboard */
303                                 return (rq->wLength.word == 1) ?
304                                                 USB_NO_MSG : 0;
305                         }
306                 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
307                         usbMsgPtr = &idlerate;
308                         return 1;
309                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
310                         idlerate = rq->wValue.bytes[1];
311                 }
312         }
313
314         return 0;
315 }
316
317 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
318 {
319         if (len == 1) {
320                 PORTB |= 1 << PB1; // LED on
321                 if (data[0] != ledstate) {
322                         ledstate = data[0];
323
324                         if (ledstate & CAPS_LOCK) {
325                                 PORTB |= 1 << PB1; // LED on
326                         } else {
327                                 PORTB &= ~(1 << PB1); // LED off
328                         }
329                 }
330                 return 1;
331         } else if (len == 8) {
332                 /* Silently consume if unexpected*/
333                 if (data[0] != 1)
334                         return 8;
335                 if ((data[4] | data[5] | data[6] | data[7]) != 0)
336                         return 8;
337
338                 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
339                         /* Temperature query */
340                         memset(temp_report, 0, 8);
341                         have_temp_int = true;
342                         temp_report[0] = 0x80;
343                         temp_report[1] = 2;
344                         temp_report[2] = last_temp >> 8;
345                         temp_report[3] = last_temp & 0xFF;
346                 } else if (data[1] == 0x82 && data[2] == 0x77 &&
347                                 data[3] == 1) {
348                         /* Initialisation Query #1 */
349                         have_temp_int = true;
350                         temp_report[0] = 0x82;
351                         temp_report[1] = 1;
352                         temp_report[2] = 0;
353                 } else if (data[1] == 0x86 && data[2] == 0xff &&
354                                 data[3] == 1) {
355                         /* Initialisation Query #2 */
356                         have_temp_int = true;
357                         memcpy(temp_report, "TEMPerF1", 8);
358                 }
359
360                 return 8;
361         }
362
363         return 0;
364 }
365
366 void hadUsbReset(void)
367 {
368         /* Reset our state machine back to having nothing to send */
369         temp_state = 0;
370         have_temp_int = false;
371 }
372
373 int main(void)
374 {
375         unsigned char i;
376         uint8_t buf[9];
377         unsigned long last_temp_time = 0;
378
379         wdt_enable(WDTO_1S);
380
381         w1_setup();
382         set_serial();
383         timer_init();
384
385         usbDeviceDisconnect();
386         i = 0;
387         while (--i) {
388                 wdt_reset();
389                 _delay_ms(1);
390         }
391         usbDeviceConnect();
392
393         usbInit();
394
395         /* PB1 as output for LED */
396         DDRB |= 1 << PB1;
397
398         sei(); /* We're ready to go; enable interrupts */
399
400         keyboard_report.modifier = 0;
401         keyboard_report.reserved = 0;
402         keyboard_report.keycode[0] = 0;
403
404         while (1) {
405                 wdt_reset();
406                 usbPoll();
407
408                 if (have_temp_int && usbInterruptIsReady3()) {
409                         usbSetInterrupt3((void *) temp_report,
410                                         sizeof(temp_report));
411                         if (temp_report[0] == 'T') {
412                                 /* Hack up second response for 0x86 query */
413                                 memcpy(temp_report, ".2", 2);
414                         } else {
415                                 have_temp_int = false;
416                         }
417                 }
418
419                 if (temp_state == 1) {
420                         if (w1_reset()) {
421                                 temp_state++;
422                         } else {
423                                 temp_state = 0;
424                         }
425                 } else if (temp_state == 2) {
426                         w1_write(0xCC);         /* SKIP ROM */
427                         temp_state++;
428                 } else if (temp_state == 3) {
429                         w1_write(0x44);         /* Convert T */
430                         temp_state++;
431                 } else if (temp_state == 4) {
432                         if (w1_read_bit())
433                                 temp_state++;
434                 } else if (temp_state == 5) {
435                         if (w1_reset()) {
436                                 temp_state++;
437                         } else {
438                                 temp_state = 0;
439                         }
440                 } else if (temp_state == 6) {
441                         w1_write(0xCC);         /* SKIP ROM */
442                         temp_state++;
443                 } else if (temp_state == 7) {
444                         w1_write(0xBE);         /* Read Scratchpad */
445                         temp_state++;
446                 } else if (temp_state > 7 && temp_state < 17) {
447                         buf[temp_state - 8] = w1_read_byte();
448                         temp_state++;
449                 } else if (temp_state == 17) {
450                         last_temp = buf[1] << 12 | buf[0] << 4;
451                         temp_state = 0;
452                         last_temp_time = millis();
453                 }
454
455                 if (temp_state == 0 &&
456                                 (millis() - last_temp_time) > TEMP_INTERVAL) {
457                         temp_state = 1;
458                 }
459         }
460 }