1 #include "main.h"
2 #include "bcmxcp.h"
3 #include "bcmxcp_io.h"
4 #include "common.h"
5 #include "usb-common.h"
6 #include "timehead.h"
7 #include "nut_stdint.h" /* for uint16_t */
8 #include <ctype.h>
9 #include <sys/file.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <usb.h>
13
14 #define SUBDRIVER_NAME "USB communication subdriver"
15 #define SUBDRIVER_VERSION "0.22"
16
17 /* communication driver description structure */
18 upsdrv_info_t comm_upsdrv_info = {
19 SUBDRIVER_NAME,
20 SUBDRIVER_VERSION,
21 NULL,
22 0,
23 { NULL }
24 };
25
26 #define MAX_TRY 4
27
28 /* Powerware */
29 #define POWERWARE 0x0592
30
31 /* Phoenixtec Power Co., Ltd */
32 #define PHOENIXTEC 0x06da
33
34 /* Hewlett Packard */
35 #define HP_VENDORID 0x03f0
36
37 /* USB functions */
38 usb_dev_handle *nutusb_open(const char *port);
39 int nutusb_close(usb_dev_handle *dev_h, const char *port);
40 /* unified failure reporting: call these often */
41 void nutusb_comm_fail(const char *fmt, ...)
42 __attribute__ ((__format__ (__printf__, 1, 2)));
43 void nutusb_comm_good(void);
44 /* function pointer, set depending on which device is used */
45 static int (*usb_set_descriptor)(usb_dev_handle *udev, unsigned char type,
46 unsigned char index, void *buf, size_t size);
47
48 /* usb_set_descriptor() for Powerware devices */
usb_set_powerware(usb_dev_handle * udev,unsigned char type,unsigned char index,void * buf,size_t size)49 static int usb_set_powerware(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, size_t size)
50 {
51 assert (size < INT_MAX);
52 return usb_control_msg(udev, USB_ENDPOINT_OUT, USB_REQ_SET_DESCRIPTOR, (type << 8) + index, 0, buf, (int)size, 1000);
53 }
54
powerware_ups(USBDevice_t * device)55 static void *powerware_ups(USBDevice_t *device) {
56 NUT_UNUSED_VARIABLE(device);
57 usb_set_descriptor = &usb_set_powerware;
58 return NULL;
59 }
60
61 /* usb_set_descriptor() for Phoenixtec devices */
usb_set_phoenixtec(usb_dev_handle * udev,unsigned char type,unsigned char index,void * buf,size_t size)62 static int usb_set_phoenixtec(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, size_t size)
63 {
64 NUT_UNUSED_VARIABLE(index);
65 NUT_UNUSED_VARIABLE(type);
66 assert (size < INT_MAX);
67 return usb_control_msg(udev, 0x42, 0x0d, (0x00 << 8) + 0x0, 0, buf, (int)size, 1000);
68 }
69
phoenixtec_ups(USBDevice_t * device)70 static void *phoenixtec_ups(USBDevice_t *device) {
71 NUT_UNUSED_VARIABLE(device);
72 usb_set_descriptor = &usb_set_phoenixtec;
73 return NULL;
74 }
75
76 /* USB IDs device table */
77 static usb_device_id_t pw_usb_device_table[] = {
78 /* various models */
79 { USB_DEVICE(POWERWARE, 0x0002), &powerware_ups },
80
81 /* various models */
82 { USB_DEVICE(PHOENIXTEC, 0x0002), &phoenixtec_ups },
83
84 /* T500 */
85 { USB_DEVICE(HP_VENDORID, 0x1f01), &phoenixtec_ups },
86 /* T750 */
87 { USB_DEVICE(HP_VENDORID, 0x1f02), &phoenixtec_ups },
88
89 /* Terminating entry */
90 { 0, 0, NULL }
91 };
92
93 /* limit the amount of spew that goes in the syslog when we lose the UPS */
94 #define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */
95 #define USB_ERR_RATE 10 /* then only print every 10th error */
96 #define XCP_USB_TIMEOUT 5000 /* in msec */
97
98 /* global variables */
99 static usb_dev_handle *upsdev = NULL;
100 extern int exit_flag;
101 static unsigned int comm_failures = 0;
102
103 /* Functions implementations */
send_read_command(unsigned char command)104 void send_read_command(unsigned char command)
105 {
106 unsigned char buf[4];
107
108 if (upsdev) {
109 buf[0] = PW_COMMAND_START_BYTE;
110 buf[1] = 0x01; /* data length */
111 buf[2] = command; /* command to send */
112 buf[3] = calc_checksum(buf); /* checksum */
113 upsdebug_hex (3, "send_read_command", buf, 4);
114 usb_set_descriptor(upsdev, USB_DT_STRING, 4, buf, 4); /* FIXME: Ignore error */
115 }
116 }
117
send_write_command(unsigned char * command,size_t command_length)118 void send_write_command(unsigned char *command, size_t command_length)
119 {
120 unsigned char sbuf[128];
121
122 if (upsdev) {
123 /* Prepare the send buffer */
124 sbuf[0] = PW_COMMAND_START_BYTE;
125 sbuf[1] = (unsigned char)(command_length);
126 memcpy(sbuf+2, command, command_length);
127 command_length += 2;
128
129 /* Add checksum */
130 sbuf[command_length] = calc_checksum(sbuf);
131 command_length += 1;
132 upsdebug_hex (3, "send_write_command", sbuf, command_length);
133 usb_set_descriptor(upsdev, USB_DT_STRING, 4, sbuf, command_length); /* FIXME: Ignore error */
134 }
135 }
136
137 #define PW_HEADER_SIZE (PW_HEADER_LENGTH + 1)
138 #define PW_CMD_BUFSIZE 256
139 /* get the answer of a command from the ups. And check that the answer is for this command */
get_answer(unsigned char * data,unsigned char command)140 ssize_t get_answer(unsigned char *data, unsigned char command)
141 {
142 unsigned char buf[PW_CMD_BUFSIZE], *my_buf = buf;
143 ssize_t res;
144 int endblock, need_data;
145 long elapsed_time; /* milliseconds */
146 ssize_t tail;
147 size_t bytes_read, end_length, length;
148 unsigned char block_number, sequence, seq_num;
149 struct timeval start_time, now;
150
151 if (upsdev == NULL)
152 return -1;
153
154 need_data = PW_HEADER_SIZE; /* 4 - cmd response header length, 1 for csum */
155 end_length = 0; /* total length of sequence(s), not counting header(s) */
156 endblock = 0; /* signal the last sequence in the block */
157 bytes_read = 0; /* total length of data read, including XCP header */
158 res = 0;
159 elapsed_time = 0;
160 seq_num = 1; /* current theoric sequence */
161
162 upsdebugx(1, "entering get_answer(%x)", command);
163
164 /* Store current time */
165 gettimeofday(&start_time, NULL);
166 memset(&buf, 0x0, PW_CMD_BUFSIZE);
167
168 assert (XCP_USB_TIMEOUT < INT_MAX);
169
170 while ( (!endblock) && ((XCP_USB_TIMEOUT - elapsed_time) > 0) ) {
171
172 /* Get (more) data if needed */
173 if (need_data > 0) {
174 res = usb_interrupt_read(upsdev,
175 0x81,
176 (char *) buf + bytes_read,
177 128,
178 (int)(XCP_USB_TIMEOUT - elapsed_time));
179
180 /* Update time */
181 gettimeofday(&now, NULL);
182 elapsed_time = (now.tv_sec - start_time.tv_sec)*1000 +
183 (now.tv_usec - start_time.tv_usec)/1000;
184
185 /* Check libusb return value */
186 if (res < 0)
187 {
188 /* Clear any possible endpoint stalls */
189 usb_clear_halt(upsdev, 0x81);
190 /* continue; */ /* FIXME: seems a break would be better! */
191 break;
192 }
193
194 /* this seems to occur on XSlot USB card */
195 if (res == 0)
196 {
197 /* FIXME: */
198 continue;
199 }
200 /* Else, we got some input bytes */
201 bytes_read += (size_t)res;
202 need_data -= res;
203 upsdebug_hex(1, "get_answer", buf, bytes_read);
204 }
205
206 if (need_data > 0) /* We need more data */
207 continue;
208
209 /* Now validate XCP frame */
210 /* Check header */
211 if ( my_buf[0] != PW_COMMAND_START_BYTE ) {
212 upsdebugx(2, "get_answer: wrong header 0xab vs %02x", my_buf[0]);
213 /* Sometime we read something wrong. bad cables? bad ports? */
214 my_buf = memchr(my_buf, PW_COMMAND_START_BYTE, bytes_read);
215 if (!my_buf)
216 return -1;
217 }
218
219 /* Read block number byte */
220 block_number = my_buf[1];
221 upsdebugx(1, "get_answer: block_number = %x", block_number);
222
223 /* Check data length byte (remove the header length) */
224 length = my_buf[2];
225 upsdebugx(3, "get_answer: data length = %zu", length);
226 if (bytes_read < (length + PW_HEADER_SIZE)) {
227 if (need_data < 0) --need_data; /* count zerro byte too */
228 need_data += length + 1; /* packet lenght + checksum */
229 upsdebugx(2, "get_answer: need to read %d more data", need_data);
230 continue;
231 }
232 /* Check if Length conforms to XCP (121 for normal, 140 for Test mode) */
233 /* Use the more generous length for testing */
234 if (length > 140) {
235 upsdebugx(2, "get_answer: bad length");
236 return -1;
237 }
238
239 if (bytes_read >= SSIZE_MAX) {
240 upsdebugx(2, "get_answer: bad length (incredibly large read)");
241 return -1;
242 }
243
244 /* Test the Sequence # */
245 sequence = my_buf[3];
246 if ((sequence & PW_SEQ_MASK) != seq_num) {
247 nutusb_comm_fail("get_answer: not the right sequence received %x!!!\n", (sequence & PW_SEQ_MASK));
248 return -1;
249 }
250 else {
251 upsdebugx(2, "get_answer: sequence number (%x) is ok", (sequence & PW_SEQ_MASK));
252 }
253
254 /* Validate checksum */
255 if (!checksum_test(my_buf)) {
256 nutusb_comm_fail("get_answer: checksum error! ");
257 return -1;
258 }
259 else {
260 upsdebugx(2, "get_answer: checksum is ok");
261 }
262
263 /* Check if it's the last sequence */
264 if (sequence & PW_LAST_SEQ) {
265 /* we're done receiving data */
266 upsdebugx(2, "get_answer: all data received");
267 endblock = 1;
268 }
269 else {
270 seq_num++;
271 upsdebugx(2, "get_answer: next sequence is %d", seq_num);
272 }
273
274 /* copy the current valid XCP frame back */
275 memcpy(data+end_length, my_buf + 4, length);
276 /* increment pointers to process the next sequence */
277 end_length += length;
278
279 /* Work around signedness of comparison result, SSIZE_MAX checked above: */
280 tail = (ssize_t)bytes_read;
281 tail -= (ssize_t)(length + PW_HEADER_SIZE);
282 if (tail > 0)
283 my_buf = memmove(&buf[0], my_buf + length + PW_HEADER_SIZE, (size_t)tail);
284 else if (tail == 0)
285 my_buf = &buf[0];
286 else { /* if (tail < 0) */
287 upsdebugx(1, "get_answer(): did not expect to get negative tail size: %zd", tail);
288 return -1;
289 }
290
291 bytes_read = (size_t)tail;
292 }
293
294 upsdebug_hex (5, "get_answer", data, end_length);
295 assert (end_length < SSIZE_MAX);
296 return (ssize_t)end_length;
297 }
298
299 /* Sends a single command (length=1). and get the answer */
command_read_sequence(unsigned char command,unsigned char * data)300 ssize_t command_read_sequence(unsigned char command, unsigned char *data)
301 {
302 ssize_t bytes_read = 0;
303 size_t retry = 0;
304
305 while ((bytes_read < 1) && (retry < 5)) {
306 send_read_command(command);
307 bytes_read = get_answer(data, command);
308 retry++;
309 }
310
311 if (bytes_read < 1) {
312 nutusb_comm_fail("Error executing command");
313 dstate_datastale();
314 return -1;
315 }
316
317 return bytes_read;
318 }
319
320 /* Sends a setup command (length > 1) */
command_write_sequence(unsigned char * command,size_t command_length,unsigned char * answer)321 ssize_t command_write_sequence(unsigned char *command, size_t command_length, unsigned char *answer)
322 {
323 ssize_t bytes_read = 0;
324 size_t retry = 0;
325
326 while ((bytes_read < 1) && (retry < 5)) {
327 send_write_command(command, command_length);
328 sleep(PW_SLEEP);
329 bytes_read = get_answer(answer, command[0]);
330 retry ++;
331 }
332
333 if (bytes_read < 1) {
334 nutusb_comm_fail("Error executing command");
335 dstate_datastale();
336 return -1;
337 }
338
339 return bytes_read;
340 }
341
342
upsdrv_comm_good(void)343 void upsdrv_comm_good(void)
344 {
345 nutusb_comm_good();
346 }
347
upsdrv_initups(void)348 void upsdrv_initups(void)
349 {
350 upsdev = nutusb_open("USB");
351 }
352
upsdrv_cleanup(void)353 void upsdrv_cleanup(void)
354 {
355 upslogx(LOG_ERR, "CLOSING\n");
356 nutusb_close(upsdev, "USB");
357 }
358
upsdrv_reconnect(void)359 void upsdrv_reconnect(void)
360 {
361 upsdebugx(4, "==================================================");
362 upsdebugx(4, "= device has been disconnected, try to reconnect =");
363 upsdebugx(4, "==================================================");
364
365 nutusb_close(upsdev, "USB");
366 upsdev = NULL;
367 upsdrv_initups();
368 }
369
370 /* USB functions */
371 static void nutusb_open_error(const char *port)
372 __attribute__((noreturn));
373
nutusb_open_error(const char * port)374 static void nutusb_open_error(const char *port)
375 {
376 printf("Unable to find POWERWARE UPS device on USB bus (%s)\n\n", port);
377
378 printf("Things to try:\n\n");
379 printf(" - Connect UPS device to USB bus\n\n");
380 printf(" - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n");
381 printf(" See upsdrvctl(8) and ups.conf(5).\n\n");
382
383 fatalx(EXIT_FAILURE, "Fatal error: unusable configuration");
384 }
385
386 /* FIXME: this part of the opening can go into common... */
open_powerware_usb(void)387 static usb_dev_handle *open_powerware_usb(void)
388 {
389 struct usb_bus *busses = usb_get_busses();
390 struct usb_bus *bus;
391 USBDevice_t curDevice;
392
393 for (bus = busses; bus; bus = bus->next)
394 {
395 struct usb_device *dev;
396
397 for (dev = bus->devices; dev; dev = dev->next)
398 {
399 if (dev->descriptor.bDeviceClass != USB_CLASS_PER_INTERFACE) {
400 continue;
401 }
402
403 curDevice.VendorID = dev->descriptor.idVendor;
404 curDevice.ProductID = dev->descriptor.idProduct;
405 curDevice.Bus = strdup(bus->dirname);
406
407 /* FIXME: we should also retrieve
408 * dev->descriptor.iManufacturer
409 * dev->descriptor.iProduct
410 * dev->descriptor.iSerialNumber
411 * as in libusb.c->libusb_open()
412 * This is part of the things to put in common... */
413
414 if (is_usb_device_supported(pw_usb_device_table, &curDevice) == SUPPORTED) {
415 return usb_open(dev);
416 }
417 }
418 }
419 return 0;
420 }
421
nutusb_open(const char * port)422 usb_dev_handle *nutusb_open(const char *port)
423 {
424 int dev_claimed = 0;
425 usb_dev_handle *dev_h = NULL;
426 int retry, errout = 0;
427
428 upsdebugx(1, "entering nutusb_open()");
429
430 /* Initialize Libusb */
431 usb_init();
432 usb_find_busses();
433 usb_find_devices();
434
435 for (retry = 0; retry < MAX_TRY ; retry++)
436 {
437 dev_h = open_powerware_usb();
438 if (!dev_h) {
439 upsdebugx(1, "Can't open POWERWARE USB device");
440 errout = 1;
441 }
442 else {
443 upsdebugx(1, "device %s opened successfully", usb_device(dev_h)->filename);
444 errout = 0;
445
446 if (usb_claim_interface(dev_h, 0) < 0)
447 {
448 upsdebugx(1, "Can't claim POWERWARE USB interface: %s", usb_strerror());
449 errout = 1;
450 }
451 else {
452 dev_claimed = 1;
453 errout = 0;
454 }
455 /* FIXME: the above part of the opening can go into common... up to here at least */
456
457 if (usb_clear_halt(dev_h, 0x81) < 0)
458 {
459 upsdebugx(1, "Can't reset POWERWARE USB endpoint: %s", usb_strerror());
460 if (dev_claimed)
461 usb_release_interface(dev_h, 0);
462 usb_reset(dev_h);
463 sleep(5); /* Wait reconnect */
464 errout = 1;
465 }
466 else
467 errout = 0;
468 }
469
470 /* Test if we succeeded */
471 if ( (dev_h != NULL) && dev_claimed && (errout == 0) )
472 break;
473 else {
474 /* Clear errors, and try again */
475 errout = 0;
476 }
477 }
478
479 if (!dev_h && !dev_claimed && retry == MAX_TRY)
480 errout = 1;
481 else
482 return dev_h;
483
484 if (dev_h && dev_claimed)
485 usb_release_interface(dev_h, 0);
486
487 if (dev_h)
488 usb_close(dev_h);
489
490 if (errout == 1)
491 nutusb_open_error(port);
492
493 return NULL;
494 }
495
496 /* FIXME: this part can go into common... */
nutusb_close(usb_dev_handle * dev_h,const char * port)497 int nutusb_close(usb_dev_handle *dev_h, const char *port)
498 {
499 NUT_UNUSED_VARIABLE(port);
500
501 if (dev_h)
502 {
503 usb_release_interface(dev_h, 0);
504 return usb_close(dev_h);
505 }
506
507 return 0;
508 }
509
nutusb_comm_fail(const char * fmt,...)510 void nutusb_comm_fail(const char *fmt, ...)
511 {
512 int ret;
513 char why[SMALLBUF];
514 va_list ap;
515
516 /* this means we're probably here because select was interrupted */
517 if (exit_flag != 0)
518 return; /* ignored, since we're about to exit anyway */
519
520 comm_failures++;
521
522 if ((comm_failures == USB_ERR_LIMIT) ||
523 ((comm_failures % USB_ERR_RATE) == 0))
524 {
525 upslogx(LOG_WARNING, "Warning: excessive comm failures, "
526 "limiting error reporting");
527 }
528
529 /* once it's past the limit, only log once every USB_ERR_LIMIT calls */
530 if ((comm_failures > USB_ERR_LIMIT) &&
531 ((comm_failures % USB_ERR_LIMIT) != 0)) {
532 /* Try reconnection */
533 upsdebugx(1, "Got to reconnect!\n");
534 upsdrv_reconnect();
535 return;
536 }
537
538 /* generic message if the caller hasn't elaborated */
539 if (!fmt)
540 {
541 upslogx(LOG_WARNING, "Communications with UPS lost - check cabling");
542 return;
543 }
544
545 va_start(ap, fmt);
546 ret = vsnprintf(why, sizeof(why), fmt, ap);
547 va_end(ap);
548
549 if ((ret < 1) || (ret >= (int) sizeof(why)))
550 upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed "
551 "more than %d bytes", (int)sizeof(why));
552
553 upslogx(LOG_WARNING, "Communications with UPS lost: %s", why);
554 }
555
nutusb_comm_good(void)556 void nutusb_comm_good(void)
557 {
558 if (comm_failures == 0)
559 return;
560
561 upslogx(LOG_NOTICE, "Communications with UPS re-established");
562 comm_failures = 0;
563 }
564