]> the.earth.li Git - temper-clone.git/blob - main.c
Add initial TEMPerV1.2 clone skeleton
[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 typedef struct {
34         uint8_t modifier;
35         uint8_t reserved;
36         uint8_t keycode[6];
37 } keyboard_report_t;
38 keyboard_report_t keyboard_report;
39
40 uint8_t temp_report[8];
41 bool have_temp_int = false;
42
43 #define NUM_LOCK        1
44 #define CAPS_LOCK       2
45 #define SCROLL_LOCK     4
46 volatile static uchar ledstate = 0xff;
47 static uchar idlerate;
48
49 /* We populate this with the 1-wire device ROM ID when we get it */
50 int serno_str[] = {
51         USB_STRING_DESCRIPTOR_HEADER(16),
52         'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
53         'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F',
54 };
55
56 #define USB_INDEX_KEYBOARD      0
57 #define USB_INDEX_MOUSE         1
58
59 PROGMEM const char usbHidKeyboardReportDescriptor[] = {
60         0x05, 0x01,             // USAGE_PAGE (Generic Desktop)
61         0x09, 0x06,             // USAGE (Keyboard)
62         0xa1, 0x01,             // COLLECTION (Application)
63         0x85, 0x01,             //   REPORT_ID (1)
64         0x05, 0x07,             //   USAGE_PAGE (Keyboard) (Key Codes)
65         0x19, 0xe0,             //   USAGE_MINIMUM (Keyboard LeftControl)(224)
66         0x29, 0xe7,             //   USAGE_MAXIMUM (Keyboard Right GUI)(231)
67         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
68         0x25, 0x01,             //   LOGICAL_MAXIMUM (1)
69         0x75, 0x01,             //   REPORT_SIZE (1)
70         0x95, 0x08,             //   REPORT_COUNT (8)
71         0x81, 0x02,             //   INPUT (Data,Var,Abs) ; Modifier byte
72         0x95, 0x01,             //   REPORT_COUNT (1)
73         0x75, 0x08,             //   REPORT_SIZE (8)
74         0x81, 0x01,             //   INPUT (Cnst,Arr,Abs) ; Reserved byte
75         0x95, 0x03,             //   REPORT_COUNT (3)
76         0x75, 0x01,             //   REPORT_SIZE (1)
77         0x05, 0x08,             //   USAGE_PAGE (LEDs)
78         0x19, 0x01,             //   USAGE_MINIMUM (Num Lock)
79         0x29, 0x03,             //   USAGE_MAXIMUM (Scroll Lock)
80         0x91, 0x02,             //   OUTPUT (Data,Var,Abs) ; LED report
81         0x95, 0x05,             //   REPORT_COUNT (5)
82         0x75, 0x01,             //   REPORT_SIZE (1)
83         0x91, 0x01,             //   OUTPUT (Cnst,Arr,Abs)
84         0x95, 0x05,             //   REPORT_COUNT (5)
85         0x75, 0x08,             //   REPORT_SIZE (8)
86         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
87         0x25, 0xff,             //   LOGICAL_MAXIMUM (255)
88         0x05, 0x07,             //   USAGE_PAGE (Keyboard)(Key Codes)
89         0x19, 0x00,             //   USAGE_MINIMUM (Reserved (no event))(0)
90         0x29, 0xff,             //   USAGE_MAXIMUM (Keyboard Application)(null)
91         0x81, 0x00,             //   INPUT (Data,Ary,Abs)
92         0xc0                    // END_COLLECTION
93 };
94
95 PROGMEM const char usbHidMouseReportDescriptor[] = {
96         0x06, 0x00, 0xff,       // USAGE_PAGE (65280)
97         0x09, 0x01,             // USAGE (1)
98         0xa1, 0x01,             // COLLECTION (Application)
99         0x09, 0x01,             // USAGE (1)
100         0x15, 0x00,             //   LOGICAL_MINIMUM (0)
101         0x26, 0xff, 0x00,       //   LOGICAL_MAXIMUM (255)
102         0x75, 0x08,             //   REPORT_SIZE (8)
103         0x95, 0x08,             //   REPORT_COUNT (8)
104         0x81, 0x02,             //   INPUT (Data,Var,Abs) ; Modifier byte
105         0x09, 0x01,             // USAGE (Pointer)
106         0x95, 0x08,             //   REPORT_COUNT (8)
107         0x91, 0x02,             //   OUTPUT (Data,Var,Abs) ; Modifier byte
108         0x05, 0x0c,             // USAGE_PAGE (Consumer)
109         0x09, 0x00,             //   USAGE (0)
110         0x15, 0x80,             //   LOGICAL_MINIMUM (128)
111         0x25, 0x7f,             //   LOGICAL_MAXIMUM (127)
112         0x75, 0x08,             //   REPORT_SIZE (8)
113         0x95, 0x08,             //   REPORT_COUNT (8)
114         0xb1, 0x02,             //   FEATURE (Data,Var,Abs)
115         0xc0                    // END_COLLECTION
116 };
117
118 /* USB configuration descriptor */
119 PROGMEM const char usbDescriptorConfiguration[] = {
120         9,                              /* sizeof(usbDescriptorConfiguration) */
121         USBDESCR_CONFIG,                /* descriptor type */
122         /* total length of data returned (including inlined descriptors) */
123         18 + 9 + 9 + 7 + 9 + 7, 0,
124         2,                              /* number of interfaces in this cfg */
125         1,                              /* index of this configuration */
126         0,                              /* configuration name string index */
127         (1 << 7) | USBATTR_REMOTEWAKE,  /* attributes */
128         USB_CFG_MAX_BUS_POWER/2,        /* max USB current in 2mA units */
129
130         /* Keyboard interface descriptor follows inline: */
131
132         /* sizeof(usbDescrInterface): length of descriptor in bytes */
133         9,
134         USBDESCR_INTERFACE,     /* descriptor type */
135         0,                      /* index of this interface */
136         0,                      /* alternate setting for this interface */
137         /* endpoints excl 0: number of endpoint descriptors to follow */
138         1,
139         3,                      /* Interface class: HID */
140         1,                      /* SubClass: Boot Interface */
141         1,                      /* Interface protocol: Keyboard */
142         0,                      /* string index for interface */
143         9,                      /* sizeof(usbDescrHID) */
144         USBDESCR_HID,           /* descriptor type: HID */
145         0x10, 0x01,             /* BCD representation of HID version */
146         0x00,                   /* target country code */
147         /* number of HID Report (or other HID class) Descriptors to follow */
148         0x01,
149         0x22,                   /* descriptor type: report */
150         sizeof(usbHidKeyboardReportDescriptor),
151         0,              /* total length of report descriptor */
152
153         /* endpoint descriptor for endpoint 1 */
154         7,                              /* sizeof(usbDescrEndpoint) */
155         USBDESCR_ENDPOINT,              /* descriptor type = endpoint */
156         (char)0x81,                     /* IN endpoint number 1 */
157         0x03,                           /* attrib: Interrupt endpoint */
158         8, 0,                           /* maximum packet size */
159         USB_CFG_INTR_POLL_INTERVAL,     /* in ms */
160
161         /* Mouse interface descriptor follows inline: */
162
163         /* sizeof(usbDescrInterface): length of descriptor in bytes */
164         9,
165         USBDESCR_INTERFACE,     /* descriptor type */
166         1,                      /* index of this interface */
167         0,                      /* alternate setting for this interface */
168         /* endpoints excl 0: number of endpoint descriptors to follow */
169         1,
170         3,                      /* Interface class: HID */
171         1,                      /* SubClass: Boot Interface */
172         2,                      /* Interface protocol: Mouse */
173         0,                      /* string index for interface */
174         9,                      /* sizeof(usbDescrHID) */
175         USBDESCR_HID,           /* descriptor type: HID */
176         0x10, 0x01,             /* BCD representation of HID version */
177         0x00,                   /* target country code */
178         /* number of HID Report (or other HID class) Descriptors to follow */
179         0x01,
180         0x22,                   /* descriptor type: report */
181         /* total length of report descriptor */
182         sizeof(usbHidMouseReportDescriptor), 0,
183
184         /* endpoint descriptor for endpoint 2 */
185         7,                              /* sizeof(usbDescrEndpoint) */
186         USBDESCR_ENDPOINT,              /* descriptor type = endpoint */
187         (char)0x82,                     /* IN endpoint number 2 */
188         0x03,                           /* attrib: Interrupt endpoint */
189         8, 0,                           /* maximum packet size */
190         USB_CFG_INTR_POLL_INTERVAL,     /* in ms */
191 };
192
193 usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq)
194 {
195         if (rq->wValue.bytes[1] == USBDESCR_STRING &&
196                         rq->wValue.bytes[0] == 3) {
197                 usbMsgPtr = (usbMsgPtr_t) serno_str;
198                 return sizeof(serno_str);
199         } else if (rq->wValue.bytes[1] == USBDESCR_HID) {
200                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
201                         /* "Mouse" */
202                         usbMsgPtr = (usbMsgPtr_t)
203                                 &usbDescriptorConfiguration[43];
204                 } else {
205                         /* "Keyboard" */
206                         usbMsgPtr = (usbMsgPtr_t)
207                                 &usbDescriptorConfiguration[18];
208                 }
209         } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) {
210                 if (rq->wIndex.word == USB_INDEX_MOUSE) {
211                         usbMsgPtr = (usbMsgPtr_t)
212                                 &usbHidMouseReportDescriptor[0];
213                         return sizeof(usbHidMouseReportDescriptor);
214                 } else {
215                         usbMsgPtr = (usbMsgPtr_t)
216                                 &usbHidKeyboardReportDescriptor[0];
217                         return sizeof(usbHidKeyboardReportDescriptor);
218                 }
219         }
220
221         return 0;
222 }
223
224 usbMsgLen_t usbFunctionSetup(uchar data[8])
225 {
226         usbRequest_t *rq = (void *) data;
227
228         if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) {
229                 if (rq->bRequest == USBRQ_HID_GET_REPORT) {
230                         /* No keys pressed, no mouse event */
231                         usbMsgPtr = (void *) &keyboard_report;
232                         keyboard_report.modifier = 0;
233                         keyboard_report.reserved = 0;
234                         keyboard_report.keycode[0] = 0;
235                         return sizeof(keyboard_report);
236                 } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
237                         if (rq->wIndex.word == USB_INDEX_MOUSE) {
238                                 return (rq->wLength.word == 8) ?
239                                                 USB_NO_MSG : 0;
240                         } else {
241                                 /* Keyboard */
242                                 return (rq->wLength.word == 1) ?
243                                                 USB_NO_MSG : 0;
244                         }
245                 } else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
246                         usbMsgPtr = &idlerate;
247                         return 1;
248                 } else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
249                         idlerate = rq->wValue.bytes[1];
250                 }
251         }
252
253         return 0;
254 }
255
256 usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len)
257 {
258         if (len == 1) {
259                 if (data[0] != ledstate) {
260                         ledstate = data[0];
261
262                         if (ledstate & CAPS_LOCK) {
263                                 PORTB |= 1 << PB1; // LED on
264                         } else {
265                                 PORTB &= ~(1 << PB1); // LED off
266                         }
267                 }
268                 return 1;
269         } else if (len == 8) {
270                 /* Silently consume if unexpected*/
271                 if (data[0] != 1)
272                         return 8;
273                 if ((data[4] | data[5] | data[6] | data[7]) != 0)
274                         return 8;
275
276                 if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) {
277                         /* Temperature query */
278                         have_temp_int = true;
279                         temp_report[0] = 0x80;
280                         temp_report[1] = 2;
281                         /* Fake 15°C */
282                         temp_report[2] = 0xF;
283                         temp_report[3] = 0x0;
284
285                 } else if (data[1] == 0x82 && data[2] == 0x77 &&
286                                 data[3] == 1) {
287                         /* Initialisation Query #1 */
288                         have_temp_int = true;
289                         temp_report[0] = 0x82;
290                         temp_report[1] = 1;
291                         temp_report[2] = 0;
292                 } else if (data[1] == 0x86 && data[2] == 0xff &&
293                                 data[3] == 1) {
294                         /* Initialisation Query #2 */
295                         have_temp_int = true;
296                         memcpy(temp_report, "TEMPerF1", 8);
297                 }
298
299                 return 8;
300         }
301
302         return 0;
303 }
304
305 int main(void)
306 {
307         unsigned char i;
308
309         wdt_enable(WDTO_1S);
310
311         usbInit();
312         usbDeviceDisconnect();
313
314         i = 0;
315         while (--i) {
316                 wdt_reset();
317                 _delay_ms(1);
318         }
319
320         usbDeviceConnect();
321
322         /* PB1 as output for LED */
323         DDRB |= 1 << PB1;
324
325         sei(); /* We're ready to go; enable interrupts */
326
327         keyboard_report.modifier = 0;
328         keyboard_report.reserved = 0;
329         keyboard_report.keycode[0] = 0;
330
331         while (1) {
332                 wdt_reset();
333                 usbPoll();
334
335                 if (have_temp_int && usbInterruptIsReady3()) {
336                         usbSetInterrupt3((void *) temp_report,
337                                         sizeof(temp_report));
338                         if (temp_report[0] == 'T') {
339                                 /* Hack up second response for 0x86 query */
340                                 memcpy(temp_report, ".2", 2);
341                         } else {
342                                 have_temp_int = false;
343                         }
344                 }
345         }
346 }