]> the.earth.li Git - temper-clone.git/blob - main.c
Remove unused read_temp() function
[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 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
225 {
226         if (rq->wValue.bytes[1] == USBDESCR_STRING &&
227                         rq->wValue.bytes[0] == 3) {
228                 usbMsgPtr = (usbMsgPtr_t) serno_str;
229                 return sizeof(serno_str);
230         } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
231                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
232                         /* "Mouse" */
233                         usbMsgPtr = (usbMsgPtr_t)
234                                 &usbDescriptorConfiguration[43];
235                 } else {
236                         /* "Keyboard" */
237                         usbMsgPtr = (usbMsgPtr_t)
238                                 &usbDescriptorConfiguration[18];
239                 }
240         } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
241                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
242                         usbMsgPtr = (usbMsgPtr_t)
243                                 &usbHidMouseReportDescriptor[0];
244                         return sizeof(usbHidMouseReportDescriptor);
245                 } else {
246                         usbMsgPtr = (usbMsgPtr_t)
247                                 &usbHidKeyboardReportDescriptor[0];
248                         return sizeof(usbHidKeyboardReportDescriptor);
249                 }
250         }
251
252         return 0;
253 }
254
255 usbMsgLen_t usbFunctionSetup(uchar data[8])
256 {
257         usbRequest_t *rq = (void *) data;
258
259         if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
260                 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
261                         /* No keys pressed, no mouse event */
262                         usbMsgPtr = (void *) &keyboard_report;
263                         keyboard_report.modifier = 0;
264                         keyboard_report.reserved = 0;
265                         keyboard_report.keycode[0] = 0;
266                         return sizeof(keyboard_report);
267                 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
268                         if (rq->wIndex.word == USB_INDEX_MOUSE) {
269                                 return (rq->wLength.word == 8) ?
270                                                 USB_NO_MSG : 0;
271                         } else {
272                                 /* Keyboard */
273                                 return (rq->wLength.word == 1) ?
274                                                 USB_NO_MSG : 0;
275                         }
276                 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
277                         usbMsgPtr = &idlerate;
278                         return 1;
279                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
280                         idlerate = rq->wValue.bytes[1];
281                 }
282         }
283
284         return 0;
285 }
286
287 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
288 {
289         if (len == 1) {
290                 PORTB |= 1 << PB1; // LED on
291                 if (data[0] != ledstate) {
292                         ledstate = data[0];
293
294                         if (ledstate & CAPS_LOCK) {
295                                 PORTB |= 1 << PB1; // LED on
296                         } else {
297                                 PORTB &= ~(1 << PB1); // LED off
298                         }
299                 }
300                 return 1;
301         } else if (len == 8) {
302                 /* Silently consume if unexpected*/
303                 if (data[0] != 1)
304                         return 8;
305                 if ((data[4] | data[5] | data[6] | data[7]) != 0)
306                         return 8;
307
308                 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
309                         /* Temperature query */
310                         memset(temp_report, 0, 8);
311                         have_temp_int = true;
312                         temp_report[0] = 0x80;
313                         temp_report[1] = 2;
314                         temp_report[2] = last_temp >> 8;
315                         temp_report[3] = last_temp & 0xFF;
316                 } else if (data[1] == 0x82 && data[2] == 0x77 &&
317                                 data[3] == 1) {
318                         /* Initialisation Query #1 */
319                         have_temp_int = true;
320                         temp_report[0] = 0x82;
321                         temp_report[1] = 1;
322                         temp_report[2] = 0;
323                 } else if (data[1] == 0x86 && data[2] == 0xff &&
324                                 data[3] == 1) {
325                         /* Initialisation Query #2 */
326                         have_temp_int = true;
327                         memcpy(temp_report, "TEMPerF1", 8);
328                 }
329
330                 return 8;
331         }
332
333         return 0;
334 }
335
336 void hadUsbReset(void)
337 {
338         /* Reset our state machine back to having nothing to send */
339         temp_state = 0;
340         have_temp_int = false;
341 }
342
343 int main(void)
344 {
345         unsigned char i;
346         uint8_t buf[9];
347         unsigned long last_temp_time = 0;
348
349         wdt_enable(WDTO_1S);
350
351         w1_setup();
352         set_serial();
353         timer_init();
354
355         usbDeviceDisconnect();
356         i = 0;
357         while (--i) {
358                 wdt_reset();
359                 _delay_ms(1);
360         }
361         usbDeviceConnect();
362
363         usbInit();
364
365         /* PB1 as output for LED */
366         DDRB |= 1 << PB1;
367
368         sei(); /* We're ready to go; enable interrupts */
369
370         keyboard_report.modifier = 0;
371         keyboard_report.reserved = 0;
372         keyboard_report.keycode[0] = 0;
373
374         while (1) {
375                 wdt_reset();
376                 usbPoll();
377
378                 if (have_temp_int && usbInterruptIsReady3()) {
379                         usbSetInterrupt3((void *) temp_report,
380                                         sizeof(temp_report));
381                         if (temp_report[0] == 'T') {
382                                 /* Hack up second response for 0x86 query */
383                                 memcpy(temp_report, ".2", 2);
384                         } else {
385                                 have_temp_int = false;
386                         }
387                 }
388
389                 if (temp_state == 1) {
390                         if (w1_reset()) {
391                                 temp_state++;
392                         } else {
393                                 temp_state = 0;
394                         }
395                 } else if (temp_state == 2) {
396                         w1_write(0xCC);         /* SKIP ROM */
397                         temp_state++;
398                 } else if (temp_state == 3) {
399                         w1_write(0x44);         /* Convert T */
400                         temp_state++;
401                 } else if (temp_state == 4) {
402                         if (w1_read_bit())
403                                 temp_state++;
404                 } else if (temp_state == 5) {
405                         if (w1_reset()) {
406                                 temp_state++;
407                         } else {
408                                 temp_state = 0;
409                         }
410                 } else if (temp_state == 6) {
411                         w1_write(0xCC);         /* SKIP ROM */
412                         temp_state++;
413                 } else if (temp_state == 7) {
414                         w1_write(0xBE);         /* Read Scratchpad */
415                         temp_state++;
416                 } else if (temp_state > 7 && temp_state < 17) {
417                         buf[temp_state - 8] = w1_read_byte();
418                         temp_state++;
419                 } else if (temp_state == 17) {
420                         last_temp = buf[1] << 12 | buf[0] << 4;
421                         temp_state = 0;
422                         last_temp_time = millis();
423                 }
424
425                 if (temp_state == 0 &&
426                                 (millis() - last_temp_time) > TEMP_INTERVAL) {
427                         temp_state = 1;
428                 }
429         }
430 }