3
* @brief HID Library - User API (Generic HID Access using MGE HIDParser)
5
* @author Copyright (C) 2003
6
* Arnaud Quette <arnaud.quette@free.fr> && <arnaud.quette@mgeups.com>
7
* Philippe Marzouk <philm@users.sourceforge.net> (dump_hex())
9
* This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com
11
* The logic of this file is ripped from mge-shut driver (also from
12
* Arnaud Quette), which is a "HID over serial link" UPS driver for
13
* Network UPS Tools <http://www.networkupstools.org/>
15
* This program is free software; you can redistribute it and/or modify
16
* it under the terms of the GNU General Public License as published by
17
* the Free Software Foundation; either version 2 of the License, or
18
* (at your option) any later version.
20
* This program is distributed in the hope that it will be useful,
21
* but WITHOUT ANY WARRANTY; without even the implied warranty of
22
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
* GNU General Public License for more details.
25
* You should have received a copy of the GNU General Public License
26
* along with this program; if not, write to the Free Software
27
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29
* -------------------------------------------------------------------------- */
33
/* #include <math.h> */
34
#include "hidparser.h"
43
static HIDParser hParser;
45
unsigned char raw_buf[100];
46
int replen; /* size of the last report retrieved */
47
unsigned char ReportDesc[4096];
49
#define MAX_REPORT_SIZE 0x1800
51
/* TODO: rework all that */
52
extern void upsdebugx(int level, const char *fmt, ...);
53
#define TRACE upsdebugx
55
/* Units and exponents table (HID PDC, ļæ½3.2.3) */
56
#define NB_HID_UNITS 10
57
const long HIDUnits[NB_HID_UNITS][2]=
59
{0x00000000,0}, /* None */
60
{0x00F0D121,7}, /* Voltage */
61
{0x00100001,0}, /* Ampere */
62
{0x0000D121,7}, /* VA */
63
{0x0000D121,7}, /* Watts */
64
{0x00001001,0}, /* second */
65
{0x00010001,0}, /* ļæ½K */
66
{0x00000000,0}, /* percent */
67
{0x0000F001,0}, /* Hertz */
68
{0x00101001,0}, /* As */
71
/* support functions */
72
void logical_to_physical(HIDData *Data);
73
void physical_to_logical(HIDData *Data);
74
int hid_lookup_usage(char *name);
75
ushort lookup_path(const char *HIDpath, HIDData *data);
76
void dump_hex (const char *msg, const unsigned char *buf, int len);
77
long get_unit_expo(long UnitType);
78
float expo(int a, int b);
81
HIDDevice *HIDOpenDevice(const char *port, MatchFlags *flg)
86
curDevice.Name = NULL;
87
curDevice.Vendor = NULL;
88
curDevice.VendorID = -1;
89
curDevice.Product = NULL;
90
curDevice.ProductID = -1;
91
curDevice.Serial = NULL;
92
curDevice.Application = -1;
95
/* get and parse descriptors (dev, cfg and report) */
96
if ((ReportSize = libusb_open(&curDevice, flg, ReportDesc)) == -1) {
100
TRACE(2, "Report Descriptor size = %d", ReportSize);
101
dump_hex ("Report Descriptor", ReportDesc, 200);
103
/* HID Parser Init */
104
ResetParser(&hParser);
105
hParser.ReportDescSize = ReportSize;
106
memcpy(hParser.ReportDesc, ReportDesc, ReportSize);
107
HIDParse(&hParser, &hData);
109
/* CHeck for matching UsageCode (1rst collection == application == type of device) */
110
if ((hData.Path.Node[0].UPage == ((flg->UsageCode & 0xFFFF0000) / 0x10000))
111
&& (hData.Path.Node[0].Usage == (flg->UsageCode & 0x0000FFFF)))
112
TRACE(2, "Found a device matching UsageCode (0x%08x)", flg->UsageCode);
114
TRACE(2, "Found a device, but not matching UsageCode (0x%08x)", flg->UsageCode);
121
HIDItem *HIDGetItem(const char *ItemPath)
126
float HIDGetItemValue(const char *path)
131
/* Prepare path of HID object */
132
hData.Type = ITEM_FEATURE;
135
if((retcode = lookup_path(path, &hData)) > 0) {
136
TRACE(2, "Path depth = %i", retcode);
138
for (i = 0; i<retcode; i++)
139
TRACE(2, "%i: UPage(%x), Usage(%x)", i,
140
hData.Path.Node[i].UPage,
141
hData.Path.Node[i].Usage);
143
hData.Path.Size = retcode;
145
/* Get info on object (reportID, offset and size) */
146
if (FindObject(&hParser,&hData) == 1) {
147
/* Get report with data */
148
/* if ((replen=libusb_get_report(hData.ReportID,
149
raw_buf, MAX_REPORT_SIZE)) > 0) { => doesn't work! */
150
if ((replen=libusb_get_report(hData.ReportID, raw_buf, 10)) > 0) {
151
/* Extract the data value */
152
GetValue((const unsigned char *) raw_buf, &hData);
154
TRACE(2, "=>> Before exponent: %ld, %i/%i)", hData.Value,
155
(int)hData.UnitExp, (int)get_unit_expo(hData.Unit) );
157
/* Convert Logical Min, Max and Value in Physical */
158
/* logical_to_physical(&hData); */
162
/* Process exponents */
163
/* Value*=(float) pow(10,(int)hData.UnitExp - get_unit_expo(hData.Unit)); */
164
Value*=(float) expo(10,(int)hData.UnitExp - get_unit_expo(hData.Unit));
165
hData.Value = (long) Value;
167
/* Convert Logical Min, Max and Value into Physical */
168
logical_to_physical(&hData);
170
dump_hex ("Report ", raw_buf, replen);
175
TRACE(2, "Can't retrieve Report %i", hData.ReportID);
179
TRACE(2, "Can't find object %s", path);
181
return -2; /* TODO: should be checked */
184
char *HIDGetItemString(const char *path)
188
/* Prepare path of HID object */
189
hData.Type = ITEM_FEATURE;
192
if((retcode = lookup_path(path, &hData)) > 0) {
193
TRACE(2, "Path depth = %i", retcode);
195
for (i = 0; i<retcode; i++)
196
TRACE(2, "%i: UPage(%x), Usage(%x)", i,
197
hData.Path.Node[i].UPage,
198
hData.Path.Node[i].Usage);
200
hData.Path.Size = retcode;
202
/* Get info on object (reportID, offset and size) */
203
if (FindObject(&hParser,&hData) == 1) {
204
if (libusb_get_report(hData.ReportID, raw_buf, 8) > 0) { /* MAX_REPORT_SIZE) > 0) { */
205
GetValue((const unsigned char *) raw_buf, &hData);
208
libusb_get_string(hData.Value, raw_buf);
212
TRACE(2, "Can't retrieve Report %i", hData.ReportID);
215
TRACE(2, "Can't find object %s", path);
222
bool HIDSetItemValue(const char *path, float value)
226
/* Begin by a standard Get to fill in com structures ... */
227
Value = HIDGetItemValue(path);
229
/* ... And play with global vars */
230
if (Value != -2) { /* Get succeed */
232
TRACE(2, "=>> SET: Before set: %.2f (%ld)", Value, (long)value);
234
/* Test if Item is settable */
235
if (hData.Attribute != ATTR_DATA_CST) {
236
/* Set new value for this item */
237
/* And Process exponents restoration */
238
Value = value * expo(10, get_unit_expo(hData.Unit) - (int)hData.UnitExp);
240
hData.Value=(long) Value;
242
TRACE(2, "=>> SET: after exp: %ld/%.2f (exp = %.2f)", hData.Value, Value,
243
expo(10, (int)get_unit_expo(hData.Unit) - (int)hData.UnitExp));
245
/* Convert Physical Min, Max and Value in Logical */
246
physical_to_logical(&hData);
247
TRACE(2, "=>> SET: after PL: %ld", hData.Value);
249
SetValue(&hData, raw_buf);
251
dump_hex ("==> Report after setvalue", raw_buf, replen);
253
if (libusb_set_report(hData.ReportID, raw_buf, replen) > 0) {
254
TRACE(2, "Set report succeeded");
257
TRACE(2, "Set report failed");
260
/* check if set succeed! => doesn't work on *Delay (decremented!) */
261
/* Value = HIDGetItemValue(path);
263
TRACE(2, "=>> SET: new value = %.2f (was set to %.2f)\n",
264
Value, (float) value);
265
return TRUE;*/ /* (Value == value); */
270
HIDItem *HIDGetNextEvent(HIDDevice *dev)
272
/* unsigned char buf[20];
274
upsdebugx(1, "Waiting for notifications\n");*/
276
/* TODO: To be written */
277
/* needs libusb-0.1.8 to work => use ifdef and autoconf */
278
/* libusb_get_interrupt(&buf[0], 20, 5000); */
281
void HIDCloseDevice(HIDDevice *dev)
283
libusb_close(&curDevice);
287
/*******************************************************
289
*******************************************************/
291
#define MAX_STRING 64
293
void logical_to_physical(HIDData *Data)
295
if(Data->PhyMax - Data->PhyMin > 0)
297
float Factor = (float)(Data->PhyMax - Data->PhyMin) / (Data->LogMax - Data->LogMin);
299
Data->Value=(long)((Data->Value - Data->LogMin) * Factor) + Data->PhyMin;
301
if(Data->Value > Data->PhyMax)
302
Data->Value |= ~Data->PhyMax;
304
else /* => nothing to do!? */
306
/* Value.m_Value=(long)(pConvPrm->HValue); */
307
if(Data->Value > Data->LogMax)
308
Data->Value |= ~Data->LogMax;
311
/* if(Data->Value > Data->Value.m_Max)
312
Value.m_Value |= ~Value.m_Max;
316
void physical_to_logical(HIDData *Data)
318
TRACE(2, "PhyMax = %ld, PhyMin = %ld, LogMax = %ld, LogMin = %ld",
319
Data->PhyMax, Data->PhyMin, Data->LogMax, Data->LogMin);
321
if(Data->PhyMax - Data->PhyMin > 0)
323
float Factor=(float)(Data->LogMax - Data->LogMin) / (Data->PhyMax - Data->PhyMin);
325
Data->Value=(long)((Data->Value - Data->PhyMin) * Factor) + Data->LogMin;
327
/* else => nothing to do!?
329
m_ConverterTab[iTab].HValue=m_ConverterTab[iTab].DValue;
333
long get_unit_expo(long UnitType)
337
while (i < NB_HID_UNITS) {
338
if (HIDUnits[i][0] == UnitType) {
339
exp = HIDUnits[i][1];
347
/* exponent function: return a^b */
348
/* TODO: check if needed to replace libmath->pow */
349
float expo(int a, int b)
354
return (float) a * expo(a,b-1);
356
return (float)((float)(1/(float)a) * (float) expo(a,b+1));
362
/* translate HID string path to numeric path and return path depth */
363
/* TODO: use usbutils functions (need to be externalised!) */
364
ushort lookup_path(const char *HIDpath, HIDData *data)
366
ushort i = 0, cond = 1;
368
char buf[MAX_STRING];
371
strncpy(buf, HIDpath, strlen(HIDpath));
372
buf[strlen(HIDpath)] = '\0';
375
TRACE(2, "entering lookup_path(%s)", buf);
379
if ((end = strchr(start, '.')) == NULL) {
385
TRACE(2, "parsing %s", start);
388
if ((cur_usage = hid_lookup_usage(start)) == -1) {
389
TRACE(2, "%s wasn't found", start);
393
data->Path.Node[i].UPage = (cur_usage & 0xFFFF0000) / 0x10000;
394
data->Path.Node[i].Usage = cur_usage & 0x0000FFFF;
405
/* Lookup this usage name to find its code (page + index) */
406
/* temporary usage code lookup */
408
const char *usage_name;
412
static usage_lkp_t usage_lkp[] = {
413
/* Power Device Page */
414
{ "PresentStatus", 0x00840002 },
415
{ "UPS", 0x00840004 },
416
{ "BatterySystem", 0x00840010 },
417
{ "Battery", 0x00840012 },
418
{ "BatteryID", 0x00840013 },
419
{ "PowerConverter", 0x00840016 },
420
{ "OutletSystem", 0x00840018 },
421
{ "Input", 0x0084001a },
422
{ "Output", 0x0084001c },
423
{ "Outlet", 0x00840020 },
424
{ "OutletID", 0x00840021 },
425
{ "PowerSummary", 0x00840024 },
426
{ "Voltage", 0x00840030 },
427
{ "Current", 0x00840031 },
428
{ "Frequency", 0x00840032 },
429
{ "PercentLoad", 0x00840035 },
430
{ "Temperature", 0x00840036 },
431
{ "ConfigVoltage", 0x00840040 },
432
{ "ConfigCurrent", 0x00840041 },
433
{ "ConfigFrequency", 0x00840042 },
434
{ "ConfigApparentPower", 0x00840043 },
435
{ "LowVoltageTransfer", 0x00840053 },
436
{ "HighVoltageTransfer", 0x00840054 },
437
{ "DelayBeforeReboot", 0x00840055 },
438
{ "DelayBeforeStartup", 0x00840056 },
439
{ "DelayBeforeShutdown", 0x00840057 },
440
{ "Test", 0x00840058 },
441
{ "OverLoad", 0x00840065 }, /* mispelled in usb.ids */
442
{ "ShutdownImminent", 0x00840069 },
443
{ "SwitchOn/Off", 0x0084006b },
444
{ "Switchable", 0x0084006c },
445
{ "Boost", 0x0084006e },
446
{ "Buck", 0x0084006f },
447
{ "Flow", 0x0084001e },
448
/* Battery System Page */
449
{ "RemainingCapacityLimit", 0x00850029 },
450
{ "BelowRemainingCapacityLimit", 0x00850042 },
451
{ "RemainingCapacity", 0x00850066 },
452
{ "RunTimeToEmpty", 0x00850068 },
453
{ "ACPresent", 0x008500d0 },
454
{ "Charging", 0x00850044 },
455
{ "Discharging", 0x00850045 },
456
{ "NeedReplacement", 0x0085004b },
457
/* TODO: per MFR specific usages */
458
/* MGE UPS SYSTEMS Page */
459
{ "iModel", 0xffff00f0 },
460
{ "RemainingCapacityLimitSetting", 0xffff004d },
461
{ "TestPeriod", 0xffff0045 },
462
{ "LowVoltageBoostTransfer", 0xffff0050 },
463
{ "HighVoltageBoostTransfer", 0xffff0051 },
464
{ "LowVoltageBuckTransfer", 0xffff0052 },
465
{ "HighVoltageBuckTransfer", 0xffff0053 },
466
/* end of structure. */
470
int hid_lookup_usage(char *name)
474
TRACE(2, "Looking up %s", name);
476
if (name[0] == '[') /* manage indexed collection */
477
return (0x00FF0000 + atoi(&name[1]));
479
for (i = 0; (usage_lkp[i].usage_code != 0x0); i++)
481
if (!strcmp(usage_lkp[i].usage_name, name))
483
TRACE(2, "hid_lookup_usage: found %04x",
484
usage_lkp[i].usage_code);
485
return usage_lkp[i].usage_code;
492
#define NIBBLE(_i) (((_i) < 10) ? '0' + (_i) : 'A' + (_i) - 10)
494
void dump_hex (const char *msg, const unsigned char *buf, int len)
498
const unsigned char *pc;
500
const unsigned char *start;
507
for (i = 0, pc = buf, nlocal = len; i < 16; i++, pc++) {
511
*out++ = NIBBLE ((c >> 4) & 0xF);
512
*out++ = NIBBLE (c & 0xF);
524
TRACE(2, "%s: (%d bytes) => %s", msg, len, line);