/*------------------------------------------------------------------------- * trinket.c * * Copyright (c) 2016, Teodor Sigaev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *------------------------------------------------------------------------- */ #include #include #include #include #include #define packed_struct __attribute__((packed)) typedef enum { CMD_PING = 0, CMD_VCC_VALUE = 1, CMD_VCC_ALARM = 2, CMD_CLICK_DELAY = 3, CMD_ALARM_LEVELS = 4, CMD_RTC = 5, CMD_DAY_ARCH = 6, CMD_YEAR_ARCH = 7, CMD_CUM_DOSE = 8, CMD_INVOKE_BSL = 9 } TrinketCommand; #define BEGIN_COMMAND (0x1B) #define END_COMMAND (0x17) #define ACK_ANSWER_OK (0x06) #define ACK_ANSWER_ERR (0x15) #define END_ANSWER (0x17) /* http://uglyduck.ath.cx/PDF/TexasInstruments/MSP430/usblib430/Programmers_Guide_MSP430_USB_API.pdf */ #define REPORT_ID (0x3f) typedef struct packed_struct { uint8_t reportId; uint8_t size; } ProtoHeader; #define DEV_TIMEOUT (5000 /* milliseconds */) typedef struct { /* * Command desc */ uint8_t cmdId; uint8_t cmdLen; /* 0 means variable, > 0 fixed */ /* * Answer desc */ uint32_t respLen; /* 0 means variable, > 0 fixed */ } TrinketProtoDesc; /* hardcoded knowledge from docs/geyger.pdf */ static const TrinketProtoDesc protoDesc[] = { {CMD_PING, 4, 5 }, {CMD_VCC_VALUE, 4, 8 }, {CMD_VCC_ALARM, 8, 8 }, {CMD_CLICK_DELAY, 8, 8 }, {CMD_ALARM_LEVELS, 24, 24 }, {CMD_RTC, 13, 13 }, {CMD_DAY_ARCH, 4, 4 + 1440 * 2 }, {CMD_YEAR_ARCH, 4, 4 + 5840 * 2 }, {CMD_CUM_DOSE, 4, 12 }, {CMD_INVOKE_BSL, 4, 0 } }; static hid_device *trinketDev = NULL; #if 0 static void dumpBuf(char *msg, uint8_t *buf, int len) { int i; fprintf(stderr,"%s:", msg); for (i=0; icmdLen}, BEGIN_COMMAND, proto->cmdLen, proto->cmdId, END_COMMAND }; struct packed_struct { ProtoHeader header; uint8_t begin; uint16_t len; uint8_t payload /* = 0 */; uint8_t end; } resp; int r; assert(proto->cmdId == CMD_PING); assert(sizeof(cmd) == cmd.len + sizeof(ProtoHeader)); assert(sizeof(resp) == proto->respLen + sizeof(ProtoHeader)); r = hid_write(trinketDev, (unsigned char*)&cmd, sizeof(cmd)); if (r < 0 || r != sizeof(cmd)) { fprintf(stderr, "hid_write returns %d instead of %u\n", r, cmd.len); return ERR_ERROR; } /* must be assigned to 0x3F by the host */ resp.header.reportId = REPORT_ID; r = hid_read_timeout(trinketDev, (unsigned char*)&resp, sizeof(resp), DEV_TIMEOUT); if (r < 0) { fprintf(stderr, "hid_read_timeout fails\n"); return ERR_NO_ANSWER; } if (r != sizeof(resp)) { fprintf(stderr, "hid_read_timeout returns %d instead of %u\n", r, (unsigned int)sizeof(resp)); return ERR_ERROR; } if (resp.header.reportId != REPORT_ID) { fprintf(stderr, "hid_read_timeout returns report id %02x instead of %02x\n", resp.header.reportId, REPORT_ID); return ERR_ERROR; } if (resp.header.size != proto->respLen) { fprintf(stderr, "hid_read_timeout returns length %u intsead if %u\n", resp.header.size, proto->respLen); return ERR_PROTO; } if (resp.len != proto->respLen) { fprintf(stderr, "hid_read_timeout response length %u instead of %u\n", resp.len, proto->respLen); return ERR_PROTO; } if (!(resp.begin == ACK_ANSWER_OK && resp.payload == 0 && resp.end == END_ANSWER)) { fprintf(stderr, "hid_read_timeout ACK %02x with payload %u and ETB %02x\n", resp.begin, resp.payload, resp.end); return ERR_PROTO; } return ERR_OK; } TrinketErrorCode trinketGetCumDose(double *value) { const TrinketProtoDesc *proto = &protoDesc[CMD_CUM_DOSE]; struct { ProtoHeader header; uint8_t begin; uint8_t len; uint8_t cmd; uint8_t end; } cmd = { {REPORT_ID, proto->cmdLen}, BEGIN_COMMAND, proto->cmdLen, proto->cmdId, END_COMMAND }; struct packed_struct { ProtoHeader header; uint8_t begin; uint16_t len; double payload; uint8_t end; } resp; int r; assert(proto->cmdId == CMD_CUM_DOSE); assert(sizeof(cmd) == cmd.len + sizeof(ProtoHeader)); assert(sizeof(resp) == proto->respLen + sizeof(ProtoHeader)); r = hid_write(trinketDev, (unsigned char*)&cmd, sizeof(cmd)); if (r < 0 || r != sizeof(cmd)) { fprintf(stderr, "hid_send_feature_report returns %d instead of %u\n", r, cmd.len); return ERR_ERROR; } /* must be assigned to 0x3F by the host */ resp.header.reportId = REPORT_ID; r = hid_read_timeout(trinketDev, (unsigned char*)&resp, sizeof(resp), DEV_TIMEOUT); if (r < 0) { fprintf(stderr, "hid_read_timeout fails\n"); return ERR_NO_ANSWER; } if (r != sizeof(resp)) { fprintf(stderr, "hid_read_timeout returns %d instead of %u\n", r, (unsigned int)sizeof(resp)); return ERR_ERROR; } if (resp.header.reportId != REPORT_ID) { fprintf(stderr, "hid_read_timeout returns report id %02x instead of %02x\n", resp.header.reportId, REPORT_ID); return ERR_ERROR; } if (resp.header.size != proto->respLen) { fprintf(stderr, "hid_read_timeout returns length %u intsead if %u\n", resp.header.size, proto->respLen); return ERR_PROTO; } if (resp.len != proto->respLen) { fprintf(stderr, "hid_read_timeout response length %u instead of %u\n", resp.len, proto->respLen); return ERR_PROTO; } if (!(resp.begin == ACK_ANSWER_OK && resp.end == END_ANSWER)) { fprintf(stderr, "hid_read_timeout ACK %02x and ETB %02x\n", resp.begin, resp.end); return ERR_PROTO; } *value = resp.payload; return ERR_OK; }