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
26 #include "usb-common.h"
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
= {
36 "Peter van Valderen <p.v.valderen@probu.nl>\n"
37 "Dirk Teurlings <dirk@upexia.nl>",
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
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
[] = {
56 { USB_DEVICE(0x0925, 0x1234), 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
))
73 case POSSIBLY_SUPPORTED
:
80 static USBDeviceMatcher_t device_matcher
= {
86 static int execute_and_retrieve_query(char *query
, char *reply
)
90 ret
= usb_control_msg(udev
, STATUS_REQUESTTYPE
, REQUEST_VALUE
,
91 MESSAGE_VALUE
, INDEX_VALUE
, query
, QUERY_PACKETSIZE
, 1000);
94 upsdebugx(3, "send: %s", ret
? usb_strerror() : "timeout");
98 upsdebug_hex(3, "send", query
, ret
);
100 ret
= usb_interrupt_read(udev
, REPLY_REQUESTTYPE
, reply
, REPLY_PACKETSIZE
, 1000);
103 upsdebugx(3, "read: %s", ret
? usb_strerror() : "timeout");
107 upsdebug_hex(3, "read", reply
, 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
, ...)
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 */
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)) {
143 /* generic message if the caller hasn't elaborated */
145 upslogx(LOG_WARNING
, "Communications with UPS lost - check cabling");
150 ret
= vsnprintf(why
, sizeof(why
), fmt
, 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) {
166 upslogx(LOG_NOTICE
, "Communications with UPS re-established");
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
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");
184 if (usb_claim_interface(handle
, 0) < 0) {
185 upsdebugx(5, "Can't claim USB interface");
189 if (usb_set_altinterface(handle
, 0) < 0) {
190 upsdebugx(5, "Can't set USB alternate interface");
194 if (usb_clear_halt(handle
, 0x81) < 0) {
195 upsdebugx(5, "Can't reset USB endpoint");
202 static int usb_device_close(usb_dev_handle
*handle
)
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
))
219 /* libusb base init */
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
);
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
) {
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
);
247 upsdebugx(4, "Failed to open USB device, skipping: %s", usb_strerror());
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
);
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
) {
270 ret
= usb_get_string_simple(handle
, dev
->descriptor
.iManufacturer
,
273 device
->Vendor
= strdup(buf
);
277 if (dev
->descriptor
.iProduct
) {
279 ret
= usb_get_string_simple(handle
, dev
->descriptor
.iProduct
,
282 device
->Product
= strdup(buf
);
286 if (dev
->descriptor
.iSerialNumber
) {
288 ret
= usb_get_string_simple(handle
, dev
->descriptor
.iSerialNumber
,
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
))
307 upsdebugx(4, "Device does not match - skipping");
310 fatal_with_errno(EXIT_FAILURE
, "matcher");
313 upsdebugx(4, "matcher: unspecified error");
318 for (i
= 0; i
< 3; i
++) {
320 ret
= callback(handle
, device
);
322 upsdebugx(4, "USB device [%04x:%04x] opened", device
->VendorID
, device
->ProductID
);
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());
332 upsdebugx(4, "detached kernel driver from USB device...");
337 fatalx(EXIT_FAILURE
, "USB device [%04x:%04x] matches, but driver callback failed: %s",
338 device
->VendorID
, device
->ProductID
, usb_strerror());
346 upsdebugx(4, "No matching USB device found");
354 void upsdrv_initups(void)
356 char reply
[REPLY_PACKETSIZE
];
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 ...");
367 "Unable to find Richcomm dry-contact to USB solution\n\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
++) {
387 void upsdrv_cleanup(void)
389 usb_device_close(udev
);
391 free(usbdevice
.Vendor
);
392 free(usbdevice
.Product
);
393 free(usbdevice
.Serial
);
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
;
413 ret
= usb_device_open(&udev
, &usbdevice
, &device_matcher
, &driver_callback
);
420 ret
= query_ups(reply
);
423 usb_comm_fail("Query to UPS failed");
426 usb_device_close(udev
);
436 * 3rd bit of 4th byte indicates whether the UPS is on line (1)
439 online
= (reply
[3]&4)>>2;
442 * 2nd bit of 4th byte indicates battery status; normal (1)
445 battery_normal
= (reply
[3]&2)>>1;
455 if (!battery_normal
) {
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
501 execute_and_retrieve_query(restart
, reply
);
504 void upsdrv_help(void)
508 void upsdrv_makevartable(void)