apcupsd-ups: ignore generated files
[networkupstools/kirr.git] / drivers / richcomm_usb.c
blob0b9b8f274247d1c5ff71e49fac91b9a82af0d84a
1 /*
2 * richcomm_usb.c - driver for UPS with Richcomm dry-contact to USB
3 * solution, such as 'Sweex Manageable UPS 1000VA'
5 * May also work on 'Kebo UPS-650D', not tested as of 05/23/2007
7 * Copyright (C) 2007 Peter van Valderen <p.v.valderen@probu.nl>
8 * Dirk Teurlings <dirk@upexia.nl>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "main.h"
26 #include "usb-common.h"
28 /* driver version */
29 #define DRIVER_NAME "Richcomm dry-contact to USB driver"
30 #define DRIVER_VERSION "0.04"
32 /* driver description structure */
33 upsdrv_info_t upsdrv_info = {
34 DRIVER_NAME,
35 DRIVER_VERSION,
36 "Peter van Valderen <p.v.valderen@probu.nl>\n"
37 "Dirk Teurlings <dirk@upexia.nl>",
38 DRV_EXPERIMENTAL,
39 { NULL }
42 #define STATUS_REQUESTTYPE 0x21
43 #define REPLY_REQUESTTYPE 0x81
44 #define QUERY_PACKETSIZE 4
45 #define REPLY_PACKETSIZE 6
46 #define REQUEST_VALUE 0x09
47 #define MESSAGE_VALUE 0x200
48 #define INDEX_VALUE 0
50 /* limit the amount of spew that goes in the syslog when we lose the UPS (from nut_usb.h) */
51 #define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */
52 #define USB_ERR_RATE 10 /* then only print every 10th error */
54 static usb_device_id_t richcomm_usb_id[] = {
55 /* Sweex 1000VA */
56 { USB_DEVICE(0x0925, 0x1234), NULL },
58 /* end of list */
59 {-1, -1, NULL}
62 static usb_dev_handle *udev = NULL;
63 static USBDevice_t usbdevice;
64 static unsigned int comm_failures = 0;
66 static int device_match_func(USBDevice_t *device, void *privdata)
68 switch (is_usb_device_supported(richcomm_usb_id, device))
70 case SUPPORTED:
71 return 1;
73 case POSSIBLY_SUPPORTED:
74 case NOT_SUPPORTED:
75 default:
76 return 0;
80 static USBDeviceMatcher_t device_matcher = {
81 &device_match_func,
82 NULL,
83 NULL
86 static int execute_and_retrieve_query(char *query, char *reply)
88 int ret;
90 ret = usb_control_msg(udev, STATUS_REQUESTTYPE, REQUEST_VALUE,
91 MESSAGE_VALUE, INDEX_VALUE, query, QUERY_PACKETSIZE, 1000);
93 if (ret <= 0) {
94 upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
95 return ret;
98 upsdebug_hex(3, "send", query, ret);
100 ret = usb_interrupt_read(udev, REPLY_REQUESTTYPE, reply, REPLY_PACKETSIZE, 1000);
102 if (ret <= 0) {
103 upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
104 return ret;
107 upsdebug_hex(3, "read", reply, ret);
108 return ret;
111 static int query_ups(char *reply)
114 * This packet is a status request to the UPS
116 char query[QUERY_PACKETSIZE] = { 0x01, 0x00, 0x00, 0x30 };
118 return execute_and_retrieve_query(query, reply);
121 static void usb_comm_fail(const char *fmt, ...)
123 int ret;
124 char why[SMALLBUF];
125 va_list ap;
127 /* this means we're probably here because select was interrupted */
128 if (exit_flag != 0) {
129 return; /* ignored, since we're about to exit anyway */
132 comm_failures++;
134 if ((comm_failures == USB_ERR_LIMIT) || ((comm_failures % USB_ERR_RATE) == 0)) {
135 upslogx(LOG_WARNING, "Warning: excessive comm failures, limiting error reporting");
138 /* once it's past the limit, only log once every USB_ERR_LIMIT calls */
139 if ((comm_failures > USB_ERR_LIMIT) && ((comm_failures % USB_ERR_LIMIT) != 0)) {
140 return;
143 /* generic message if the caller hasn't elaborated */
144 if (!fmt) {
145 upslogx(LOG_WARNING, "Communications with UPS lost - check cabling");
146 return;
149 va_start(ap, fmt);
150 ret = vsnprintf(why, sizeof(why), fmt, ap);
151 va_end(ap);
153 if ((ret < 1) || (ret >= (int) sizeof(why))) {
154 upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed more than %d bytes", (int)sizeof(why));
157 upslogx(LOG_WARNING, "Communications with UPS lost: %s", why);
160 static void usb_comm_good(void)
162 if (comm_failures == 0) {
163 return;
166 upslogx(LOG_NOTICE, "Communications with UPS re-established");
167 comm_failures = 0;
171 * Callback that is called by usb_device_open() that handles USB device
172 * settings prior to accepting the devide. At the very least claim the
173 * device here. Detaching the kernel driver will be handled by the
174 * caller, don't do this here. Return < 0 on error, 0 or higher on
175 * success.
177 static int driver_callback(usb_dev_handle *handle, USBDevice_t *device)
179 if (usb_set_configuration(handle, 1) < 0) {
180 upsdebugx(5, "Can't set USB configuration");
181 return -1;
184 if (usb_claim_interface(handle, 0) < 0) {
185 upsdebugx(5, "Can't claim USB interface");
186 return -1;
189 if (usb_set_altinterface(handle, 0) < 0) {
190 upsdebugx(5, "Can't set USB alternate interface");
191 return -1;
194 if (usb_clear_halt(handle, 0x81) < 0) {
195 upsdebugx(5, "Can't reset USB endpoint");
196 return -1;
199 return 1;
202 static int usb_device_close(usb_dev_handle *handle)
204 if (!handle) {
205 return 0;
208 /* usb_release_interface() sometimes blocks and goes
209 into uninterruptible sleep. So don't do it. */
210 /* usb_release_interface(handle, 0); */
211 return usb_close(handle);
214 static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDeviceMatcher_t *matcher,
215 int (*callback)(usb_dev_handle *handle, USBDevice_t *device))
217 struct usb_bus *bus;
219 /* libusb base init */
220 usb_init();
221 usb_find_busses();
222 usb_find_devices();
224 #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */
225 /* Causes a double free corruption in linux if device is detached! */
226 usb_device_close(*handlep);
227 #endif
229 for (bus = usb_busses; bus; bus = bus->next) {
231 struct usb_device *dev;
232 usb_dev_handle *handle;
234 for (dev = bus->devices; dev; dev = dev->next) {
236 int i, ret;
237 USBDeviceMatcher_t *m;
239 upsdebugx(4, "Checking USB device [%04x:%04x] (%s/%s)", dev->descriptor.idVendor,
240 dev->descriptor.idProduct, bus->dirname, dev->filename);
242 /* supported vendors are now checked by the supplied matcher */
244 /* open the device */
245 *handlep = handle = usb_open(dev);
246 if (!handle) {
247 upsdebugx(4, "Failed to open USB device, skipping: %s", usb_strerror());
248 continue;
251 /* collect the identifying information of this
252 device. Note that this is safe, because
253 there's no need to claim an interface for
254 this (and therefore we do not yet need to
255 detach any kernel drivers). */
257 free(device->Vendor);
258 free(device->Product);
259 free(device->Serial);
260 free(device->Bus);
262 memset(device, 0, sizeof(*device));
264 device->VendorID = dev->descriptor.idVendor;
265 device->ProductID = dev->descriptor.idProduct;
266 device->Bus = strdup(bus->dirname);
268 if (dev->descriptor.iManufacturer) {
269 char buf[SMALLBUF];
270 ret = usb_get_string_simple(handle, dev->descriptor.iManufacturer,
271 buf, sizeof(buf));
272 if (ret > 0) {
273 device->Vendor = strdup(buf);
277 if (dev->descriptor.iProduct) {
278 char buf[SMALLBUF];
279 ret = usb_get_string_simple(handle, dev->descriptor.iProduct,
280 buf, sizeof(buf));
281 if (ret > 0) {
282 device->Product = strdup(buf);
286 if (dev->descriptor.iSerialNumber) {
287 char buf[SMALLBUF];
288 ret = usb_get_string_simple(handle, dev->descriptor.iSerialNumber,
289 buf, sizeof(buf));
290 if (ret > 0) {
291 device->Serial = strdup(buf);
295 upsdebugx(4, "- VendorID : %04x", device->VendorID);
296 upsdebugx(4, "- ProductID : %04x", device->ProductID);
297 upsdebugx(4, "- Manufacturer : %s", device->Vendor ? device->Vendor : "unknown");
298 upsdebugx(4, "- Product : %s", device->Product ? device->Product : "unknown");
299 upsdebugx(4, "- Serial Number: %s", device->Serial ? device->Serial : "unknown");
300 upsdebugx(4, "- Bus : %s", device->Bus ? device->Bus : "unknown");
302 for (m = matcher; m; m = m->next) {
304 switch (m->match_function(device, m->privdata))
306 case 0:
307 upsdebugx(4, "Device does not match - skipping");
308 goto next_device;
309 case -1:
310 fatal_with_errno(EXIT_FAILURE, "matcher");
311 goto next_device;
312 case -2:
313 upsdebugx(4, "matcher: unspecified error");
314 goto next_device;
318 for (i = 0; i < 3; i++) {
320 ret = callback(handle, device);
321 if (ret >= 0) {
322 upsdebugx(4, "USB device [%04x:%04x] opened", device->VendorID, device->ProductID);
323 return ret;
325 #ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP
326 /* this method requires at least libusb 0.1.8:
327 * it force device claiming by unbinding
328 * attached driver... From libhid */
329 if (usb_detach_kernel_driver_np(handle, 0) < 0) {
330 upsdebugx(4, "failed to detach kernel driver from USB device: %s", usb_strerror());
331 } else {
332 upsdebugx(4, "detached kernel driver from USB device...");
334 #endif
337 fatalx(EXIT_FAILURE, "USB device [%04x:%04x] matches, but driver callback failed: %s",
338 device->VendorID, device->ProductID, usb_strerror());
340 next_device:
341 usb_close(handle);
345 *handlep = NULL;
346 upsdebugx(4, "No matching USB device found");
348 return -1;
352 * Initialise the UPS
354 void upsdrv_initups(void)
356 char reply[REPLY_PACKETSIZE];
357 int i;
359 for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) {
361 if ((i < 32) && (sleep(5) == 0)) {
362 usb_comm_fail("Can't open USB device, retrying ...");
363 continue;
366 fatalx(EXIT_FAILURE,
367 "Unable to find Richcomm dry-contact to USB solution\n\n"
369 "Things to try:\n"
370 " - Connect UPS device to USB bus\n"
371 " - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n"
372 " See upsdrvctl(8) and ups.conf(5).\n\n"
374 "Fatal error: unusable configuration");
378 * Read rubbish data a few times; the UPS doesn't seem to respond properly
379 * the first few times after connecting
381 for (i = 0; i < 5; i++) {
382 query_ups(reply);
383 sleep(1);
387 void upsdrv_cleanup(void)
389 usb_device_close(udev);
391 free(usbdevice.Vendor);
392 free(usbdevice.Product);
393 free(usbdevice.Serial);
394 free(usbdevice.Bus);
397 void upsdrv_initinfo(void)
399 dstate_setinfo("ups.mfr", "%s", "Richcomm dry-contact to USB solution");
400 dstate_setinfo("ups.model", "%s", usbdevice.Product ? usbdevice.Product : "unknown");
401 dstate_setinfo("ups.serial", "%s", usbdevice.Serial ? usbdevice.Serial : "unknown");
403 dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID);
404 dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID);
407 void upsdrv_updateinfo(void)
409 char reply[REPLY_PACKETSIZE];
410 int ret, online, battery_normal;
412 if (!udev) {
413 ret = usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback);
415 if (ret < 0) {
416 return;
420 ret = query_ups(reply);
422 if (ret < 4) {
423 usb_comm_fail("Query to UPS failed");
424 dstate_datastale();
426 usb_device_close(udev);
427 udev = NULL;
429 return;
432 usb_comm_good();
433 dstate_dataok();
436 * 3rd bit of 4th byte indicates whether the UPS is on line (1)
437 * or on battery (0)
439 online = (reply[3]&4)>>2;
442 * 2nd bit of 4th byte indicates battery status; normal (1)
443 * or low (0)
445 battery_normal = (reply[3]&2)>>1;
447 status_init();
449 if (online) {
450 status_set("OL");
451 } else {
452 status_set("OB");
455 if (!battery_normal) {
456 status_set("LB");
459 status_commit();
463 * The shutdown feature is a bit strange on this UPS IMHO, it
464 * switches the polarity of the 'Shutdown UPS' signal, at which
465 * point it will automatically power down once it loses power.
467 * It will still, however, be possible to poll the UPS and
468 * reverse the polarity _again_, at which point it will
469 * start back up once power comes back.
471 * Maybe this is the normal way, it just seems a bit strange.
473 * Please note, this function doesn't power the UPS off if
474 * line power is connected.
476 void upsdrv_shutdown(void)
479 * This packet shuts down the UPS, that is,
480 * if it is not currently on line power
482 char prepare[QUERY_PACKETSIZE] = { 0x02, 0x00, 0x00, 0x00 };
485 * This should make the UPS turn itself back on once the
486 * power comes back on; which is probably what we want
488 char restart[QUERY_PACKETSIZE] = { 0x02, 0x01, 0x00, 0x00 };
489 char reply[REPLY_PACKETSIZE];
491 execute_and_retrieve_query(prepare, reply);
494 * have to, the previous command seems to be
495 * ignored if the second command comes right
496 * behind it
498 sleep(1);
501 execute_and_retrieve_query(restart, reply);
504 void upsdrv_help(void)
508 void upsdrv_makevartable(void)