diff --git a/data/driver.list.in b/data/driver.list.in index 8c547ebf11..6a83d64f08 100644 --- a/data/driver.list.in +++ b/data/driver.list.in @@ -517,7 +517,43 @@ "LDLC" "ups" "2" "UPS-1200D" "" "blazer_usb langid_fix=0x4095" -"Legrand" "ups" "2" "Keor Multiplug" "" "nutdrv_qx" +"Legrand" "ups" "2" "Daker DK" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Daker DK" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Daker DK Plus" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Daker DK Plus" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor Line RT" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Keor Line RT" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor LP" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Keor Multiplug" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor S" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Keor S" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Keor PDU" "USB" "usbhid-ups" +"Legrand" "ups" "2" "Keor SP" "USB" "usbhid-ups" +"Legrand" "ups" "2" "Keor SPX" "USB" "nutdrv_qx" +"Legrand" "ups" "4" "Megaline 1250" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 2500" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 3750" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 5000" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 5000 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 6250 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 7500 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 8750 /2" "Serial" "metasys" +"Legrand" "ups" "4" "Megaline 10000 /2" "Serial" "metasys" +"Legrand" "ups" "2" "Niky" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Niky" "USB" "nutdrv_qx" +"Legrand" "ups" "2" "Niky S" "Serial" "nutdrv_qx" +"Legrand" "ups" "2" "Niky S" "USB" "nutdrv_qx" +"Legrand" "ups" "4" "WHAD 800" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 1000" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 1250" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 1500" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 2000" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD 2500" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD CAB 1250" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD CAB 2500" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD HE 800" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD HE 1000" "Serial" "metasys" +"Legrand" "ups" "4" "WHAD HE 1500" "Serial" "metasys" "Lestar" "ups" "2" "MD-800E" "" "blazer_ser" @@ -542,48 +578,50 @@ "Mecer" "ups" "2" "ME-1000-WTU" "USB" "nutdrv_qx" # http://www.comx-computers.co.za/download/mecer/ME-1000-WTU.pdf "Mecer" "ups" "2" "ME-2000" "" "blazer_ser" -"Meta System" "ups" "1" "HF Line" "1..4 boards" "metasys" -"Meta System" "ups" "1" "HF Line \/2" "5..8 boards" "metasys" -"Meta System" "ups" "1" "HF Millennium 810" "" "metasys" -"Meta System" "ups" "1" "HF Millennium 820" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 910" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 920" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 930" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 940" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 950" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 960" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 970" "" "metasys" -"Meta System" "ups" "1" "HF TOP Line 980" "" "metasys" -"Meta System" "ups" "1" "ECO Network 750" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1000" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1050" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1500" "" "metasys" -"Meta System" "ups" "1" "ECO Network 1800" "" "metasys" -"Meta System" "ups" "1" "ECO Network 2000" "" "metasys" -"Meta System" "ups" "1" "ECO Network 2100" "" "metasys" -"Meta System" "ups" "1" "ECO Network 2500" "" "metasys" -"Meta System" "ups" "1" "ECO Network 3000" "" "metasys" -"Meta System" "ups" "1" "ECO 305" "" "metasys" -"Meta System" "ups" "1" "ECO 308" "" "metasys" -"Meta System" "ups" "1" "ECO 311" "" "metasys" -"Meta System" "ups" "1" "ECO 511" "" "metasys" -"Meta System" "ups" "1" "ECO 516" "" "metasys" -"Meta System" "ups" "1" "ECO 519" "" "metasys" -"Meta System" "ups" "1" "ECO 522" "" "metasys" -"Meta System" "ups" "1" "ally HF 800" "" "metasys" -"Meta System" "ups" "1" "ally HF 1000" "" "metasys" -"Meta System" "ups" "1" "ally HF 1250" "" "metasys" -"Meta System" "ups" "1" "ally HF 1600" "" "metasys" -"Meta System" "ups" "1" "ally HF 2000" "" "metasys" -"Meta System" "ups" "1" "ally HF 2500" "" "metasys" -"Meta System" "ups" "1" "Megaline 1250" "" "metasys" -"Meta System" "ups" "1" "Megaline 2500" "" "metasys" -"Meta System" "ups" "1" "Megaline 3750" "" "metasys" -"Meta System" "ups" "1" "Megaline 5000" "" "metasys" -"Meta System" "ups" "1" "Megaline 6250" "" "metasys" -"Meta System" "ups" "1" "Megaline 7500" "" "metasys" -"Meta System" "ups" "1" "Megaline 8750" "" "metasys" -"Meta System" "ups" "1" "Megaline 10000" "" "metasys" +"Meta System" "ups" "4" "HF Line" "1..4 boards" "metasys" +"Meta System" "ups" "4" "HF Line /2" "5..8 boards" "metasys" +"Meta System" "ups" "4" "HF Millennium 810" "" "metasys" +"Meta System" "ups" "4" "HF Millennium 820" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 910" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 920" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 930" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 940" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 950" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 960" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 970" "" "metasys" +"Meta System" "ups" "4" "HF TOP Line 980" "" "metasys" +"Meta System" "ups" "4" "ECO Network 750" "" "metasys" +"Meta System" "ups" "4" "ECO Network 1000" "" "metasys" +"Meta System" "ups" "4" "ECO Network 1050" "" "metasys" +"Meta System" "ups" "4" "ECO Network 1500" "" "metasys" +"Meta System" "ups" "4" "ECO Network 1800" "" "metasys" +"Meta System" "ups" "4" "ECO Network 2000" "" "metasys" +"Meta System" "ups" "4" "ECO Network 2100" "" "metasys" +"Meta System" "ups" "4" "ECO Network 2500" "" "metasys" +"Meta System" "ups" "4" "ECO Network 3000" "" "metasys" +"Meta System" "ups" "4" "ECO 305" "" "metasys" +"Meta System" "ups" "4" "ECO 308" "" "metasys" +"Meta System" "ups" "4" "ECO 311" "" "metasys" +"Meta System" "ups" "4" "ECO 511" "" "metasys" +"Meta System" "ups" "4" "ECO 516" "" "metasys" +"Meta System" "ups" "4" "ECO 519" "" "metasys" +"Meta System" "ups" "4" "ECO 522" "" "metasys" +"Meta System" "ups" "4" "ally HF 800" "" "metasys" +"Meta System" "ups" "4" "ally HF 1000" "" "metasys" +"Meta System" "ups" "4" "ally HF 1250" "" "metasys" +"Meta System" "ups" "4" "ally HF 1600" "" "metasys" +"Meta System" "ups" "4" "ally HF 2000" "" "metasys" +"Meta System" "ups" "4" "ally HF 2500" "" "metasys" +"Meta System" "ups" "4" "Megaline 1250" "" "metasys" +"Meta System" "ups" "4" "Megaline 2500" "" "metasys" +"Meta System" "ups" "4" "Megaline 3750" "" "metasys" +"Meta System" "ups" "4" "Megaline 5000" "" "metasys" +"Meta System" "ups" "4" "Megaline 6250" "" "metasys" +"Meta System" "ups" "4" "Megaline 7500" "" "metasys" +"Meta System" "ups" "4" "Megaline 8750" "" "metasys" +"Meta System" "ups" "4" "Megaline 10000" "" "metasys" +"Meta System" "ups" "4" "DHEA 1000" "Serial" "metasys" +"Meta System" "ups" "4" "DHEA 1500" "Serial" "metasys" "MGE Office Protection Systems" "ups" "5" "Protection Center 500/675 VA" "USB" "usbhid-ups" "MGE Office Protection Systems" "ups" "5" "Protection Station 500/650/800 VA" "USB" "usbhid-ups" diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 19f376eab5..3a6dfd9042 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -178,7 +178,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 powervar-hid.c delta_ups-hid.c + openups-hid.c powervar-hid.c delta_ups-hid.c legrand-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 @@ -305,7 +305,7 @@ dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-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 \ + eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h # Define a dummy library so that Automake builds rules for the diff --git a/drivers/hidparser.c b/drivers/hidparser.c index 74a01fa939..c5879e9a69 100644 --- a/drivers/hidparser.c +++ b/drivers/hidparser.c @@ -452,7 +452,7 @@ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue) int Weight, Bit; unsigned long mask, signbit, magMax, magMin; - long value = 0; + long value = 0, range; Bit = pData->Offset + 8; /* First byte of report is report ID */ @@ -464,6 +464,13 @@ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue) } } + range = pData->LogMax - pData->LogMin + 1; + if (range <= 0) { + /* makes no sense, give up */ + *pValue = value; + return; + } + /* translate Value into a signed/unsigned value in the range LogMin..LogMax, as appropriate. See HID spec, p.38: "If both the Logical Minimum and Logical Maximum extents are defined as diff --git a/drivers/legrand-hid.c b/drivers/legrand-hid.c new file mode 100644 index 0000000000..b251e63e78 --- /dev/null +++ b/drivers/legrand-hid.c @@ -0,0 +1,235 @@ +/* legrand-hid.c - subdriver to monitor Legrand USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2018 Gabriele Taormina , for Legrand + * + * 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 "common.h" +#include "usbhid-ups.h" +#include "legrand-hid.h" +#include "main.h" +#include "usb-common.h" + +#define LEGRAND_HID_VERSION "Legrand HID 0.1" + +/* Legrand VendorID and ProductID */ +#define LEGRAND_VID 0x1cb0 /* Legrand */ +#define LEGRAND_PID_PDU 0x0038 /* Keor PDU model (800VA) */ +#define LEGRAND_PID_SP 0x0032 /* Keor SP model (600, 800, 1000, 1500, 2000VA version) */ + +static void *disable_interrupt_pipe(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + if (!use_interrupt_pipe) + return NULL; + use_interrupt_pipe = FALSE; + upslogx(LOG_INFO, "interrupt pipe disabled (add 'pollonly' flag to 'ups.conf' to get rid of this message)"); + return NULL; +} + +/* USB IDs device table */ +static usb_device_id_t legrand_usb_device_table[] = { + { USB_DEVICE(LEGRAND_VID, LEGRAND_PID_PDU), disable_interrupt_pipe }, /* Legrand Keor PDU */ + { USB_DEVICE(LEGRAND_VID, LEGRAND_PID_SP), disable_interrupt_pipe }, /* Legrand Keor SP */ + + /* Terminating entry */ + { -1, -1, NULL } +}; + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* LEGRAND usage table */ +static usage_lkp_t legrand_usage_lkp[] = { + { NULL, 0 } +}; + +static usage_tables_t legrand_utab[] = { + legrand_usage_lkp, + hid_usage_lkp, + NULL, +}; + +static const char *legrand_times10(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 10); + return buf; +} + +static info_lkp_t legrand_times10_info[] = { + { 0, NULL, legrand_times10, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *legrand_times100k(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 100000); + return buf; +} + +static info_lkp_t legrand_times100k_info[] = { + { 0, NULL, legrand_times100k, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *legrand_times1M(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 1000000); + return buf; +} + +static info_lkp_t legrand_times1M_info[] = { + { 0, NULL, legrand_times1M, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *legrand_times10M(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 10000000); + return buf; +} + +static info_lkp_t legrand_times10M_info[] = { + { 0, NULL, legrand_times10M, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t legrand_hid2nut[] = { + /* Input Data */ + { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.0f", 0, NULL }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%.0f", 0, legrand_times1M_info }, + { "input.transfer.high", 0, 0, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, legrand_times10_info }, + { "input.transfer.low", 0, 0, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.voltage.nominal", 0, 0, "UPS.Flow.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* Battery Data */ + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, divide_by_10_conversion }, + { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.Battery.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.0f", 0, divide_by_10_conversion }, + { "battery.voltage", 0, 0, "UPS.BatterySystem.Battery.Voltage", NULL, "%.0f", 0, legrand_times100k_info }, + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RuntimeToEmpty", NULL, "%.0f", 0, NULL }, + { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* Output Data */ + { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.0f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.0f", 0, legrand_times10M_info }, + { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.0f", 0, NULL }, + { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.load", 0, 0, "UPS.OutletSystem.Outlet.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.realpower.nominal", 0, 0, "UPS.Output.ConfigActivePower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "ups.realpower.nominal", 0, 0, "UPS.Flow.ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* UPS Status */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + { "BOOL", 0, 0, "UPS.Output.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info }, + + /* Delays */ + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL }, + { "load.off.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "load.off.delay", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + + /* Battery Testing */ + { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, + { "test.battery.start.quick", 0, 0, "UPS.Output.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.Output.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.Output.Test", NULL, "3", HU_TYPE_CMD, NULL }, + + /* Buzzer */ + { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *legrand_format_model(HIDDevice_t *hd) +{ + return hd->Product; +} + +static const char *legrand_format_mfr(HIDDevice_t *hd) +{ + return hd->Vendor ? hd->Vendor : "Legrand"; +} + +static const char *legrand_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 legrand_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(legrand_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("Legrand", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t legrand_subdriver = { + LEGRAND_HID_VERSION, + legrand_claim, + legrand_utab, + legrand_hid2nut, + legrand_format_model, + legrand_format_mfr, + legrand_format_serial, +}; diff --git a/drivers/legrand-hid.h b/drivers/legrand-hid.h new file mode 100644 index 0000000000..59770cd1bd --- /dev/null +++ b/drivers/legrand-hid.h @@ -0,0 +1,30 @@ +/* legrand-hid.h - subdriver to monitor Legrand 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 LEGRAND_HID_H +#define LEGRAND_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t legrand_subdriver; + +#endif /* LEGRAND_HID_H */ diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index 8f3fe9335c..ba791f694f 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -550,6 +550,16 @@ static int sgs_command(const char *cmd, char *buf, size_t buflen) } + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (i && memchr(buf, '\r', i) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if (i < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[i++] = '\r'; + buf[i] = 0; + } + } + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); return i; } @@ -687,6 +697,16 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) memset(buf, 0, buflen); memcpy(buf, tmp, len); + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (len && memchr(buf, '\r', len) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if (len < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[len++] = '\r'; + buf[len] = 0; + } + } + return (int)len; } @@ -818,6 +838,16 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) ret = di; } + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (ret && memchr(buf, '\r', ret) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if ((size_t)ret < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[ret++] = '\r'; + buf[ret] = 0; + } + } + /* "UPS No Ack" has a special meaning */ if ( strcspn(buf, "\r") == 10 && @@ -924,6 +954,16 @@ static int fabula_command(const char *cmd, char *buf, size_t buflen) return ret; } + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (memchr(buf, '\r', ret) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if ((size_t)ret < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[ret++] = '\r'; + buf[ret] = 0; + } + } + upsdebug_hex(5, "read", buf, ret); upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); @@ -1458,6 +1498,7 @@ typedef struct { static qx_usb_device_id_t qx_usb_id[] = { { USB_DEVICE(0x05b8, 0x0000), NULL, NULL, &cypress_subdriver }, /* Agiler UPS */ { USB_DEVICE(0xffff, 0x0000), NULL, NULL, &krauler_subdriver }, /* Ablerex 625L USB */ + { USB_DEVICE(0x1cb0, 0x0035), NULL, NULL, &krauler_subdriver }, /* Legrand Daker DK / DK Plus */ { USB_DEVICE(0x0665, 0x5161), NULL, NULL, &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */ { USB_DEVICE(0x06da, 0x0002), "Phoenixtec Power","USB Cable (V2.00)", &phoenixtec_subdriver },/* Masterguard A Series */ { USB_DEVICE(0x06da, 0x0002), NULL, NULL, &cypress_subdriver }, /* Online Yunto YQ450 */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index 68e5bc3e7e..532f7b15ee 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -47,6 +47,7 @@ #include "cps-hid.h" #include "delta_ups-hid.h" #include "idowell-hid.h" + #include "legrand-hid.h" #include "liebert-hid.h" #include "openups-hid.h" #include "powercom-hid.h" @@ -66,6 +67,7 @@ static subdriver_t *subdriver_list[] = { &cps_subdriver, &delta_ups_subdriver, &idowell_subdriver, + &legrand_subdriver, &liebert_subdriver, &openups_subdriver, &powercom_subdriver, diff --git a/scripts/upower/95-upower-hid.rules b/scripts/upower/95-upower-hid.rules index 059e1b465c..36b56e1c99 100644 --- a/scripts/upower/95-upower-hid.rules +++ b/scripts/upower/95-upower-hid.rules @@ -35,6 +35,7 @@ ATTRS{idVendor}=="0764", ENV{UPOWER_VENDOR}="Cyber Power Systems" ATTRS{idVendor}=="09ae", ENV{UPOWER_VENDOR}="TrippLite" ATTRS{idVendor}=="0d9f", ENV{UPOWER_VENDOR}="PowerCOM" ATTRS{idVendor}=="10af", ENV{UPOWER_VENDOR}="Liebert" +ATTRS{idVendor}=="1cb0", ENV{UPOWER_VENDOR}="Legrand" ATTRS{idVendor}=="2b2d", ENV{UPOWER_VENDOR}="AEG" ATTRS{idVendor}=="4234", ENV{UPOWER_VENDOR}="Powervar" @@ -150,6 +151,10 @@ ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0008", ENV{UPOWER_BATTERY_TYPE}="ups" +# Legrand +ATTRS{idVendor}=="1cb0", ATTRS{idProduct}=="0032", ENV{UPOWER_BATTERY_TYPE}="ups" +ATTRS{idVendor}=="1cb0", ATTRS{idProduct}=="0038", ENV{UPOWER_BATTERY_TYPE}="ups" + # AEG ATTRS{idVendor}=="2b2d", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups"