diff --git a/docs/acknowledgements.txt b/docs/acknowledgements.txt index 822e3c286b..f8b663bd39 100644 --- a/docs/acknowledgements.txt +++ b/docs/acknowledgements.txt @@ -89,6 +89,10 @@ provide you with official support from Eaton, or a better level of device support in NUT.* ================================================================================ +* link:https://www.ametek.com/products/businessunits/powersystemsandinstruments/powervar[AMETEK +Powervar], through Andrew McCartney, has added support for all AMETEK Powervar +UPM models as usb-hid UPS. + * link:http://www.gamatronic.com[Gamatronic], through Nadav Moskovitch, has revived the 'sec' driver (as gamatronic), and expanded a bit genericups for its UPSs with alarm interface. diff --git a/docs/man/usbhid-ups.txt b/docs/man/usbhid-ups.txt index a54325e3df..805173eccc 100644 --- a/docs/man/usbhid-ups.txt +++ b/docs/man/usbhid-ups.txt @@ -26,6 +26,7 @@ At the present time, usbhid-ups supports: - the newer Eaton USB models, - all MGE USB models, - all Dell USB models, + - all AMETEK Powervar UPM models, - some APC models, - some Belkin models, - some Cyber Power Systems models, diff --git a/docs/nut.dict b/docs/nut.dict index 7a49d100ad..a354f083db 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -16,6 +16,7 @@ AEG AES AGM AIX +AMETEK APC's API APIs @@ -801,6 +802,7 @@ PowerVault PowerWalker PowerWare Powerchute +Powervar Powerwell Prachi PresentStatus @@ -1075,6 +1077,7 @@ UINT UNKCOMMAND UNV UPGUARDS +UPM UPOII UPS's UPSCONN diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 610f051bcd..7fbab73423 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -175,7 +175,7 @@ skel_LDADD = $(LDADD_DRIVERS) # USB USBHID_UPS_SUBDRIVERS = apc-hid.c belkin-hid.c cps-hid.c explore-hid.c \ liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c \ - openups-hid.c + openups-hid.c powervar-hid.c usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ usb-common.c $(USBHID_UPS_SUBDRIVERS) usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm @@ -287,7 +287,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ xppc-mib.h huawei-mib.h eaton-ats16-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ apc-pdu-mib.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h \ eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h \ - hpe-pdu-mib.h + hpe-pdu-mib.h powervar-hid.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, diff --git a/drivers/libusb.c b/drivers/libusb.c index 8258961dac..bc899d5b87 100644 --- a/drivers/libusb.c +++ b/drivers/libusb.c @@ -47,6 +47,9 @@ upsdrv_info_t comm_upsdrv_info = { #define MAX_REPORT_SIZE 0x1800 +/* Used for Powervar UPS to make sure we use the right interface in the Composite device */ +int hid_rep_index = 0; + static void libusb_close(usb_dev_handle *udev); /*! Add USB-related driver variables with addvar(). @@ -239,6 +242,11 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); + /* Powervar UPS is a composite device. Index must be 1 to select the right interface */ + if ((curDevice->VendorID == 0x4234) && (curDevice->ProductID == 0x0002)) { + hid_rep_index = 1; + upsdebugx(2, "Set rep index to 1"); + } if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { hid_desc_index = 1; } @@ -275,11 +283,11 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice * it force device claiming by unbinding * attached driver... From libhid */ retries = 3; - while (usb_claim_interface(udev, 0) < 0) { + while (usb_claim_interface(udev, hid_rep_index) < 0) { upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); - if (usb_detach_kernel_driver_np(udev, 0) < 0) { + if (usb_detach_kernel_driver_np(udev, hid_rep_index) < 0) { upsdebugx(2, "failed to detach kernel driver from USB device: %s", usb_strerror()); } else { upsdebugx(2, "detached kernel driver from USB device..."); @@ -292,7 +300,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #else - if (usb_claim_interface(udev, 0) < 0) { + if (usb_claim_interface(udev, hid_rep_index) < 0) { fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); } #endif @@ -316,7 +324,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice /* FIRST METHOD: ask for HID descriptor directly. */ /* res = usb_get_descriptor(udev, USB_DT_HID, hid_desc_index, buf, 0x9); */ res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, - (USB_DT_HID << 8) + hid_desc_index, 0, buf, 0x9, USB_TIMEOUT); + (USB_DT_HID << 8) + hid_desc_index, hid_rep_index, buf, 0x9, USB_TIMEOUT); if (res < 0) { upsdebugx(2, "Unable to get HID descriptor (%s)", usb_strerror()); @@ -343,7 +351,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice /* for now, we always assume configuration 0, interface 0, altsetting 0, as above. */ - iface = &dev->config[0].interface[0].altsetting[0]; + iface = &dev->config[0].interface[hid_rep_index].altsetting[0]; for (i=0; iextralen; i+=iface->extra[i]) { upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, iface->extra[i], iface->extra[i+1]); @@ -389,7 +397,7 @@ static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDevice /* res = usb_get_descriptor(udev, USB_DT_REPORT, hid_desc_index, bigbuf, rdlen); */ res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, - (USB_DT_REPORT << 8) + hid_desc_index, 0, rdbuf, rdlen, USB_TIMEOUT); + (USB_DT_REPORT << 8) + hid_desc_index, hid_rep_index, rdbuf, rdlen, USB_TIMEOUT); if (res < 0) { @@ -486,7 +494,7 @@ static int libusb_get_report(usb_dev_handle *udev, int ReportId, unsigned char * USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x01, /* HID_REPORT_GET */ ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ - 0, raw_buf, ReportSize, USB_TIMEOUT); + hid_rep_index, raw_buf, ReportSize, USB_TIMEOUT); /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { @@ -508,7 +516,7 @@ static int libusb_set_report(usb_dev_handle *udev, int ReportId, unsigned char * USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, /* HID_REPORT_SET = 0x09*/ ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ - 0, raw_buf, ReportSize, USB_TIMEOUT); + hid_rep_index, raw_buf, ReportSize, USB_TIMEOUT); /* Ignore "protocol stall" (for unsupported request) on control endpoint */ if (ret == -EPIPE) { diff --git a/drivers/powervar-hid.c b/drivers/powervar-hid.c new file mode 100755 index 0000000000..8d5f478ca0 --- /dev/null +++ b/drivers/powervar-hid.c @@ -0,0 +1,141 @@ +/* powervar-hid.c - subdriver to monitor Powervar USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2019 AMETEK Powervar - Andrew McCartney + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-usbhid-subdriver script. It must be customized. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "usbhid-ups.h" +#include "powervar-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define POWERVAR_HID_VERSION "Powervar HID 0.19" +/* FIXME: experimental flag to be put in upsdrv_info */ + +/* Powervar */ +#define POWERVAR_VENDORID 0x4234 + +/* USB IDs device table */ +static usb_device_id_t powervar_usb_device_table[] = { + /* Powervar */ + { USB_DEVICE(POWERVAR_VENDORID, 0x0002), NULL }, + + /* Terminating entry */ + { -1, -1, NULL } +}; + + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* POWERVAR usage table */ +static usage_lkp_t powervar_usage_lkp[] = { + { "POWERVAR1", 0xff000001 }, + { NULL, 0 } +}; + +static usage_tables_t powervar_utab[] = { + powervar_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t powervar_hid2nut[] = { + + /* Battery page */ + { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.capacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RuntimeToEmpty", NULL, "%.0f", 0, NULL }, + +/* Special case: ups.status & ups.alarm */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, 0, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, 0, yes_no_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, 0, yes_no_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, 0, fullycharged_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyDischarged", NULL, NULL, 0, depleted_info }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *powervar_format_model(HIDDevice_t *hd) { + return hd->Product; +} + +static const char *powervar_format_mfr(HIDDevice_t *hd) { + return hd->Vendor ? hd->Vendor : "Powervar"; +} + +static const char *powervar_format_serial(HIDDevice_t *hd) { + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int powervar_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(powervar_usb_device_table, hd); + + switch (status) + { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + possibly_supported("Powervar", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t powervar_subdriver = { + POWERVAR_HID_VERSION, + powervar_claim, + powervar_utab, + powervar_hid2nut, + powervar_format_model, + powervar_format_mfr, + powervar_format_serial, +}; diff --git a/drivers/powervar-hid.h b/drivers/powervar-hid.h new file mode 100755 index 0000000000..f86d5b87ce --- /dev/null +++ b/drivers/powervar-hid.h @@ -0,0 +1,30 @@ +/* powervar-hid.h - subdriver to monitor Powervar USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef POWERVAR_HID_H +#define POWERVAR_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t powervar_subdriver; + +#endif /* POWERVAR_HID_H */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 254427ce40..d0090a56f2 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -49,6 +49,7 @@ #include "tripplite-hid.h" #include "idowell-hid.h" #include "openups-hid.h" + #include "powervar-hid.h" #endif /* master list of avaiable subdrivers */ @@ -66,6 +67,7 @@ static subdriver_t *subdriver_list[] = { &tripplite_subdriver, &idowell_subdriver, &openups_subdriver, + &powervar_subdriver, #endif NULL };