From f8546df4c18c087eac829d3dfc097c919abb6d1c Mon Sep 17 00:00:00 2001 From: aquette Date: Wed, 28 Nov 2012 21:44:21 +0000 Subject: [PATCH] Official support for Minibox openUPS Intelligent UPS Add a new usbhid-ups subdriver to handle Minibox openUPS Intelligent UPS (USB ID 0x04d8:0xd004) (patch from Nicu Pavel, Mini-Box.Com) git-svn-id: svn://anonscm.debian.org/nut/trunk@3791 72a954d1-e00c-0410-aa02-d9c7bb700a61 --- data/driver.list.in | 2 + drivers/Makefile.am | 5 +- drivers/openups-hid.c | 360 +++++++++++++++++++++++++++++++++++++ drivers/openups-hid.h | 38 ++++ drivers/usbhid-ups.c | 5 +- scripts/upower/95-upower-hid.rules | 4 + 6 files changed, 411 insertions(+), 3 deletions(-) create mode 100644 drivers/openups-hid.c create mode 100644 drivers/openups-hid.h diff --git a/data/driver.list.in b/data/driver.list.in index 11014d6..70b5e0f 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -645,6 +645,8 @@ "Microsol" "ups" "5" "Rhino 10.0" "10000VA" "rhino" "Microsol" "ups" "5" "Rhino 20.0" "20000VA" "rhino" +"Minibox" "ups" "5" "openUPS Intelligent UPS" "USB port" "usbhid-ups" + "Mustek" "ups" "2" "Powermust" "400VA Plus" "blazer_ser" "Mustek" "ups" "2" "Powermust" "600VA Plus" "blazer_ser" "Mustek" "ups" "2" "Powermust" "800VA Pro" "blazer_ser" diff --git a/drivers/Makefile.am b/drivers/Makefile.am index cd900cf..ad30874 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -162,7 +162,8 @@ 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 + liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c \ + openups-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) @@ -245,7 +246,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \ powercom.h powerpanel.h powerp-bin.h powerp-txt.h powerware-mib.h raritan-pdu-mib.h \ safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ - apcsmart.h apcsmart_tabs.h apcsmart-old.h cyberpower-mib.h + apcsmart.h apcsmart_tabs.h apcsmart-old.h cyberpower-mib.h openups-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/openups-hid.c b/drivers/openups-hid.c new file mode 100644 index 0000000..69a4a61 --- /dev/null +++ b/drivers/openups-hid.c @@ -0,0 +1,360 @@ +/* openups-hid.c - subdriver to monitor Minibox openUPS USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2012 Nicu Pavel + * + * 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 "openups-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define OPENUPS_HID_VERSION "openUPS HID 0.1" + +/* Minibox */ +#define OPENUPS_VENDORID 0x04d8 + +static char openups_scratch_buf[20]; + +/* USB IDs device table */ +static usb_device_id_t openups_usb_device_table[] = { + /* openUPS Intelligent UPS (minimum required firmware 1.4) */ + {USB_DEVICE(OPENUPS_VENDORID, 0xd004), NULL}, + + /* Terminating entry */ + {-1, -1, NULL} +}; + +/* Thermistor table used for temperature lookups + * taken from the windows monitoring application + */ +static unsigned int therm_tbl[] = +{ + (unsigned int)0x31, + (unsigned int)0x40, + (unsigned int)0x53, + (unsigned int)0x68, + (unsigned int)0x82, + (unsigned int)0xA0, + (unsigned int)0xC3, + (unsigned int)0xE9, + (unsigned int)0x113, + (unsigned int)0x13F, + (unsigned int)0x16E, + (unsigned int)0x19F, + (unsigned int)0x1CF, + (unsigned int)0x200, + (unsigned int)0x22F, + (unsigned int)0x25C, + (unsigned int)0x286, + (unsigned int)0x2AE, + (unsigned int)0x2D3, + (unsigned int)0x2F4, + (unsigned int)0x312, + (unsigned int)0x32D, + (unsigned int)0x345, + (unsigned int)0x35A, + (unsigned int)0x36D, + (unsigned int)0x37E, + (unsigned int)0x38C, + (unsigned int)0x399, + (unsigned int)0x3A5, + (unsigned int)0x3AF, + (unsigned int)0x3B7, + (unsigned int)0x3BF, + (unsigned int)0x3C6, + (unsigned int)0x3CC +}; + +static unsigned int therm_tbl_size = sizeof(therm_tbl)/sizeof(therm_tbl[0]); + +static const char *openups_charging_fun(double value); +static const char *openups_discharging_fun(double value); +static const char *openups_online_fun(double value); +static const char *openups_nobattery_fun(double value); +static const char *openups_off_fun(double value); + +static const char *openups_scale_vin_fun(double value); +static const char *openups_scale_vout_fun(double value); +/* static const char *openups_scale_vbat_fun(double value); */ +static const char *openups_scale_ccharge_fun(double value); +static const char *openups_scale_cdischarge_fun(double value); +static const char *openups_temperature_fun(double value); + +static info_lkp_t openups_charging_info[] = { + {0, NULL, openups_charging_fun} +}; + +static info_lkp_t openups_discharging_info[] = { + {0, NULL, openups_discharging_fun} +}; + +static info_lkp_t openups_online_info[] = { + {0, NULL, openups_online_fun} +}; + +static info_lkp_t openups_nobattery_info[] = { + {0, NULL, openups_nobattery_fun} +}; + +static info_lkp_t openups_off_info[] = { + {0, NULL, openups_off_fun} +}; + +static info_lkp_t openups_vin_info[] = { + {0, NULL, openups_scale_vin_fun} +}; + +static info_lkp_t openups_vout_info[] = { + {0, NULL, openups_scale_vout_fun} +}; + +/* static info_lkp_t openups_vbat_info[] = { + {0, NULL, openups_scale_vbat_fun} +};*/ + +static info_lkp_t openups_ccharge_info[] = { + {0, NULL, openups_scale_ccharge_fun} +}; + +static info_lkp_t openups_cdischarge_info[] = { + {0, NULL, openups_scale_cdischarge_fun} +}; + +static info_lkp_t openups_temperature_info[] = { + {0, NULL, openups_temperature_fun} +}; + +static const char *openups_charging_fun(double value) +{ + return value ? "chrg" : "!chrg"; +} + +static const char *openups_discharging_fun(double value) +{ + return value ? "dischrg" : "!dischrg"; +} + +static const char *openups_online_fun(double value) +{ + return value ? "online" : "!online"; +} + +static const char *openups_nobattery_fun(double value) +{ + return value ? "nobattery" : "!nobattery"; +} + +static const char *openups_off_fun(double value) +{ + return value ? "!off" : "off"; +} + +static const char *openups_scale_vin_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vin_scale); + return openups_scratch_buf; +} + +static const char *openups_scale_vout_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vout_scale); + return openups_scratch_buf; +} + +/* static const char *openups_scale_vbat_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vbat_scale); + return openups_scratch_buf; +}*/ + +static const char *openups_scale_ccharge_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.3f", value * ccharge_scale); + return openups_scratch_buf; +} + +static const char *openups_scale_cdischarge_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.3f", value * cdischarge_scale); + return openups_scratch_buf; +} + +static const char *openups_temperature_fun(double value) +{ + int i; + int pos = -1; + unsigned int thermistor = value * 100; + + if (thermistor <= therm_tbl[0]) { + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", -40); + } else { + if (thermistor >= therm_tbl[therm_tbl_size - 1]) { + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", 125); + } else { + for (i = therm_tbl_size - 1; i >= 0; i--) { + if (thermistor >= therm_tbl[i]) { + pos = i; + break; + } + } + + if (thermistor == therm_tbl[pos]) { + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", pos * 5 - 40); + } else { + int t1 = pos * 5 - 40; + int t2 = (pos + 1) * 5 - 40; + + unsigned int d1 = therm_tbl[pos]; + unsigned int d2 = therm_tbl[pos + 1]; + + float temp = (float) (thermistor - d1) * (t2 - t1) / (d2 - d1) + t1; + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", temp); + } + } + } + + return openups_scratch_buf; +} + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* OPENUPS usage table */ +static usage_lkp_t openups_usage_lkp[] = { + {"Cell1", 0x00000001}, /* Battery cell 1 on J6 pin 1 */ + {"Cell2", 0x00000002}, /* Battery cell 2 on J6 pin 2 */ + {"Cell3", 0x00000003}, /* Battery cell 3 on J6 pin 3 */ + {"Cell4", 0x00000004}, /* Battery cell 4 on J6 pin 4 */ + {"Cell5", 0x00000005}, /* Battery cell 5 on J6 pin 5 */ + {"Cell6", 0x00000006}, /* Battery cell 6 on J4 pin 1 */ + /* Usage table for windows monitoring app only updates when + * certain request codes are written to USB endpoint */ + /*{ "OpenUPSExtra", 0xff000001 }, */ + {NULL, 0} +}; + +static usage_tables_t openups_utab[] = { + openups_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t openups_hid2nut[] = { + {"ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion}, + + /* Battery */ + {"battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion}, + {"battery.mfr.date", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", 0, stringid_conversion}, + {"battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", HU_FLAG_QUICK_POLL, NULL}, + /* { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info }, */ + {"battery.current", 0, 0, "UPS.PowerSummary.Current", NULL, "%.3f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.capacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", HU_FLAG_STATIC, NULL}, + {"battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL}, + {"battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.temperature", 0, 0, "UPS.PowerSummary.Temperature", NULL, NULL, HU_FLAG_QUICK_POLL, openups_temperature_info}, +/* {"battery.cell1.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell1", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell2.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell2", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell3.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell3", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell4.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell4", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell5.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell5", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell6.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell6", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, +*/ + /* Output */ + {"output.voltage", 0, 0, "UPS.PowerSummary.Output.Voltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vout_info}, + {"output.current", 0, 0, "UPS.PowerSummary.Output.Current", NULL, NULL, HU_FLAG_QUICK_POLL, openups_cdischarge_info}, + + /* Input */ + {"input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vin_info}, + {"input.current", 0, 0, "UPS.PowerSummary.Input.Current", NULL, NULL, HU_FLAG_QUICK_POLL, openups_ccharge_info}, + + /* Status */ + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, HU_FLAG_QUICK_POLL, openups_off_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, HU_FLAG_QUICK_POLL, commfault_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, HU_FLAG_QUICK_POLL, overheat_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, HU_FLAG_QUICK_POLL, shutdownimm_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, HU_FLAG_QUICK_POLL, timelimitexpired_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, openups_charging_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, openups_discharging_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, openups_online_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, HU_FLAG_QUICK_POLL, openups_nobattery_info}, + + /* end of structure. */ + {NULL, 0, 0, NULL, NULL, NULL, 0, NULL} +}; + +static const char *openups_format_model(HIDDevice_t * hd) +{ + return hd->Product; +} + +static const char *openups_format_mfr(HIDDevice_t * hd) +{ + return hd->Vendor ? hd->Vendor : "openUPS"; +} + +static const char *openups_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 openups_claim(HIDDevice_t * hd) +{ + int status = is_usb_device_supported(openups_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("openUPS", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t openups_subdriver = { + OPENUPS_HID_VERSION, + openups_claim, + openups_utab, + openups_hid2nut, + openups_format_model, + openups_format_mfr, + openups_format_serial, +}; diff --git a/drivers/openups-hid.h b/drivers/openups-hid.h new file mode 100644 index 0000000..c87b3d1 --- /dev/null +++ b/drivers/openups-hid.h @@ -0,0 +1,38 @@ +/* openups-hid.h - subdriver to monitor Minibox openUPS USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2012 Nicu Pavel + * + * 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 OPENUPS_HID_H +#define OPENUPS_HID_H + +#include "usbhid-ups.h" + +/* constants for converting HID read values to real values */ +static const float vin_scale = 0.03545 * 100; +static const float vout_scale = 0.02571 * 100; +static const float vbat_scale = 0.00857 * 100; +static const float ccharge_scale = 0.8274 / 10; +static const float cdischarge_scale = 16.113 / 10; + +extern subdriver_t openups_subdriver; + +#endif /* OPENUPS_HID_H */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 2d254c4..fcf628d 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -27,7 +27,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.37" +#define DRIVER_VERSION "0.38" #include "main.h" #include "libhid.h" @@ -37,6 +37,7 @@ /* include all known subdrivers */ #include "mge-hid.h" + #ifndef SHUT_MODE #include "explore-hid.h" #include "apc-hid.h" @@ -46,6 +47,7 @@ #include "powercom-hid.h" #include "tripplite-hid.h" #include "idowell-hid.h" + #include "openups-hid.h" #endif /* master list of avaiable subdrivers */ @@ -62,6 +64,7 @@ static subdriver_t *subdriver_list[] = { &powercom_subdriver, &tripplite_subdriver, &idowell_subdriver, + &openups_subdriver, #endif NULL }; diff --git a/scripts/upower/95-upower-hid.rules b/scripts/upower/95-upower-hid.rules index a9e18fe..c57ef75 100644 --- a/scripts/upower/95-upower-hid.rules +++ b/scripts/upower/95-upower-hid.rules @@ -15,6 +15,7 @@ ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end" ATTRS{idVendor}=="03f0", ENV{UPOWER_VENDOR}="Hewlett Packard" ATTRS{idVendor}=="0463", ENV{UPOWER_VENDOR}="Eaton" ATTRS{idVendor}=="047c", ENV{UPOWER_VENDOR}="Dell" +ATTRS{idVendor}=="04d8", ENV{UPOWER_VENDOR}="Minibox" ATTRS{idVendor}=="050d", ENV{UPOWER_VENDOR}="Belkin" ATTRS{idVendor}=="051d", ENV{UPOWER_VENDOR}="APC" ATTRS{idVendor}=="0592", ENV{UPOWER_VENDOR}="Powerware" @@ -47,6 +48,9 @@ ATTRS{idVendor}=="0463", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups # Dell ATTRS{idVendor}=="047c", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" +# Minibox +ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="d004", ENV{UPOWER_BATTERY_TYPE}="ups" + # Belkin ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0375", ENV{UPOWER_BATTERY_TYPE}="ups" ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0551", ENV{UPOWER_BATTERY_TYPE}="ups" -- 1.8.0