1 /*-------------------------------------------------------------------------
4 * Copyright (c) 2016, Teodor Sigaev <teodor@sigaev.ru>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 *-------------------------------------------------------------------------
33 #include <hidapi/hidapi.h>
38 #define packed_struct __attribute__((packed))
53 #define BEGIN_COMMAND (0x1B)
54 #define END_COMMAND (0x17)
55 #define ACK_ANSWER_OK (0x06)
56 #define ACK_ANSWER_ERR (0x15)
57 #define END_ANSWER (0x17)
59 /* http://uglyduck.ath.cx/PDF/TexasInstruments/MSP430/usblib430/Programmers_Guide_MSP430_USB_API.pdf */
60 #define REPORT_ID (0x3f)
61 typedef struct packed_struct {
67 #define DEV_TIMEOUT (5000 /* milliseconds */)
73 uint8_t cmdLen; /* 0 means variable, > 0 fixed */
78 uint32_t respLen; /* 0 means variable, > 0 fixed */
81 /* hardcoded knowledge from docs/geyger.pdf */
82 static const TrinketProtoDesc protoDesc[] = {
84 {CMD_VCC_VALUE, 4, 8 },
85 {CMD_VCC_ALARM, 8, 8 },
86 {CMD_CLICK_DELAY, 8, 8 },
87 {CMD_ALARM_LEVELS, 24, 24 },
89 {CMD_DAY_ARCH, 4, 4 + 1440 * 2 },
90 {CMD_YEAR_ARCH, 4, 4 + 5840 * 2 },
91 {CMD_CUM_DOSE, 4, 12 },
92 {CMD_INVOKE_BSL, 4, 0 }
95 static hid_device *trinketDev = NULL;
99 dumpBuf(char *msg, uint8_t *buf, int len) {
102 fprintf(stderr,"%s:", msg);
103 for (i=0; i<len; i++)
104 fprintf(stderr, " %02x", buf[i]);
105 fprintf(stderr, "\n");
114 for(i = 0; i < sizeof(protoDesc) / sizeof(*protoDesc); i++)
115 tassert(i == protoDesc[i].cmdId);
117 trinketDev = hid_open(0x2047, 0x0301, NULL);
119 if (trinketDev == NULL) {
120 tlog(TL_WARN, "hid_open fails");
124 if ((r = trinketPing()) != ERR_OK)
132 hid_close(trinketDev);
138 const TrinketProtoDesc *proto = &protoDesc[CMD_PING];
139 struct packed_struct {
146 {REPORT_ID, proto->cmdLen},
152 struct packed_struct {
156 uint8_t payload /* = 0 */;
161 tassert(proto->cmdId == CMD_PING);
162 tassert(sizeof(cmd) == cmd.len + sizeof(ProtoHeader));
163 tassert(sizeof(resp) == proto->respLen + sizeof(ProtoHeader));
165 r = hid_write(trinketDev, (unsigned char*)&cmd, sizeof(cmd));
167 if (r < 0 || r != sizeof(cmd)) {
168 tlog(TL_WARN, "hid_write returns %d instead of %u", r, cmd.len);
172 /* must be assigned to 0x3F by the host */
173 resp.header.reportId = REPORT_ID;
175 r = hid_read_timeout(trinketDev, (unsigned char*)&resp, sizeof(resp), DEV_TIMEOUT);
177 tlog(TL_WARN, "hid_read_timeout fails");
178 return ERR_NO_ANSWER;
181 if (r != sizeof(resp)) {
182 tlog(TL_WARN, "hid_read_timeout returns %d instead of %u", r, (unsigned int)sizeof(resp));
186 if (resp.header.reportId != REPORT_ID) {
187 tlog(TL_WARN, "hid_read_timeout returns report id %02x instead of %02x", resp.header.reportId, REPORT_ID);
191 if (resp.header.size != proto->respLen) {
192 tlog(TL_WARN, "hid_read_timeout returns length %u intsead if %u", resp.header.size, proto->respLen);
196 if (resp.len != proto->respLen) {
197 tlog(TL_WARN, "hid_read_timeout response length %u instead of %u", resp.len, proto->respLen);
201 if (!(resp.begin == ACK_ANSWER_OK && resp.payload == 0 && resp.end == END_ANSWER)) {
202 tlog(TL_WARN, "hid_read_timeout ACK %02x with payload %u and ETB %02x", resp.begin, resp.payload, resp.end);
210 trinketGetCumDose(double *value) {
211 const TrinketProtoDesc *proto = &protoDesc[CMD_CUM_DOSE];
219 {REPORT_ID, proto->cmdLen},
225 struct packed_struct {
234 tassert(proto->cmdId == CMD_CUM_DOSE);
235 tassert(sizeof(cmd) == cmd.len + sizeof(ProtoHeader));
236 tassert(sizeof(resp) == proto->respLen + sizeof(ProtoHeader));
238 r = hid_write(trinketDev, (unsigned char*)&cmd, sizeof(cmd));
240 if (r < 0 || r != sizeof(cmd)) {
241 tlog(TL_WARN, "hid_send_feature_report returns %d instead of %u", r, cmd.len);
245 /* must be assigned to 0x3F by the host */
246 resp.header.reportId = REPORT_ID;
248 r = hid_read_timeout(trinketDev, (unsigned char*)&resp, sizeof(resp), DEV_TIMEOUT);
250 tlog(TL_WARN, "hid_read_timeout fails");
251 return ERR_NO_ANSWER;
254 if (r != sizeof(resp)) {
255 tlog(TL_WARN, "hid_read_timeout returns %d instead of %u", r, (unsigned int)sizeof(resp));
259 if (resp.header.reportId != REPORT_ID) {
260 tlog(TL_WARN, "hid_read_timeout returns report id %02x instead of %02x", resp.header.reportId, REPORT_ID);
264 if (resp.header.size != proto->respLen) {
265 tlog(TL_WARN, "hid_read_timeout returns length %u intsead if %u", resp.header.size, proto->respLen);
269 if (resp.len != proto->respLen) {
270 tlog(TL_WARN, "hid_read_timeout response length %u instead of %u", resp.len, proto->respLen);
274 if (!(resp.begin == ACK_ANSWER_OK && resp.end == END_ANSWER)) {
275 tlog(TL_WARN, "hid_read_timeout ACK %02x and ETB %02x", resp.begin, resp.end);
279 *value = resp.payload;
285 trinketSetAlarmLevel(double value[N_ALARM_LEVELS]) {
286 const TrinketProtoDesc *proto = &protoDesc[CMD_ALARM_LEVELS];
292 float levels[N_ALARM_LEVELS];
295 {REPORT_ID, proto->cmdLen},
302 struct packed_struct {
306 float payload[N_ALARM_LEVELS];
311 tassert(proto->cmdId == CMD_ALARM_LEVELS);
312 tassert(sizeof(cmd) == cmd.len + sizeof(ProtoHeader));
313 tassert(sizeof(resp) == proto->respLen + sizeof(ProtoHeader));
315 memcpy(cmd.levels, value, sizeof(value[0]) * N_ALARM_LEVELS);
317 r = hid_write(trinketDev, (unsigned char*)&cmd, sizeof(cmd));
319 if (r < 0 || r != sizeof(cmd)) {
320 tlog(TL_WARN, "hid_send_feature_report returns %d instead of %u", r, cmd.len);
324 /* must be assigned to 0x3F by the host */
325 resp.header.reportId = REPORT_ID;
327 r = hid_read_timeout(trinketDev, (unsigned char*)&resp, sizeof(resp), DEV_TIMEOUT);
329 tlog(TL_WARN, "hid_read_timeout fails");
330 return ERR_NO_ANSWER;
333 if (r != sizeof(resp)) {
334 tlog(TL_WARN, "hid_read_timeout returns %d instead of %u", r, (unsigned int)sizeof(resp));
338 if (resp.header.reportId != REPORT_ID) {
339 tlog(TL_WARN, "hid_read_timeout returns report id %02x instead of %02x", resp.header.reportId, REPORT_ID);
343 if (resp.header.size != proto->respLen) {
344 tlog(TL_WARN, "hid_read_timeout returns length %u intsead if %u", resp.header.size, proto->respLen);
348 if (resp.len != proto->respLen) {
349 tlog(TL_WARN, "hid_read_timeout response length %u instead of %u", resp.len, proto->respLen);
353 if (!(resp.begin == ACK_ANSWER_OK && resp.end == END_ANSWER)) {
354 tlog(TL_WARN, "hid_read_timeout ACK %02x and ETB %02x", resp.begin, resp.end);
358 memcpy(value, resp.payload, sizeof(value[0]) * N_ALARM_LEVELS);