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