From 3c727aad6ec52918a14a6550349a2e2ca35d0645 Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Wed, 2 May 2018 18:47:22 +0100 Subject: [PATCH] Add initial TEMPerV1.2 clone skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is a clone of the pcsensors.com TEMPerV1.2 device. At present we just lie and claim 15°C rather than supporting a temperature sensor. Tested with pcsensor-temper, temper-python + TEMPered. --- main.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++++ usbconfig.h | 34 +++--- 2 files changed, 362 insertions(+), 18 deletions(-) create mode 100644 main.c diff --git a/main.c b/main.c new file mode 100644 index 0000000..fff2ae8 --- /dev/null +++ b/main.c @@ -0,0 +1,346 @@ +/* + * Basic firmware to emulate a USB TEMPer device (which uses an FM75 I2C + * sensor internally) using a Digispark + a DS18B20 1-wire sensor. + * + * Copyright 2018 Jonathan McDowell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "usbdrv.h" +#include "libs-device/osccal.h" + +typedef struct { + uint8_t modifier; + uint8_t reserved; + uint8_t keycode[6]; +} keyboard_report_t; +keyboard_report_t keyboard_report; + +uint8_t temp_report[8]; +bool have_temp_int = false; + +#define NUM_LOCK 1 +#define CAPS_LOCK 2 +#define SCROLL_LOCK 4 +volatile static uchar ledstate = 0xff; +static uchar idlerate; + +/* We populate this with the 1-wire device ROM ID when we get it */ +int serno_str[] = { + USB_STRING_DESCRIPTOR_HEADER(16), + 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', + 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', +}; + +#define USB_INDEX_KEYBOARD 0 +#define USB_INDEX_MOUSE 1 + +PROGMEM const char usbHidKeyboardReportDescriptor[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x06, // USAGE (Keyboard) + 0xa1, 0x01, // COLLECTION (Application) + 0x85, 0x01, // REPORT_ID (1) + 0x05, 0x07, // USAGE_PAGE (Keyboard) (Key Codes) + 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)(224) + 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)(231) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x75, 0x01, // REPORT_SIZE (1) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x01, // INPUT (Cnst,Arr,Abs) ; Reserved byte + 0x95, 0x03, // REPORT_COUNT (3) + 0x75, 0x01, // REPORT_SIZE (1) + 0x05, 0x08, // USAGE_PAGE (LEDs) + 0x19, 0x01, // USAGE_MINIMUM (Num Lock) + 0x29, 0x03, // USAGE_MAXIMUM (Scroll Lock) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; LED report + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x91, 0x01, // OUTPUT (Cnst,Arr,Abs) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x08, // REPORT_SIZE (8) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0xff, // LOGICAL_MAXIMUM (255) + 0x05, 0x07, // USAGE_PAGE (Keyboard)(Key Codes) + 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event))(0) + 0x29, 0xff, // USAGE_MAXIMUM (Keyboard Application)(null) + 0x81, 0x00, // INPUT (Data,Ary,Abs) + 0xc0 // END_COLLECTION +}; + +PROGMEM const char usbHidMouseReportDescriptor[] = { + 0x06, 0x00, 0xff, // USAGE_PAGE (65280) + 0x09, 0x01, // USAGE (1) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE (1) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x08, // REPORT_COUNT (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) ; Modifier byte + 0x09, 0x01, // USAGE (Pointer) + 0x95, 0x08, // REPORT_COUNT (8) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) ; Modifier byte + 0x05, 0x0c, // USAGE_PAGE (Consumer) + 0x09, 0x00, // USAGE (0) + 0x15, 0x80, // LOGICAL_MINIMUM (128) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x08, // REPORT_COUNT (8) + 0xb1, 0x02, // FEATURE (Data,Var,Abs) + 0xc0 // END_COLLECTION +}; + +/* USB configuration descriptor */ +PROGMEM const char usbDescriptorConfiguration[] = { + 9, /* sizeof(usbDescriptorConfiguration) */ + USBDESCR_CONFIG, /* descriptor type */ + /* total length of data returned (including inlined descriptors) */ + 18 + 9 + 9 + 7 + 9 + 7, 0, + 2, /* number of interfaces in this cfg */ + 1, /* index of this configuration */ + 0, /* configuration name string index */ + (1 << 7) | USBATTR_REMOTEWAKE, /* attributes */ + USB_CFG_MAX_BUS_POWER/2, /* max USB current in 2mA units */ + + /* Keyboard interface descriptor follows inline: */ + + /* sizeof(usbDescrInterface): length of descriptor in bytes */ + 9, + USBDESCR_INTERFACE, /* descriptor type */ + 0, /* index of this interface */ + 0, /* alternate setting for this interface */ + /* endpoints excl 0: number of endpoint descriptors to follow */ + 1, + 3, /* Interface class: HID */ + 1, /* SubClass: Boot Interface */ + 1, /* Interface protocol: Keyboard */ + 0, /* string index for interface */ + 9, /* sizeof(usbDescrHID) */ + USBDESCR_HID, /* descriptor type: HID */ + 0x10, 0x01, /* BCD representation of HID version */ + 0x00, /* target country code */ + /* number of HID Report (or other HID class) Descriptors to follow */ + 0x01, + 0x22, /* descriptor type: report */ + sizeof(usbHidKeyboardReportDescriptor), + 0, /* total length of report descriptor */ + + /* endpoint descriptor for endpoint 1 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + (char)0x81, /* IN endpoint number 1 */ + 0x03, /* attrib: Interrupt endpoint */ + 8, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ + + /* Mouse interface descriptor follows inline: */ + + /* sizeof(usbDescrInterface): length of descriptor in bytes */ + 9, + USBDESCR_INTERFACE, /* descriptor type */ + 1, /* index of this interface */ + 0, /* alternate setting for this interface */ + /* endpoints excl 0: number of endpoint descriptors to follow */ + 1, + 3, /* Interface class: HID */ + 1, /* SubClass: Boot Interface */ + 2, /* Interface protocol: Mouse */ + 0, /* string index for interface */ + 9, /* sizeof(usbDescrHID) */ + USBDESCR_HID, /* descriptor type: HID */ + 0x10, 0x01, /* BCD representation of HID version */ + 0x00, /* target country code */ + /* number of HID Report (or other HID class) Descriptors to follow */ + 0x01, + 0x22, /* descriptor type: report */ + /* total length of report descriptor */ + sizeof(usbHidMouseReportDescriptor), 0, + + /* endpoint descriptor for endpoint 2 */ + 7, /* sizeof(usbDescrEndpoint) */ + USBDESCR_ENDPOINT, /* descriptor type = endpoint */ + (char)0x82, /* IN endpoint number 2 */ + 0x03, /* attrib: Interrupt endpoint */ + 8, 0, /* maximum packet size */ + USB_CFG_INTR_POLL_INTERVAL, /* in ms */ +}; + +usbMsgLen_t usbFunctionDescriptor(usbRequest_t *rq) +{ + if (rq->wValue.bytes[1] == USBDESCR_STRING && + rq->wValue.bytes[0] == 3) { + usbMsgPtr = (usbMsgPtr_t) serno_str; + return sizeof(serno_str); + } else if (rq->wValue.bytes[1] == USBDESCR_HID) { + if (rq->wIndex.word == USB_INDEX_MOUSE) { + /* "Mouse" */ + usbMsgPtr = (usbMsgPtr_t) + &usbDescriptorConfiguration[43]; + } else { + /* "Keyboard" */ + usbMsgPtr = (usbMsgPtr_t) + &usbDescriptorConfiguration[18]; + } + } else if (rq->wValue.bytes[1] == USBDESCR_HID_REPORT) { + if (rq->wIndex.word == USB_INDEX_MOUSE) { + usbMsgPtr = (usbMsgPtr_t) + &usbHidMouseReportDescriptor[0]; + return sizeof(usbHidMouseReportDescriptor); + } else { + usbMsgPtr = (usbMsgPtr_t) + &usbHidKeyboardReportDescriptor[0]; + return sizeof(usbHidKeyboardReportDescriptor); + } + } + + return 0; +} + +usbMsgLen_t usbFunctionSetup(uchar data[8]) +{ + usbRequest_t *rq = (void *) data; + + if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { + if (rq->bRequest == USBRQ_HID_GET_REPORT) { + /* No keys pressed, no mouse event */ + usbMsgPtr = (void *) &keyboard_report; + keyboard_report.modifier = 0; + keyboard_report.reserved = 0; + keyboard_report.keycode[0] = 0; + return sizeof(keyboard_report); + } else if (rq->bRequest == USBRQ_HID_SET_REPORT) { + if (rq->wIndex.word == USB_INDEX_MOUSE) { + return (rq->wLength.word == 8) ? + USB_NO_MSG : 0; + } else { + /* Keyboard */ + return (rq->wLength.word == 1) ? + USB_NO_MSG : 0; + } + } else if (rq->bRequest == USBRQ_HID_GET_IDLE) { + usbMsgPtr = &idlerate; + return 1; + } else if (rq->bRequest == USBRQ_HID_SET_IDLE) { + idlerate = rq->wValue.bytes[1]; + } + } + + return 0; +} + +usbMsgLen_t usbFunctionWrite(uint8_t * data, uchar len) +{ + if (len == 1) { + if (data[0] != ledstate) { + ledstate = data[0]; + + if (ledstate & CAPS_LOCK) { + PORTB |= 1 << PB1; // LED on + } else { + PORTB &= ~(1 << PB1); // LED off + } + } + return 1; + } else if (len == 8) { + /* Silently consume if unexpected*/ + if (data[0] != 1) + return 8; + if ((data[4] | data[5] | data[6] | data[7]) != 0) + return 8; + + if (data[1] == 0x80 && data[2] == 0x33 && data[3] == 1) { + /* Temperature query */ + have_temp_int = true; + temp_report[0] = 0x80; + temp_report[1] = 2; + /* Fake 15°C */ + temp_report[2] = 0xF; + temp_report[3] = 0x0; + + } else if (data[1] == 0x82 && data[2] == 0x77 && + data[3] == 1) { + /* Initialisation Query #1 */ + have_temp_int = true; + temp_report[0] = 0x82; + temp_report[1] = 1; + temp_report[2] = 0; + } else if (data[1] == 0x86 && data[2] == 0xff && + data[3] == 1) { + /* Initialisation Query #2 */ + have_temp_int = true; + memcpy(temp_report, "TEMPerF1", 8); + } + + return 8; + } + + return 0; +} + +int main(void) +{ + unsigned char i; + + wdt_enable(WDTO_1S); + + usbInit(); + usbDeviceDisconnect(); + + i = 0; + while (--i) { + wdt_reset(); + _delay_ms(1); + } + + usbDeviceConnect(); + + /* PB1 as output for LED */ + DDRB |= 1 << PB1; + + sei(); /* We're ready to go; enable interrupts */ + + keyboard_report.modifier = 0; + keyboard_report.reserved = 0; + keyboard_report.keycode[0] = 0; + + while (1) { + wdt_reset(); + usbPoll(); + + if (have_temp_int && usbInterruptIsReady3()) { + usbSetInterrupt3((void *) temp_report, + sizeof(temp_report)); + if (temp_report[0] == 'T') { + /* Hack up second response for 0x86 query */ + memcpy(temp_report, ".2", 2); + } else { + have_temp_int = false; + } + } + } +} diff --git a/usbconfig.h b/usbconfig.h index 207af65..d6166f4 100644 --- a/usbconfig.h +++ b/usbconfig.h @@ -76,18 +76,18 @@ section at the end of this file). /* --------------------------- Functional Range ---------------------------- */ -#define USB_CFG_HAVE_INTRIN_ENDPOINT 0 +#define USB_CFG_HAVE_INTRIN_ENDPOINT 1 /* Define this to 1 if you want to compile a version with two endpoints: The * default control endpoint 0 and an interrupt-in endpoint (any other endpoint * number). */ -#define USB_CFG_HAVE_INTRIN_ENDPOINT3 0 +#define USB_CFG_HAVE_INTRIN_ENDPOINT3 1 /* Define this to 1 if you want to compile a version with three endpoints: The * default control endpoint 0, an interrupt-in endpoint 3 (or the number * configured below) and a catch-all default interrupt-in endpoint as above. * You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature. */ -#define USB_CFG_EP3_NUMBER 3 +#define USB_CFG_EP3_NUMBER 2 /* If the so-called endpoint 3 is used, it can now be configured to any other * endpoint number (except 0) with this macro. Default if undefined is 3. */ @@ -111,7 +111,7 @@ section at the end of this file). * (e.g. HID), but never want to send any data. This option saves a couple * of bytes in flash memory and the transmit buffers in RAM. */ -#define USB_CFG_INTR_POLL_INTERVAL 10 +#define USB_CFG_INTR_POLL_INTERVAL 20 /* If you compile a version with endpoint 1 (interrupt-in), this is the poll * interval. The value is in milliseconds and must not be less than 10 ms for * low speed devices. @@ -125,7 +125,7 @@ section at the end of this file). * The value is in milliamperes. [It will be divided by two since USB * communicates power requirements in units of 2 mA.] */ -#define USB_CFG_IMPLEMENT_FN_WRITE 0 +#define USB_CFG_IMPLEMENT_FN_WRITE 1 /* Set this to 1 if you want usbFunctionWrite() to be called for control-out * transfers. Set it to 0 if you don't need it and want to save a couple of * bytes. @@ -209,7 +209,7 @@ section at the end of this file). /* define this macro to 1 if you want the function usbMeasureFrameLength() * compiled in. This function can be used to calibrate the AVR's RC oscillator. */ -#define USB_USE_FAST_CRC 0 +#define USB_USE_FAST_CRC 1 /* The assembler module has two implementations for the CRC algorithm. One is * faster, the other is smaller. This CRC routine is only used for transmitted * messages where timing is not critical. The faster routine needs 31 cycles @@ -220,7 +220,7 @@ section at the end of this file). /* -------------------------- Device Description --------------------------- */ -#define USB_CFG_VENDOR_ID 0xc0, 0x16 /* = 0x16c0 = 5824 = voti.nl */ +#define USB_CFG_VENDOR_ID 0x45, 0x0c /* USB vendor ID for the device, low byte first. If you have registered your * own Vendor ID, define it here. Otherwise you may use one of obdev's free * shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules! @@ -229,7 +229,7 @@ section at the end of this file). * with libusb: 0x16c0/0x5dc. Use this VID/PID pair ONLY if you understand * the implications! */ -#define USB_CFG_DEVICE_ID 0xdc, 0x05 /* = 0x05dc = 1500 */ +#define USB_CFG_DEVICE_ID 0x01, 0x74 /* This is the ID of the product, low byte first. It is interpreted in the * scope of the vendor ID. If you have registered your own VID with usb.org * or if you have licensed a PID from somebody else, define it here. Otherwise @@ -243,7 +243,7 @@ section at the end of this file). #define USB_CFG_DEVICE_VERSION 0x00, 0x01 /* Version number of the device: Minor number first, then major number. */ -#define USB_CFG_VENDOR_NAME 'o', 'b', 'd', 'e', 'v', '.', 'a', 't' +#define USB_CFG_VENDOR_NAME 'e', 'a', 'r', 't', 'h', '.', 'l', 'i' #define USB_CFG_VENDOR_NAME_LEN 8 /* These two values define the vendor name returned by the USB device. The name * must be given as a list of characters under single quotes. The characters @@ -253,8 +253,8 @@ section at the end of this file). * obdev's free shared VID/PID pair. See the file USB-IDs-for-free.txt for * details. */ -#define USB_CFG_DEVICE_NAME 'T', 'e', 'm', 'p', 'l', 'a', 't', 'e' -#define USB_CFG_DEVICE_NAME_LEN 8 +#define USB_CFG_DEVICE_NAME 'T', 'E', 'M', 'P', 'e', 'r', 'V', '1', '.', '2' +#define USB_CFG_DEVICE_NAME_LEN 10 /* Same as above for the device name. If you don't want a device name, undefine * the macros. See the file USB-IDs-for-free.txt before you assign a name if * you use a shared VID/PID. @@ -268,12 +268,12 @@ section at the end of this file). * to fine tune control over USB descriptors such as the string descriptor * for the serial number. */ -#define USB_CFG_DEVICE_CLASS 0xff /* set to 0 if deferred to interface */ +#define USB_CFG_DEVICE_CLASS 0 #define USB_CFG_DEVICE_SUBCLASS 0 /* See USB specification if you want to conform to an existing device class. * Class 0xff is "vendor specific". */ -#define USB_CFG_INTERFACE_CLASS 0 /* define class here if not at device level */ +#define USB_CFG_INTERFACE_CLASS 0 /* At device level */ #define USB_CFG_INTERFACE_SUBCLASS 0 #define USB_CFG_INTERFACE_PROTOCOL 0 /* See USB specification if you want to conform to an existing device class or @@ -346,18 +346,16 @@ section at the end of this file). * }; */ -#define CONFIG_DESCRIPTOR_SIZE 41 - #define USB_CFG_DESCR_PROPS_DEVICE 0 -#define USB_CFG_DESCR_PROPS_CONFIGURATION 0 +#define USB_CFG_DESCR_PROPS_CONFIGURATION (9 + 2 * (9 + 9 + 7)) #define USB_CFG_DESCR_PROPS_STRINGS 0 #define USB_CFG_DESCR_PROPS_STRING_0 0 #define USB_CFG_DESCR_PROPS_STRING_VENDOR 0 #define USB_CFG_DESCR_PROPS_STRING_PRODUCT 0 -#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER 0 +#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER (USB_PROP_IS_DYNAMIC | USB_PROP_IS_RAM) #define USB_CFG_DESCR_PROPS_HID 0 #define USB_CFG_DESCR_PROPS_HID_REPORT 0 -#define USB_CFG_DESCR_PROPS_UNKNOWN 0 +#define USB_CFG_DESCR_PROPS_UNKNOWN 0xFFFF //#define usbMsgPtr_t unsigned short -- 2.39.2