Commit | Line | Data |
---|---|---|
93ac315e MPM |
1 | /* $Id: hostsoftware.c,v 1.10 2010/07/13 20:12:10 simimeie Exp $ |
2 | * This is the control software for the ds1820-to-usb device that runs | |
3 | * on the USB host to which the device is connected. | |
4 | * You will need libusb and its development headers to compile this. | |
5 | * On Linux, you will usually have to run this as root to be allowed to | |
6 | * access the device, unless you configure udev accordingly. | |
7 | * This is based on powerSwitch.c, included in the reference implementation | |
8 | * from Objective Development Software GmbH for the avrusb library from the | |
9 | * same company. | |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <stdlib.h> | |
14 | #include <string.h> | |
15 | #include <time.h> | |
16 | #include <usb.h> /* this is libusb, see http://libusb.sourceforge.net/ */ | |
17 | #include <sys/socket.h> | |
18 | #include <sys/types.h> | |
19 | #include <netinet/in.h> | |
20 | #include <errno.h> | |
21 | #include <fcntl.h> | |
22 | #include <signal.h> | |
23 | #include <unistd.h> | |
24 | #include <sys/select.h> /* According to POSIX.1-2001 */ | |
25 | ||
26 | int verblev = 1; | |
27 | #define VERBPRINT(lev, fmt...) \ | |
28 | if (verblev > lev) { \ | |
29 | printf(fmt); \ | |
30 | fflush(stdout); \ | |
31 | } | |
32 | int devicenr = 1; | |
33 | int runinforeground = 0; | |
34 | int restartonerror = 0; | |
35 | ||
36 | #define USBDEV_SHARED_VENDOR 0x16C0 /* VOTI */ | |
37 | #define USBDEV_SHARED_PRODUCT 0x05DC /* Obdev's free shared PID */ | |
38 | /* Use obdev's generic shared VID/PID pair and follow the rules outlined | |
39 | * in firmware/usbdrv/USBID-License.txt. | |
40 | */ | |
41 | ||
42 | /* These are the vendor specific SETUP commands implemented by our USB device */ | |
43 | #define CMD_ECHO 0 /* echo 2 bytes */ | |
44 | #define CMD_STATUS_SHORT 1 /* query device for status */ | |
45 | #define CMD_RESCAN 2 /* tell the device to rescan the probe bus */ | |
46 | #define CMD_STATUS_LONG 3 /* query device for list of probes and their status */ | |
47 | #define CMD_HARDRESET 4 /* reset device and probe bus */ | |
48 | ||
49 | struct daemondata { | |
50 | unsigned char serial[6]; | |
51 | unsigned int port; | |
52 | int fd; | |
53 | time_t lastseen; | |
54 | double lasttemp; | |
55 | unsigned char outputformat[1000]; | |
56 | struct daemondata * next; | |
57 | }; | |
58 | ||
59 | static void usage(char *name) | |
60 | { | |
61 | printf("usage: %s [-v] [-q] [-d n] [-h] command <parameters>\n", name); | |
62 | printf(" -v more verbose output. can be repeated numerous times.\n"); | |
63 | printf(" -q less verbose output. using this more than once will have no effect.\n"); | |
64 | printf(" -d n Use the n-th device found (default: 1)\n"); | |
65 | printf(" -f relevant for daemon mode only: run in foreground.\n"); | |
66 | printf(" -h show this help\n"); | |
67 | printf("Valid commands are:\n"); | |
68 | printf(" test do an echo test with the device\n"); | |
69 | printf(" status query and show device status\n"); | |
70 | printf(" rescan tell the device to rescan its probe bus\n"); | |
71 | printf(" reset tell the device to reset the probe bus and itself\n"); | |
72 | printf(" showint shows 'interrupts' received from the device. These are\n"); | |
73 | printf(" not real interrupts, they are USB 'interrupts', and actually\n"); | |
74 | printf(" work by polling in regular intervals...\n"); | |
75 | printf(" daemon Daemonize and answer queries. This requires one or more\n"); | |
76 | printf(" parameters in the format 'serial:port', where serial is the serial\n"); | |
77 | printf(" number of a probe, and port is a TCP port where the data from this\n"); | |
78 | printf(" probe is to be served, e.g.: affeaffeaf:31337\n"); | |
79 | printf(" optionally, you can give a third parameter, that specifies how the\n"); | |
80 | printf(" output to the network should look like, e.g.: affeaffeaf:31337:%%T\n"); | |
81 | printf(" Available are: %%S = serial of probe, %%T = temperature,\n"); | |
82 | printf(" %%L = last seen timestamp. The default is '%%S %%T'.\n"); | |
83 | } | |
84 | ||
85 | ||
86 | void sigpipehandler(int bla) { /* Dummyhandler for catching the event */ | |
87 | return; | |
88 | } | |
89 | ||
90 | ||
91 | static int usbGetStringAscii(usb_dev_handle *dev, int index, int langid, char *buf, int buflen) | |
92 | { | |
93 | char buffer[256]; | |
94 | int rval, i; | |
95 | ||
96 | if ((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, langid, buffer, sizeof(buffer), 1000)) < 0) { | |
97 | return rval; | |
98 | } | |
99 | if (buffer[1] != USB_DT_STRING) { | |
100 | return 0; | |
101 | } | |
102 | if ((unsigned char)buffer[0] < rval) { | |
103 | rval = (unsigned char)buffer[0]; | |
104 | } | |
105 | /* lossy conversion to ISO Latin1 */ | |
106 | rval /= 2; | |
107 | for (i = 1; i < rval; i++) { | |
108 | if (i > buflen) { /* destination buffer overflow */ | |
109 | break; | |
110 | } | |
111 | buf[i-1] = buffer[2 * i]; | |
112 | if (buffer[2 * i + 1] != 0) { /* outside of ISO Latin1 range */ | |
113 | buf[i-1] = '?'; | |
114 | } | |
115 | } | |
116 | buf[i-1] = 0; | |
117 | return i-1; | |
118 | } | |
119 | ||
120 | ||
121 | static int usbOpenDevice(usb_dev_handle **device, int vendor, char *vendorName, int product, char *productName, int devicerequested) | |
122 | { | |
123 | struct usb_bus *bus; | |
124 | struct usb_device *dev; | |
125 | usb_dev_handle *handle = NULL; | |
126 | static int didUsbInit = 0; | |
127 | int devicesfound = 0; | |
128 | ||
129 | if (!didUsbInit) { | |
130 | didUsbInit = 1; | |
131 | usb_init(); | |
132 | } | |
133 | usb_find_busses(); | |
134 | usb_find_devices(); | |
135 | for (bus = usb_get_busses(); bus; bus = bus->next) { | |
136 | for (dev = bus->devices; dev; dev = dev->next) { | |
137 | if ((dev->descriptor.idVendor == vendor) && (dev->descriptor.idProduct == product)) { | |
138 | char string[256]; | |
139 | int len; | |
140 | handle = usb_open(dev); /* we need to open the device in order to query strings */ | |
141 | if (!handle) { | |
142 | VERBPRINT(1, "Warning: cannot open USB device: %s\n", usb_strerror()); | |
143 | continue; | |
144 | } | |
145 | if ((vendorName == NULL) && (productName == NULL)) { /* name does not matter */ | |
146 | /* we found it! */ | |
147 | devicesfound++; | |
148 | if (devicesfound == devicerequested) { | |
149 | break; | |
150 | } else { | |
151 | usb_close(handle); | |
152 | handle = NULL; | |
153 | continue; | |
154 | } | |
155 | } | |
156 | /* now check whether the names match: */ | |
157 | len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, 0x0409, string, sizeof(string)); | |
158 | if (len < 0) { | |
159 | VERBPRINT(1, "Warning: cannot query manufacturer for device: %s\n", usb_strerror()); | |
160 | } else { | |
161 | VERBPRINT(3, "seen device from vendor '%s'\n", string); | |
162 | if (strcmp(string, vendorName) == 0) { | |
163 | len = usbGetStringAscii(handle, dev->descriptor.iProduct, 0x0409, string, sizeof(string)); | |
164 | if (len < 0) { | |
165 | VERBPRINT(1, "Warning: cannot query product for device: %s\n", usb_strerror()); | |
166 | } else { | |
167 | VERBPRINT(3, "seen product '%s'\n", string); | |
168 | if (strcmp(string, productName) == 0) { | |
169 | devicesfound++; | |
170 | if (devicesfound == devicerequested) { | |
171 | break; | |
172 | } | |
173 | } | |
174 | } | |
175 | } | |
176 | usb_close(handle); | |
177 | handle = NULL; | |
178 | } | |
179 | } | |
180 | if (handle) { | |
181 | break; /* Stop searching */ | |
182 | } | |
183 | } | |
184 | } | |
185 | if (handle != NULL) { | |
186 | *device = handle; | |
187 | return 0; | |
188 | } | |
189 | return 1; | |
190 | } | |
191 | ||
192 | void dodevicetest(usb_dev_handle * handle) | |
193 | { | |
194 | unsigned char buffer[8]; | |
195 | unsigned short int i, v, r; | |
196 | int nBytes; | |
197 | ||
198 | VERBPRINT(0, "Doing device test, this may take some time...\n"); | |
199 | /* The test consists of writing 1000 random numbers to the device and checking | |
200 | * the echo. This should discover systematic bit errors (e.g. in bit stuffing). | |
201 | */ | |
202 | for (i = 0; i < 1000; i++){ | |
203 | VERBPRINT(2, "."); | |
204 | v = rand() & 0xffff; | |
205 | nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_ECHO, v, 0, (char *)buffer, sizeof(buffer), 5000); | |
206 | if (nBytes != 2) { | |
207 | if (nBytes < 0) { | |
208 | fprintf(stderr, "ERROR: USB error: %s\n", usb_strerror()); | |
209 | } | |
210 | fprintf(stderr, "ERROR: wrong number of bytes (%d instead of 2) received in iteration %d\n", nBytes, i); | |
211 | fprintf(stderr, "value sent = 0x%x\n", v); | |
212 | exit(1); | |
213 | } | |
214 | r = buffer[0] | (buffer[1] << 8); | |
215 | if (r != v) { | |
216 | fprintf(stderr, "ERROR: data error: received 0x%x instead of 0x%x in iteration %d\n", r, v, i); | |
217 | exit(1); | |
218 | } | |
219 | } | |
220 | VERBPRINT(0, "Test succeeded.\n"); | |
221 | exit(0); | |
222 | } | |
223 | ||
224 | static uint32_t make32bit(unsigned char * d) { | |
225 | return *((uint32_t *)d); | |
226 | } | |
227 | ||
228 | static double maketemp(unsigned char * d) { | |
229 | double res; int32_t t2; | |
230 | t2 = ((d[1] & 0x0F) << 8) + d[0]; | |
231 | if (t2 > 0x07FF) { /* Negative temperature */ | |
232 | t2 -= 0x1000; | |
233 | } | |
234 | res = (double)t2 * 0.0625L; | |
235 | return res; | |
236 | } | |
237 | ||
238 | static void dodevicestatus(usb_dev_handle * handle) { | |
239 | int nBytes; int p; int i; | |
240 | unsigned char buffer[1024]; /* That would be enough for 64 probes... */ | |
241 | uint32_t devicetime; | |
242 | unsigned int nprobes; | |
243 | ||
244 | nBytes = usb_control_msg(handle, | |
245 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_STATUS_SHORT, | |
246 | 0, 0, | |
247 | (char *)buffer, 8, | |
248 | 5000); | |
249 | if (nBytes < 0) { | |
250 | fprintf(stderr, "ERROR: USB error: %s\n", usb_strerror()); | |
251 | exit(1); | |
252 | } | |
253 | if (nBytes != 8) { | |
254 | fprintf(stderr, "ERROR: invalid status received from device (%d bytes)\n", nBytes); | |
255 | exit(1); | |
256 | } | |
257 | printf("Device Version is %02x.%02x\n", buffer[0], buffer[1]); | |
258 | devicetime = make32bit(&buffer[2]); | |
259 | printf("Device time is currently timestamp %u\n", devicetime); | |
260 | nprobes = buffer[6]; | |
261 | printf("%u probes supported by device\n", nprobes); | |
262 | if (nprobes >= ((sizeof(buffer)) / 16)) { | |
263 | nprobes = ((sizeof(buffer)) / 16) - 1; | |
264 | printf("Warning: Cannot handle this many probes myself - will only show first %u.\n", buffer[6]); | |
265 | } | |
266 | nBytes = usb_control_msg(handle, | |
267 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_STATUS_LONG, | |
268 | 0, 0, | |
269 | (char *)buffer, (nprobes * 16), | |
270 | 5000); | |
271 | if ((nBytes % 16) != 0) { | |
272 | fprintf(stderr, "ERROR: invalid status received from device (%d bytes)\n", nBytes); | |
273 | exit(1); | |
274 | } | |
275 | if (nBytes != (nprobes * 16)) { | |
276 | fprintf(stderr, "WARNING: expected %u probes, got %u.\n", nprobes, (nBytes / 16)); | |
277 | } | |
278 | printf("%2s %-12s %2s %10s %10s %-6s\n", "fa", "serial", "fl", "lastseen", "ticksago", "lastvalue"); | |
279 | for (p = 0; p < nBytes; p += 16) { | |
280 | printf("%02x ", buffer[p+6]); | |
281 | for (i = 5; i >= 0; i--) { | |
282 | printf("%02x", buffer[p+i]); | |
283 | } | |
284 | if ((buffer[p+7] & 0x01) == 0x01) { /* in USE */ | |
285 | printf(" U"); | |
286 | } else { | |
287 | printf(" "); | |
288 | } | |
289 | if ((buffer[p+7] & 0x02) == 0x02) { /* Parasite powered */ | |
290 | printf("P"); | |
291 | } else { | |
292 | printf(" "); | |
293 | } | |
294 | printf(" %10u %10u ", make32bit(&buffer[p+10]), | |
295 | devicetime - make32bit(&buffer[p+10])); | |
296 | printf("%6.2lf\n", maketemp(&buffer[p+8])); | |
297 | } | |
298 | } | |
299 | ||
300 | static void doshowint(usb_dev_handle * handle) { | |
301 | int nBytes; int i; | |
302 | unsigned char buffer[8]; | |
303 | ||
304 | printf("Waiting for 'interrupts' from the device - abort with ctrl+c\n"); | |
305 | while (1) { | |
306 | nBytes = usb_interrupt_read(handle, | |
307 | USB_ENDPOINT_IN | 0x01, | |
308 | (char *)buffer, sizeof(buffer), | |
309 | 0 /* no timeout */ ); | |
310 | if (nBytes < 0) { | |
311 | fprintf(stderr, "ERROR: USB error: %s\n", usb_strerror()); | |
312 | exit(1); | |
313 | } | |
314 | if (nBytes != 8) { | |
315 | fprintf(stderr, "ERROR: invalid interrupt message received from device (%d bytes instead of 8) - continuing\n", nBytes); | |
316 | continue; | |
317 | } | |
318 | { | |
319 | char tbuf[100]; | |
320 | time_t tt; | |
321 | tt = time(NULL); | |
322 | strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", localtime(&tt)); | |
323 | printf("[%s] ", tbuf); | |
324 | } | |
325 | printf("Data: probeserial='"); | |
326 | for (i = 5; i >= 0; i--) { | |
327 | printf("%02x", buffer[i]); | |
328 | } | |
329 | printf("' temp='%6.2lf'\n", maketemp(&buffer[6])); | |
330 | } | |
331 | /* never reached */ | |
332 | } | |
333 | ||
334 | void logaccess(struct sockaddr * soa, int soalen, char * txt) { | |
335 | struct sockaddr_in * sav4; | |
336 | struct sockaddr_in6 * sav6; | |
337 | ||
338 | if (soalen == sizeof(struct sockaddr_in6)) { | |
339 | sav6 = (struct sockaddr_in6 *)soa; | |
340 | if ((sav6->sin6_addr.s6_addr[ 0] == 0) | |
341 | && (sav6->sin6_addr.s6_addr[ 1] == 0) | |
342 | && (sav6->sin6_addr.s6_addr[ 2] == 0) | |
343 | && (sav6->sin6_addr.s6_addr[ 3] == 0) | |
344 | && (sav6->sin6_addr.s6_addr[ 4] == 0) | |
345 | && (sav6->sin6_addr.s6_addr[ 5] == 0) | |
346 | && (sav6->sin6_addr.s6_addr[ 6] == 0) | |
347 | && (sav6->sin6_addr.s6_addr[ 7] == 0) | |
348 | && (sav6->sin6_addr.s6_addr[ 8] == 0) | |
349 | && (sav6->sin6_addr.s6_addr[ 9] == 0) | |
350 | && (sav6->sin6_addr.s6_addr[10] == 0xFF) | |
351 | && (sav6->sin6_addr.s6_addr[11] == 0xFF)) { | |
352 | /* This is really a IPv4 not a V6 access, so log it as | |
353 | * a such. */ | |
354 | VERBPRINT(2, "%d.%d.%d.%d\t%s\n", sav6->sin6_addr.s6_addr[12], | |
355 | sav6->sin6_addr.s6_addr[13], | |
356 | sav6->sin6_addr.s6_addr[14], | |
357 | sav6->sin6_addr.s6_addr[15], txt); | |
358 | } else { | |
359 | /* True IPv6 access */ | |
360 | VERBPRINT(2, "%x:%x:%x:%x:%x:%x:%x:%x\t%s\n", | |
361 | (sav6->sin6_addr.s6_addr[ 0] << 8) | sav6->sin6_addr.s6_addr[ 1], | |
362 | (sav6->sin6_addr.s6_addr[ 2] << 8) | sav6->sin6_addr.s6_addr[ 3], | |
363 | (sav6->sin6_addr.s6_addr[ 4] << 8) | sav6->sin6_addr.s6_addr[ 5], | |
364 | (sav6->sin6_addr.s6_addr[ 6] << 8) | sav6->sin6_addr.s6_addr[ 7], | |
365 | (sav6->sin6_addr.s6_addr[ 8] << 8) | sav6->sin6_addr.s6_addr[ 9], | |
366 | (sav6->sin6_addr.s6_addr[10] << 8) | sav6->sin6_addr.s6_addr[11], | |
367 | (sav6->sin6_addr.s6_addr[12] << 8) | sav6->sin6_addr.s6_addr[13], | |
368 | (sav6->sin6_addr.s6_addr[14] << 8) | sav6->sin6_addr.s6_addr[15], | |
369 | txt); | |
370 | } | |
371 | } else if (soalen == sizeof(struct sockaddr_in)) { | |
372 | unsigned char brokeni32[4]; | |
373 | ||
374 | sav4 = (struct sockaddr_in *)soa; | |
375 | brokeni32[0] = (sav4->sin_addr.s_addr & 0xFF000000UL) >> 24; | |
376 | brokeni32[1] = (sav4->sin_addr.s_addr & 0x00FF0000UL) >> 16; | |
377 | brokeni32[2] = (sav4->sin_addr.s_addr & 0x0000FF00UL) >> 8; | |
378 | brokeni32[3] = (sav4->sin_addr.s_addr & 0x000000FFUL) >> 0; | |
379 | VERBPRINT(2, "%d.%d.%d.%d\t%s\n", brokeni32[0], brokeni32[1], | |
380 | brokeni32[2], brokeni32[3], txt); | |
381 | } else { | |
382 | VERBPRINT(2, "!UNKNOWN_ADDRESS_TYPE!\t%s\n", txt); | |
383 | } | |
384 | } | |
385 | ||
386 | ||
387 | static void printtooutbuf(char * outbuf, int oblen, struct daemondata * dd) { | |
388 | unsigned char * pos = &dd->outputformat[0]; | |
389 | while (*pos != 0) { | |
390 | if (*pos == '%') { | |
391 | pos++; | |
392 | if (*pos == 'S') { /* Serial */ | |
393 | int i; | |
394 | for (i = 5; i >= 0; i--) { | |
395 | outbuf += sprintf(outbuf, "%02x", dd->serial[i]); | |
396 | } | |
397 | } else if ((*pos == 'T') || (*pos == 't')) { /* Temperature */ | |
398 | if ((dd->lastseen + 60) < time(NULL)) { /* Stale data / no data yet */ | |
399 | outbuf += sprintf(outbuf, "%s", "N/A"); | |
400 | } else { | |
401 | if (*pos == 'T') { /* fixed width */ | |
402 | outbuf += sprintf(outbuf, "%6.2lf", dd->lasttemp); | |
403 | } else { /* variable width. */ | |
404 | outbuf += sprintf(outbuf, "%.2lf", dd->lasttemp); | |
405 | } | |
406 | } | |
407 | } else if (*pos == 'r') { /* carriage return */ | |
408 | *outbuf = '\r'; | |
409 | outbuf++; | |
410 | } else if (*pos == 'n') { /* linefeed / Newline */ | |
411 | *outbuf = '\n'; | |
412 | outbuf++; | |
413 | } else if (*pos == 'L') { /* Last seen */ | |
414 | outbuf += sprintf(outbuf, "%u", (unsigned int)dd->lastseen); | |
415 | } else if (*pos == 0) { | |
416 | *outbuf = 0; | |
417 | return; | |
418 | } | |
419 | pos++; | |
420 | } else { | |
421 | *outbuf = *pos; | |
422 | outbuf++; | |
423 | pos++; | |
424 | } | |
425 | } | |
426 | *outbuf = 0; | |
427 | } | |
428 | ||
429 | static void dotryrestart(struct daemondata * dd, char ** argv) { | |
430 | if (restartonerror) { | |
431 | struct daemondata * curdd = dd; | |
432 | while (curdd != NULL) { | |
433 | close(curdd->fd); | |
434 | curdd = curdd->next; | |
435 | } | |
436 | fprintf(stderr, "Will try to restart in %d second(s)...\n", restartonerror); | |
437 | sleep(restartonerror); | |
438 | execv(argv[0], argv); | |
439 | } | |
440 | } | |
441 | ||
442 | static void dodaemon(usb_dev_handle * handle, struct daemondata * dd, char ** argv) { | |
443 | fd_set mylsocks; | |
444 | struct daemondata * curdd; | |
445 | struct timeval to; | |
446 | int nBytes; | |
447 | unsigned char buffer[8]; | |
448 | int maxfd; | |
449 | int readysocks; | |
450 | ||
451 | while (1) { | |
452 | do { | |
453 | curdd = dd; /* Start from beginning */ | |
454 | maxfd = 0; | |
455 | FD_ZERO(&mylsocks); | |
456 | while (curdd != NULL) { | |
457 | FD_SET(curdd->fd, &mylsocks); | |
458 | if (curdd->fd > maxfd) { maxfd = curdd->fd; } | |
459 | curdd = curdd->next; | |
460 | } | |
461 | to.tv_sec = 0; to.tv_usec = 1; | |
462 | if ((readysocks = select((maxfd + 1), &mylsocks, NULL, NULL, &to)) < 0) { /* Error?! */ | |
463 | if (errno != EINTR) { | |
464 | perror("ERROR: error on select()"); | |
465 | dotryrestart(dd, argv); | |
466 | exit(1); | |
467 | } | |
468 | } else { | |
469 | curdd = dd; | |
470 | while (curdd != NULL) { | |
471 | if (FD_ISSET(curdd->fd, &mylsocks)) { | |
472 | int tmpfd; | |
473 | struct sockaddr_in6 srcad; | |
474 | socklen_t adrlen = sizeof(srcad); | |
475 | tmpfd = accept(curdd->fd, (struct sockaddr *)&srcad, &adrlen); | |
476 | if (tmpfd < 0) { | |
477 | perror("WARNING: Failed to accept() connection"); | |
478 | } else { | |
479 | char outbuf[250]; | |
480 | printtooutbuf(outbuf, strlen(outbuf), curdd); | |
481 | logaccess((struct sockaddr *)&srcad, adrlen, outbuf); | |
482 | write(tmpfd, outbuf, strlen(outbuf)); | |
483 | close(tmpfd); | |
484 | } | |
485 | } | |
486 | curdd = curdd->next; | |
487 | } | |
488 | } | |
489 | } while (readysocks > 0); | |
490 | /* Now check if there is anything available on USB */ | |
491 | nBytes = usb_interrupt_read(handle, | |
492 | USB_ENDPOINT_IN | 0x01, | |
493 | (char *)buffer, sizeof(buffer), | |
494 | 100 /* short timeout - but can't be shorter, else no data is seen? strange. */ ); | |
495 | if (nBytes < 0) { /* error reported by libusb */ | |
496 | /* As nice as libusb is, the documentation is just HORROR. And so is | |
497 | * this interface: This is the best way I have found to check if the | |
498 | * error is actually a timeout. I would expect something like an | |
499 | * usb_errno variable one can poll, but no... */ | |
500 | if (nBytes != -ETIMEDOUT) { | |
501 | fprintf(stderr, "ERROR: USB error: %s\n", usb_strerror()); | |
502 | dotryrestart(dd, argv); | |
503 | exit(1); | |
504 | } | |
505 | } else if (nBytes != 8) { /* No error but wrong number of bytes */ | |
506 | fprintf(stderr, "WARNING: invalid interrupt message received from device (%d bytes instead of 8) - continuing\n", nBytes); | |
507 | } else { | |
508 | VERBPRINT(3, "interrupt data received from device %02x%02x%02x%02x%02x%02x ", | |
509 | buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5]); | |
510 | curdd = dd; | |
511 | while (curdd != NULL) { | |
512 | if (memcmp(&curdd->serial[0], &buffer[0], 6) == 0) { | |
513 | /* This belongs here - update! */ | |
514 | curdd->lasttemp = maketemp(&buffer[6]); | |
515 | VERBPRINT(3, "temp = %.2f\n", curdd->lasttemp); | |
516 | curdd->lastseen = time(NULL); | |
517 | } | |
518 | curdd = curdd->next; | |
519 | } | |
520 | } | |
521 | } | |
522 | /* never reached */ | |
523 | } | |
524 | ||
525 | int main(int argc, char ** argv) | |
526 | { | |
527 | usb_dev_handle * handle = NULL; | |
528 | unsigned char buffer[8]; | |
529 | int nBytes; | |
530 | int curarg; | |
531 | ||
532 | for (curarg = 1; curarg < argc; curarg++) { | |
533 | if (strcmp(argv[curarg], "-v") == 0) { | |
534 | verblev++; | |
535 | } else if (strcmp(argv[curarg], "-q") == 0) { | |
536 | verblev--; | |
537 | } else if (strcmp(argv[curarg], "-f") == 0) { | |
538 | runinforeground = 1; | |
539 | } else if (strcmp(argv[curarg], "-h") == 0) { | |
540 | usage(argv[0]); exit(0); | |
541 | } else if (strcmp(argv[curarg], "--help") == 0) { | |
542 | usage(argv[0]); exit(0); | |
543 | } else if (strcmp(argv[curarg], "--restartonerror") == 0) { | |
544 | restartonerror++; | |
545 | } else if (strcmp(argv[curarg], "-d") == 0) { | |
546 | curarg++; | |
547 | if (curarg >= argc) { | |
548 | fprintf(stderr, "ERROR: -d requires a numeric parameter >= 1!\n"); | |
549 | usage(argv[0]); exit(1); | |
550 | } | |
551 | devicenr = strtoul(argv[curarg], NULL, 10); | |
552 | if (devicenr <= 0) { | |
553 | fprintf(stderr, "ERROR: -d requires a numeric parameter >= 1!\n"); | |
554 | usage(argv[0]); exit(1); | |
555 | } | |
556 | } else { | |
557 | /* Unknown - must be the command. */ | |
558 | break; | |
559 | } | |
560 | } | |
561 | if (curarg == argc) { | |
562 | fprintf(stderr, "ERROR: No command given!\n"); | |
563 | usage(argv[0]); | |
564 | exit(1); | |
565 | } | |
566 | usb_init(); | |
567 | if (usbOpenDevice(&handle, USBDEV_SHARED_VENDOR, "www.poempelfox.de", USBDEV_SHARED_PRODUCT, "ds1820tousb", devicenr) != 0) { | |
568 | fprintf(stderr, "ERROR: Could not find the ds1820tousb device nr. %d on the USB.\n", devicenr); | |
569 | exit(1); | |
570 | } | |
571 | if (usb_claim_interface(handle, 0) < 0) { | |
572 | fprintf(stderr, "ERROR: Failed to claim ds1820tousb device nr. %d on the USB: %s\n", devicenr, usb_strerror()); | |
573 | exit(1); | |
574 | } | |
575 | if (strcmp(argv[curarg], "test") == 0) { | |
576 | dodevicetest(handle); | |
577 | } else if (strcmp(argv[1], "status") == 0) { | |
578 | dodevicestatus(handle); | |
579 | } else if (strcmp(argv[curarg], "rescan") == 0) { | |
580 | nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_RESCAN, 0, 0, (char *)buffer, sizeof(buffer), 5000); | |
581 | if (nBytes < 0) { | |
582 | fprintf(stderr, "ERROR: USB error: %s\n", usb_strerror()); | |
583 | } else { | |
584 | if (nBytes == 1) { | |
585 | switch(buffer[0]) { | |
586 | case 23: | |
587 | VERBPRINT(0, "Device responded: OK, scheduling rescan\n"); | |
588 | break; | |
589 | case 42: | |
590 | VERBPRINT(0, "Device responded: rescan already scheduled or in progress\n"); | |
591 | break; | |
592 | default: | |
593 | fprintf(stderr, "ERROR: Invalid reply to rescan command from device (unknown status %02x)\n", buffer[0]); | |
594 | break; | |
595 | }; | |
596 | } else { | |
597 | fprintf(stderr, "ERROR: Invalid reply to rescan command from device (%d bytes instead of 1)\n", nBytes); | |
598 | } | |
599 | } | |
600 | } else if (strcmp(argv[curarg], "reset") == 0) { | |
601 | nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_HARDRESET, 0, 0, (char *)buffer, sizeof(buffer), 5000); | |
602 | VERBPRINT(1, "%d bytes received as reply.\n", nBytes); | |
603 | VERBPRINT(0, "%s\n", "Reset command sent. If this worked, you should see an USB disconnect and reconnect now\n"); | |
604 | } else if (strcmp(argv[curarg], "showint") == 0) { /* More of a debug mode: Show interrupt data */ | |
605 | doshowint(handle); | |
606 | } else if (strcmp(argv[curarg], "daemon") == 0) { /* Daemon mode */ | |
607 | struct daemondata * mydaemondata = NULL; | |
608 | curarg++; | |
609 | do { | |
610 | int l; int optval; | |
611 | struct daemondata * newdd; | |
612 | struct sockaddr_in6 soa; | |
613 | ||
614 | if (curarg >= argc) continue; | |
615 | newdd = calloc(sizeof(struct daemondata), 1); | |
616 | newdd->next = mydaemondata; | |
617 | mydaemondata = newdd; | |
618 | l = sscanf(argv[curarg], "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx:%u:%999[^\n]", | |
619 | &mydaemondata->serial[5], &mydaemondata->serial[4], &mydaemondata->serial[3], &mydaemondata->serial[2], | |
620 | &mydaemondata->serial[1], &mydaemondata->serial[0], &mydaemondata->port, &mydaemondata->outputformat[0]); | |
621 | if (l < 7) { | |
622 | fprintf(stderr, "ERROR: failed to parse daemon command parameter '%s'\n", argv[curarg]); | |
623 | exit(1); | |
624 | } | |
625 | if (l == 7) { | |
626 | strcpy((char *)&mydaemondata->outputformat[0], "%S %T"); | |
627 | } | |
628 | /* Open the port */ | |
629 | mydaemondata->fd = socket(PF_INET6, SOCK_STREAM, 0); | |
630 | soa.sin6_family = AF_INET6; | |
631 | soa.sin6_addr = in6addr_any; | |
632 | soa.sin6_port = htons(mydaemondata->port); | |
633 | optval = 1; | |
634 | setsockopt(mydaemondata->fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); | |
635 | if (bind(mydaemondata->fd, (struct sockaddr *)&soa, sizeof(soa)) < 0) { | |
636 | perror("Bind failed"); | |
637 | printf("Could not bind to port %u\n", mydaemondata->port); | |
638 | exit(1); | |
639 | } | |
640 | if (listen(mydaemondata->fd, 20) < 0) { /* Large Queue as we block for some time while reading USB! */ | |
641 | perror("Listen failed"); | |
642 | exit(1); | |
643 | } | |
644 | curarg++; | |
645 | } while (curarg < argc); | |
646 | if (mydaemondata == NULL) { | |
647 | fprintf(stderr, "ERROR: the daemon command requires parameters.\n"); | |
648 | exit(1); | |
649 | } | |
650 | /* the good old doublefork trick from 'systemprogrammierung 1' */ | |
651 | if (runinforeground != 1) { | |
652 | int ourpid; | |
653 | VERBPRINT(2, "launching into the background...\n"); | |
654 | ourpid = fork(); | |
655 | if (ourpid < 0) { | |
656 | perror("Ooops, fork() #1 failed"); | |
657 | exit(1); | |
658 | } | |
659 | if (ourpid == 0) { /* We're the child */ | |
660 | ourpid = fork(); /* fork again */ | |
661 | if (ourpid < 0) { | |
662 | perror("Ooooups. fork() #2 failed"); | |
663 | exit(1); | |
664 | } | |
665 | if (ourpid == 0) { /* Child again */ | |
666 | /* Just don't exit, we'll continue below. */ | |
667 | } else { /* Parent */ | |
668 | exit(0); /* Just exit */ | |
669 | } | |
670 | } else { /* Parent */ | |
671 | exit(0); /* Just exit */ | |
672 | } | |
673 | } | |
674 | { | |
675 | struct sigaction sia; | |
676 | sia.sa_handler = sigpipehandler; | |
677 | sigemptyset(&sia.sa_mask); /* If we don't do this, we're likely */ | |
678 | sia.sa_flags = 0; /* to die from 'broken pipe'! */ | |
679 | sigaction(SIGPIPE, &sia, NULL); | |
680 | } | |
681 | dodaemon(handle, mydaemondata, argv); | |
682 | } else { | |
683 | fprintf(stderr, "ERROR: Command '%s' is unknown.\n", argv[curarg]); | |
684 | usage(argv[0]); | |
685 | exit(1); | |
686 | } | |
687 | usb_close(handle); | |
688 | return 0; | |
689 | } |