1
/* mge-shut.h - monitor MGE UPS for NUT with SHUT protocol
3
* Copyright (C) 2002-2004
4
* Arnaud Quette <arnaud.quette@free.fr> & <arnaud.quette@mgeups.com>
5
* Philippe Marzouk <philm@users.sourceforge.net>
6
* Russell Kroll <rkroll@exploits.org>
8
* Sponsored by MGE UPS SYSTEMS <http://opensource.mgeups.com/>
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 "hidparser.h"
29
#define DRIVER_VERSION "0.59"
31
#define DEFAULT_TIMEOUT 3000
34
#define DEFAULT_LOWBATT 30 /* low battery level, in % */
35
#define DEFAULT_ONDELAY 3 /* delay between return of utility power
36
and powering up of load, 10 seconds units */
37
#define DEFAULT_OFFDELAY 20 /* delay befor power off, in SECONDS*/
38
#define OFF_NOTIFICATION 1 /* notification off */
40
#define LIGHT_NOTIFICATION 2 /* light notification */
41
#define COMPLETE_NOTIFICATION 3 /* complete notification for UPSs which do
42
* not support disabling it like some early
44
#define DEFAULT_NOTIFICATION COMPLETE_NOTIFICATION
48
#define HID_REPORT_TYPE_INPUT 0x01
49
#define HID_REPORT_TYPE_OUTPUT 0x02
50
#define HID_REPORT_TYPE_FEATURE 0x03
52
#define REQUEST_TYPE_USB 0x80
53
#define REQUEST_TYPE_HID 0x81
54
#define REQUEST_TYPE_GET_REPORT 0xa1
55
#define REQUEST_TYPE_SET_REPORT 0x21
57
#define DEVICE_DESCRIPTOR 0x0001
58
#define CONFIG_DESCRIPTOR 0x0002
59
#define STRING_DESCRIPTOR 0x0003
60
#define INTERFACE_DESCRIPTOR 0x0004
61
#define ENDPOINT_DESCRIPTOR 0x0005
62
#define HID_DESCRIPTOR 0x0021
63
#define REPORT_DESCRIPTOR 0x0022
65
#define MAX_REPORT_SIZE 0x1800
67
/* SHUT definitions - From Simplified SHUT spec */
69
#define SHUT_TYPE_REQUEST 0x01
70
#define SHUT_TYPE_RESPONSE 0x04
71
#define SHUT_TYPE_NOTIFY 0x05
74
#define SHUT_SYNC 0x16 /* complete notifications - not yet managed
75
but needed for some early Ellipse models */
76
#define SHUT_SYNC_LIGHT 0x17 /* partial notifications */
77
#define SHUT_SYNC_OFF 0x18 /* disable notifications - only do polling */
78
#define SHUT_PKT_LAST 0x80
80
/* From SHUT specifications */
82
typedef struct hid_packet {
83
unsigned char bmRequestType;
84
unsigned char bRequest;
85
unsigned short wValue;
86
unsigned short wIndex;
87
unsigned short wLength;
88
/* unsigned char padding[8]; for use with shut_set_report */
91
typedef union hid_data {
93
unsigned char raw_pkt[8]; /* max report lengh, was 8 */
96
typedef struct shut_packet {
98
unsigned char bLength;
100
unsigned char bChecksum;
103
typedef union shut_data {
104
shut_packet_t shut_pkt;
105
unsigned char raw_pkt[11];
108
/* From USB/HID specifications */
110
typedef struct hid_descriptor {
111
unsigned char bLength;
112
unsigned char bDescriptorType;
113
unsigned short bcdHID;
114
unsigned char bCountryCode;
115
unsigned char bNumDescriptors;
116
unsigned char bReportDescriptorType;
117
unsigned short wDescriptorLength;
120
typedef union hid_desc_data {
121
hid_descriptor_t hid_desc;
122
unsigned char raw_desc[9]; /* max report lengh, aws 9 */
125
typedef struct device_descriptor {
126
unsigned char bLength;
127
unsigned char bDescriptorType;
128
unsigned short bcdUSB;
129
unsigned char bDeviceClass;
130
unsigned char bDeviceSubClass;
131
unsigned char bDeviceProtocol;
132
unsigned char bMaxPacketSize0;
133
unsigned short idVendor;
134
unsigned short idProduct;
135
unsigned short bcdDevice;
136
unsigned char iManufacturer;
137
unsigned char iProduct;
138
unsigned char iSerialNumber;
139
unsigned char bNumConfigurations;
140
} device_descriptor_t;
142
typedef union device_desc_data {
143
device_descriptor_t dev_desc;
144
unsigned char raw_desc[18];
145
} device_desc_data_u;
147
/* --------------------------------------------------------------- */
148
/* Explicit Booleans */
149
/* --------------------------------------------------------------- */
151
#define SHUT_FLAG_OK (1 << 0) /* show element to upsd. */
152
#define SHUT_FLAG_STATIC (1 << 1) /* retrieve info only once. */
153
#define SHUT_FLAG_ABSENT (1 << 2) /* data is absent in the device,
154
use default value. */
155
#define SHUT_FLAG_STALE (1 << 3) /* data stale, don't try too often. */
156
#define SHUT_FLAG_DELAY (1 << 4) /* delay type value: formated differently. */
158
/* --------------------------------------------------------------- */
159
/* Query Commands and their Mapping to INFO_ Variables */
160
/* --------------------------------------------------------------- */
162
/* Structure defining how to query UPS for a variable and write
163
information to INFO structure.
166
char *type; /* INFO_* element */
167
int flags; /* INFO-element flags to set in addinfo */
168
int length; /* INFO-element length of strings */
169
char item_path[MAX_STRING]; /* HID object (fully qualified string path) */
170
char fmt[6]; /* printf format string for INFO entry */
171
char *dfl; /* default value */
172
unsigned long shut_flags; /* specific SHUT flags */
175
/* Array containing information to translate between UTalk and NUT info
177
* - Array is terminated by element with type NULL.
178
* - Essential INFO items (ups.{mfr, model, firmware, status}) are
179
* handled separately.
180
* - Array is NOT const, since "shut_flags" can be changed.
183
static mge_info_item mge_info[] = {
184
{ "driver.version.internal", FLAG_STRING, 5, "", "%s", DRIVER_VERSION, SHUT_FLAG_ABSENT | SHUT_FLAG_OK },
186
{ "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", "%i", NULL, SHUT_FLAG_OK },
187
{ "battery.charge.low", FLAG_RW |ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimitSetting", "%ld", NULL, SHUT_FLAG_OK }, /* RW, to be caught first if exists... */
188
{ "battery.charge.low",ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimit", "%ld", NULL, SHUT_FLAG_OK }, /* ... or Read only */
189
{ "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", "%05d", NULL, SHUT_FLAG_OK },
191
{ "ups.mfr",ST_FLAG_STRING, 20, "", "%s", "MGE UPS SYSTEMS", SHUT_FLAG_ABSENT | SHUT_FLAG_OK },
192
{ "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", "%i", NULL, SHUT_FLAG_OK },
193
{ "ups.delay.shutdown",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeShutdown", "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY},
194
{ "ups.delay.reboot",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeReboot", "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY},
195
{ "ups.delay.start",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeStartup", "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY},
196
{ "ups.test.interval",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.TestPeriod", "%i", NULL, SHUT_FLAG_OK },
197
{ "ups.test.result",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.Test", "%i", NULL, SHUT_FLAG_OK },
200
{ "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", "%i", NULL, SHUT_FLAG_OK },
201
{ "output.voltage.target.line", 0, 0, "UPS.PowerSummary.ConfigVoltage", "%i", NULL, SHUT_FLAG_OK },
202
{ "output.voltage.target.battery", 0, 0, "UPS.PowerSummary.ConfigVoltage", "%i", NULL, SHUT_FLAG_OK },
203
{ "output.current", 0, 0, "UPS.PowerSummary.Output.Current", "%i", NULL, SHUT_FLAG_OK },
204
{ "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", "%i", NULL, SHUT_FLAG_OK },
206
/* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */
207
/* TODO: add an iterative semantic [%x] to factorise outlets */
208
{ "outlet.0.id", 0, 0, "UPS.OutletSystem.Outlet.[1].OutletID", "%i", NULL, SHUT_FLAG_OK },
209
{ "outlet.0.desc",ST_FLAG_RW |ST_FLAG_STRING, 20, "", "s", "Main Outlet", SHUT_FLAG_ABSENT | SHUT_FLAG_OK },
210
{ "outlet.0.switchable", 0, 0, "UPS.OutletSystem.Outlet.[1].PresentStatus.Switchable", "%i", NULL, SHUT_FLAG_OK },
211
{ "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.[2].OutletID", "%i", NULL, SHUT_FLAG_OK },
212
{ "outlet.1.desc",ST_FLAG_RW |ST_FLAG_STRING, 20, "", "s", "PowerShare Outlet 1", SHUT_FLAG_ABSENT | SHUT_FLAG_OK },
213
{ "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.Switchable", "%i", NULL, SHUT_FLAG_OK },
214
{ "outlet.1.switch",ST_FLAG_RW |ST_FLAG_STRING, 2, "UPS.OutletSystem.Outlet.[2].PresentStatus.SwitchOn/Off", "%i", NULL, SHUT_FLAG_OK },
215
{ "outlet.1.autoswitch.charge.low",ST_FLAG_RW |ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[2].RemainingCapacityLimit", "%i", NULL, SHUT_FLAG_OK },
216
{ "outlet.1.delay.shutdown",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].DelayBeforeShutdown", "%i", NULL, SHUT_FLAG_OK },
217
{ "outlet.1.delay.start",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].DelayBeforeStartup", "%i", NULL, SHUT_FLAG_OK },
218
{ "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet.[3].OutletID", "%i", NULL, SHUT_FLAG_OK },
219
{ "outlet.2.desc",ST_FLAG_RW |ST_FLAG_STRING, 20, "", "s", "PowerShare Outlet 2", SHUT_FLAG_ABSENT | SHUT_FLAG_OK },
220
{ "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", "%i", NULL, SHUT_FLAG_OK },
221
{ "outlet.2.switch",ST_FLAG_RW |ST_FLAG_STRING, 2, "UPS.OutletSystem.Outlet.[3].PresentStatus.SwitchOn/Off", "%i", NULL, SHUT_FLAG_OK },
222
{ "outlet.2.autoswitch.charge.low",ST_FLAG_RW |ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].RemainingCapacityLimit", "%i", NULL, SHUT_FLAG_OK },
223
{ "outlet.2.delay.shutdown",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].DelayBeforeShutdown", "%i", NULL, SHUT_FLAG_OK },
224
{ "outlet.2.delay.start",ST_FLAG_RW |ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].DelayBeforeStartup", "%i", NULL, SHUT_FLAG_OK },
227
{ "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", "%i", NULL, SHUT_FLAG_OK },
228
{ "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", "%i", NULL, SHUT_FLAG_OK },
230
/* terminating element */
231
{ NULL, 0, 0, "\0", "\0", NULL, 0 }
234
/* temporary usage code lookup */
240
static usage_lkp_t usage_lkp[] = {
241
/* Power Device Page */
242
{ "PresentStatus", 0x00840002 },
243
{ "UPS", 0x00840004 },
244
{ "BatterySystem", 0x00840010 },
245
{ "Battery", 0x00840012 },
246
{ "BatteryID", 0x00840013 },
247
{ "PowerConverter", 0x00840016 },
248
{ "OutletSystem", 0x00840018 },
249
{ "Input", 0x0084001a },
250
{ "Output", 0x0084001c },
251
{ "Outlet", 0x00840020 },
252
{ "OutletID", 0x00840021 },
253
{ "PowerSummary", 0x00840024 },
254
{ "Voltage", 0x00840030 },
255
{ "Current", 0x00840031 },
256
{ "Frequency", 0x00840032 },
257
{ "PercentLoad", 0x00840035 },
258
{ "ConfigVoltage", 0x00840040 },
259
{ "ConfigCurrent", 0x00840041 },
260
{ "ConfigFrequency", 0x00840042 },
261
{ "ConfigApparentPower", 0x00840043 },
262
{ "LowVoltageTransfer", 0x00840053 },
263
{ "HighVoltageTransfer", 0x00840054 },
264
{ "DelayBeforeReboot", 0x00840055 },
265
{ "DelayBeforeStartup", 0x00840056 },
266
{ "DelayBeforeShutdown", 0x00840057 },
267
{ "Test", 0x00840058 },
268
{ "OverLoad", 0x00840065 }, /* mispelled in usb.ids */
269
{ "SwitchOn/Off", 0x0084006b },
270
{ "Switchable", 0x0084006c },
271
{ "Flow", 0x0084001e },
272
/* Battery System Page */
273
{ "RemainingCapacityLimit", 0x00850029 },
274
{ "BelowRemainingCapacityLimit", 0x00850042 },
275
{ "RemainingCapacity", 0x00850066 },
276
{ "RunTimeToEmpty", 0x00850068 },
277
{ "ACPresent", 0x008500d0 },
278
{ "Charging", 0x00850044 },
279
{ "Discharging", 0x00850045 },
280
{ "NeedReplacement", 0x0085004b },
281
/* MGE UPS SYSTEMS Page */
282
{ "iModel", 0xffff00f0 },
283
{ "RemainingCapacityLimitSetting", 0xffff004d },
284
{ "TestPeriod", 0xffff0001 },
289
/* SHUT / HID functions Prototypes */
291
int shut_ups_start(void);
292
uchar shut_checksum(const unsigned char *buf, int bufsize);
293
int shut_token_send(unsigned char token);
294
int shut_packet_send (hid_data_u *hdata, int datalen, unsigned char token);
295
int shut_packet_recv (unsigned char *Buf, int datalen);
297
int shut_get_descriptor(int desctype, unsigned char *pkt, int reportlen);
298
int shut_get_string(int index, char *string, int strlen);
299
int shut_get_report(int id, unsigned char *pkt, int reportlen);
300
int shut_set_report(int id, unsigned char *pkt, int reportlen);
301
int shut_identify_ups (void);
302
int shut_wait_ack (void);
303
void shut_ups_status(void);
305
int hid_init_device(void);
306
int hid_lookup_usage(char *name);
307
int hid_get_value(char *item_path);
308
int hid_set_value(const char *varname, const char *val);
309
ushort lookup_path(char *HIDpath, HIDData *data);
311
int instcmd(const char *cmdname, const char *extra);
312
void setline(int set);
313
int serial_read (int read_timeout, unsigned char *read);
314
int serial_send(unsigned char *buf, int len);
316
void dump_hex(const char *msg, const unsigned char *buf, int len);
317
void make_string(unsigned char *buf, int datalen, char *string);
320
mge_info_item *shut_find_info(const char *varname);