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