| 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 | } |