1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
1 |
/* hidups.c - prototype HID UPS driver for Network UPS Tools
|
2 |
|
|
3 |
Copyright (C) 2001 Russell Kroll <rkroll@exploits.org>
|
|
4 |
|
|
5 |
Based on evtest.c v1.10 - Copyright (c) 1999-2000 Vojtech Pavlik
|
|
6 |
|
|
7 |
This program is free software; you can redistribute it and/or modify
|
|
8 |
it under the terms of the GNU General Public License as published by
|
|
9 |
the Free Software Foundation; either version 2 of the License, or
|
|
10 |
(at your option) any later version.
|
|
11 |
|
|
12 |
This program is distributed in the hope that it will be useful,
|
|
13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 |
GNU General Public License for more details.
|
|
16 |
|
|
17 |
You should have received a copy of the GNU General Public License
|
|
18 |
along with this program; if not, write to the Free Software
|
|
19 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
20 |
*/
|
|
21 |
||
22 |
#include "hidups.h" |
|
23 |
#include "main.h" |
|
24 |
||
25 |
int fd; |
|
26 |
||
27 |
#ifdef HIDIOCGPHYS
|
|
28 |
/* standards here are in flux for now */
|
|
29 |
char *hiddev_prefix = "/dev/usb/hid/hiddev"; /* in devfs */ |
|
30 |
int hiddev_limit = 16; /* no dynamic minors */ |
|
31 |
#endif
|
|
32 |
||
33 |
int setvalue(int type, int value); |
|
34 |
int offdelay = DEFAULT_OFFDELAY; |
|
35 |
int ondelay = DEFAULT_ONDELAY; |
|
36 |
||
37 |
void upsdrv_shutdown(void) |
|
38 |
{
|
|
39 |
/* XXX: replace with a proper shutdown function
|
|
40 |
fatalx("shutdown not supported"); */
|
|
41 |
||
42 |
/* 1) set DelayBeforeStartup */
|
|
43 |
if (getval ("ondelay")) |
|
44 |
ondelay = atoi (getval ("ondelay")); |
|
45 |
||
46 |
setvalue(UPS_WAKEDELAY, ondelay); |
|
47 |
||
48 |
/* 2) set DelayBeforeShutdown */
|
|
49 |
if (getval ("offdelay")) |
|
50 |
offdelay = atoi (getval ("offdelay")); |
|
51 |
||
52 |
setvalue(UPS_GRACEDELAY, offdelay); |
|
53 |
}
|
|
54 |
||
55 |
void instcmd(int auxcmd, int dlen, char *data) {} |
|
56 |
||
57 |
void upsdrv_help(void) {} |
|
58 |
||
59 |
void upsdrv_makevartable(void) |
|
60 |
{
|
|
61 |
char temp [128]; |
|
62 |
||
63 |
/* add command line/conf variables */
|
|
64 |
sprintf(temp, "Set shutdown delay, in seconds (default=%d).", DEFAULT_OFFDELAY); |
|
65 |
addvar (VAR_VALUE, "offdelay", temp); |
|
66 |
||
67 |
sprintf(temp, "Set startup delay, in ten seconds units for MGE (default=%d).", DEFAULT_ONDELAY); |
|
68 |
addvar (VAR_VALUE, "ondelay", temp); |
|
69 |
||
70 |
#ifdef HIDIOCGPHYS
|
|
71 |
||
72 |
/* add command line/conf variables */
|
|
73 |
snprintf(temp, sizeof(temp), "/dev/... prefix (default %s)", |
|
74 |
hiddev_prefix); |
|
75 |
||
76 |
addvar(VAR_VALUE, "HidDevPrefix", temp); |
|
77 |
||
78 |
snprintf(temp, sizeof(temp), |
|
79 |
"how many devices to check (default %d) ", |
|
80 |
hiddev_limit); |
|
81 |
||
82 |
addvar(VAR_VALUE, "HidDevLimit", temp); |
|
83 |
#endif
|
|
84 |
}
|
|
85 |
||
86 |
void upsdrv_banner(void) |
|
87 |
{
|
|
88 |
printf("Network UPS Tools: HID UPS driver 0.13 (%s)\n\n", UPS_VERSION); |
|
89 |
||
90 |
experimental_driver = 1; |
|
91 |
}
|
|
92 |
||
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
93 |
static inline int find_application(int fd, int usage) |
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
94 |
{
|
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
95 |
int i = 0, ret; |
96 |
||
97 |
while ((ret = ioctl(fd, HIDIOCAPPLICATION, i)) > 0 && |
|
98 |
ret != usage) |
|
99 |
i++; |
|
100 |
||
101 |
return (ret == usage); |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
102 |
}
|
103 |
||
104 |
static void parse_event(struct hiddev_event *ev) |
|
105 |
{
|
|
106 |
int sc = 0; |
|
107 |
static int ol = -1, ob = -1, lb = -1; |
|
108 |
||
109 |
/* XXX: deal with bogosity by throwing it out */
|
|
110 |
if (ev->value > 3000000) { |
|
111 |
upslogx(LOG_INFO, "Ignoring bogus event 0x%x (%d)", |
|
112 |
ev->hid, ev->value); |
|
113 |
return; |
|
114 |
}
|
|
115 |
||
116 |
/* x86 page == ups-specific, ignore these for now */
|
|
117 |
if ((ev->hid & 0x860000) == 0x860000) { |
|
118 |
upslogx(LOG_INFO, "Ignoring x86 page event 0x%x (%d)", |
|
119 |
ev->hid, ev->value); |
|
120 |
return; |
|
121 |
}
|
|
122 |
||
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
123 |
upsdebugx(2, "event(%x, %d)",ev->hid,ev->value); |
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
124 |
switch (ev->hid) { |
125 |
case UPS_BATTVOLT: |
|
126 |
dstate_setinfo("battery.voltage", "%2.1f", |
|
127 |
ev->value / 100.0); |
|
128 |
break; |
|
129 |
case BATT_RUNTIME_TO_EMPTY: |
|
130 |
dstate_setinfo("battery.runtime", "%d", ev->value); |
|
131 |
break; |
|
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
132 |
case UPS_LOADPCT: |
133 |
dstate_setinfo("ups.load", "%d", ev->value); |
|
134 |
break; |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
135 |
case BATT_REMAINING_CAPACITY: |
136 |
dstate_setinfo("battery.charge", "%d", ev->value); |
|
137 |
break; |
|
138 |
||
139 |
/* OB/OL/LB: update temp storage and flag changed */
|
|
140 |
case BATT_DISCHARGING: |
|
141 |
if ((ev->value == 0) || (ev->value == 1)) { |
|
142 |
ob = ev->value; |
|
143 |
sc = 1; |
|
144 |
}
|
|
145 |
else
|
|
146 |
upslogx(LOG_WARNING, |
|
147 |
"Got bogus value for BATT_DISCHARGING: %d", |
|
148 |
ev->value); |
|
149 |
break; |
|
150 |
||
151 |
case BATT_AC_PRESENT: |
|
152 |
if ((ev->value == 0) || (ev->value == 1)) { |
|
153 |
ol = ev->value; |
|
154 |
sc = 1; |
|
155 |
}
|
|
156 |
else
|
|
157 |
upslogx(LOG_WARNING, |
|
158 |
"Got bogus value for BATT_AC_PRESENT: %d", |
|
159 |
ev->value); |
|
160 |
break; |
|
161 |
||
162 |
case UPS_SHUTDOWN_IMMINENT: |
|
163 |
if ((ev->value == 0) || (ev->value == 1)) { |
|
164 |
lb = ev->value; |
|
165 |
sc = 1; |
|
166 |
}
|
|
167 |
else
|
|
168 |
upslogx(LOG_WARNING, |
|
169 |
"Got bogus value for UPS_SHUTDOWN_IMMINENT: %d", |
|
170 |
ev->value); |
|
171 |
break; |
|
172 |
||
173 |
/* things that we don't care about */
|
|
174 |
case BATT_BELOW_RCL: |
|
175 |
case BATT_CHARGING: |
|
176 |
break; |
|
177 |
||
178 |
default: |
|
179 |
upslogx(LOG_INFO, "Unhandled event: 0x%x (%d)", |
|
180 |
ev->hid, ev->value); |
|
181 |
}
|
|
182 |
||
183 |
/* deal with any status changes */
|
|
184 |
if (sc == 0) |
|
185 |
return; |
|
186 |
||
187 |
status_init(); |
|
188 |
||
189 |
if (ol == 1) |
|
190 |
status_set("OL"); |
|
191 |
||
192 |
if (ob == 1) |
|
193 |
status_set("OB"); |
|
194 |
||
195 |
if (lb == 1) |
|
196 |
status_set("LB"); |
|
197 |
||
198 |
status_commit(); |
|
199 |
dstate_dataok(); |
|
200 |
}
|
|
201 |
||
202 |
static int getvalue(int type) |
|
203 |
{
|
|
204 |
struct hiddev_usage_ref uref; |
|
205 |
||
206 |
/* TODO: build a report table so we don't need HID_REPORT_ID_UNKNOWN */
|
|
207 |
||
208 |
memset(&uref, 0, sizeof(uref)); |
|
209 |
uref.report_type = HID_REPORT_TYPE_FEATURE; |
|
210 |
uref.report_id = HID_REPORT_ID_UNKNOWN; |
|
211 |
uref.field_index = 0; |
|
212 |
uref.usage_index = 0; |
|
213 |
uref.usage_code = type; |
|
214 |
uref.value = 0; |
|
215 |
||
216 |
if (ioctl(fd, HIDIOCGUSAGE, &uref) >= 0) |
|
217 |
return uref.value; |
|
218 |
else
|
|
219 |
return -2; /* -1 is valid for Delay values (== not set) */ |
|
220 |
}
|
|
221 |
||
222 |
static char *getstring(int type) |
|
223 |
{
|
|
224 |
struct hiddev_usage_ref uref; |
|
225 |
struct hiddev_string_descriptor sdesc; |
|
226 |
static char str[256]; |
|
227 |
||
228 |
/* TODO: build a report table so we don't need HID_REPORT_ID_UNKNOWN */
|
|
229 |
||
230 |
memset(&uref, 0, sizeof(uref)); |
|
231 |
memset(&sdesc, 0, sizeof(sdesc)); |
|
232 |
uref.report_type = HID_REPORT_TYPE_FEATURE; |
|
233 |
uref.report_id = HID_REPORT_ID_UNKNOWN; |
|
234 |
uref.usage_code = type; |
|
235 |
||
236 |
snprintf(str, sizeof(str), "Unknown"); |
|
237 |
if (ioctl(fd, HIDIOCGUSAGE, &uref) == 0) { |
|
238 |
if ( 0 != (sdesc.index = uref.value)) { |
|
239 |
if (ioctl(fd, HIDIOCGSTRING, &sdesc) > 0) |
|
240 |
snprintf(str, sizeof(str), "%s", sdesc.value); |
|
241 |
else
|
|
242 |
upslog(LOG_ERR, "ioctl HIDIOCGSTRING"); |
|
243 |
}
|
|
244 |
}
|
|
245 |
||
246 |
return str; |
|
247 |
}
|
|
248 |
||
249 |
/* results of querying some x86 page values on my APC UPS */
|
|
250 |
||
251 |
/* note to APC: i can decode your "secret protocol" just by *
|
|
252 |
* looking at it. you're not helping anyone by keeping it closed! */
|
|
253 |
||
254 |
/* 0x860060 == "441HMLL" - looks like a 'capability' string */
|
|
255 |
/* == locale 4, 4 choices, 1 byte each */
|
|
256 |
/* == line sensitivity (high, medium, low, low) */
|
|
257 |
||
258 |
/* 0x860013 == 44200155090 - capability again */
|
|
259 |
/* == locale 4, 4 choices, 2 bytes, 00, 15, 50, 90 */
|
|
260 |
/* == minimum charge to return online */
|
|
261 |
||
262 |
/* 0x860062 == D43133136127130 */
|
|
263 |
/* == locale D, 4 choices, 3 bytes, 133, 136, 127, 130 */
|
|
264 |
/* == high transfer voltage */
|
|
265 |
||
266 |
/* 0x860064 == D43103100097106 */
|
|
267 |
/* == locale D, 4 choices, 3 bytes, 103, 100, 097, 106 */
|
|
268 |
/* == low transfer voltage */
|
|
269 |
||
270 |
/* 0x860066 == 441HMLL (see 860060) */
|
|
271 |
||
272 |
/* 0x860074 == 4410TLN */
|
|
273 |
/* == locale 4, 4 choices, 1 byte, 0, T, L, N */
|
|
274 |
/* == alarm setting (5s, 30s, low battery, none) */
|
|
275 |
||
276 |
/* 0x860077 == 443060180300600 */
|
|
277 |
/* == locale 4, 4 choices, 3 bytes, 060,180,300,600 */
|
|
278 |
/* == wake-up delay (after power returns) */
|
|
279 |
||
280 |
void upsdrv_updateinfo(void) |
|
281 |
{
|
|
282 |
fd_set fdset; |
|
283 |
struct timeval tv; |
|
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
284 |
int rd; |
285 |
unsigned int i; |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
286 |
struct hiddev_event ev[64]; |
287 |
||
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
288 |
/* For some reason, APC XS BX1500 does not report changes
|
289 |
* in LOADPCT without HIDIOCINITREPORT. */
|
|
290 |
ioctl(fd, HIDIOCINITREPORT, 0); |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
291 |
FD_ZERO(&fdset); |
292 |
FD_SET(fd, &fdset); |
|
293 |
tv.tv_sec = 0; |
|
294 |
tv.tv_usec = 0; |
|
295 |
rd = select(fd+1, &fdset, NULL, NULL, &tv); |
|
296 |
||
297 |
/* XXX: alarm around this read */
|
|
298 |
if (rd > 0) { |
|
299 |
rd = read(fd, ev, sizeof(ev)); |
|
300 |
||
301 |
if (rd < (int) sizeof(ev[0])) { |
|
302 |
if (rd < 0) |
|
303 |
fatal("read"); |
|
304 |
else { |
|
305 |
upslog(LOG_INFO, "short read from device"); |
|
306 |
dstate_datastale(); |
|
307 |
}
|
|
308 |
}
|
|
309 |
||
310 |
for (i = 0; i < rd / sizeof(ev[0]); i++) |
|
311 |
parse_event(&ev[i]); |
|
312 |
} /* if rd > 0 */ |
|
313 |
}
|
|
314 |
||
315 |
static void addhidvalue(int query, const char *varname) |
|
316 |
{
|
|
317 |
int val; |
|
318 |
||
319 |
val = getvalue(query); |
|
320 |
||
321 |
if (val < -1) |
|
322 |
return; |
|
323 |
||
324 |
/* XXX: deal with "3 million" kernel bogosity for now */
|
|
325 |
if (val > 3000000) { |
|
326 |
int i; |
|
327 |
||
328 |
for (i = 0; i < 5; i++) { |
|
329 |
val = getvalue(query); |
|
330 |
if (val < 3000000) |
|
331 |
break; |
|
332 |
}
|
|
333 |
||
334 |
if (val > 3000000) { |
|
335 |
upslogx(LOG_WARNING, "Unable to add 0x%x: got bogus value %d", |
|
336 |
query, val); |
|
337 |
return; |
|
338 |
}
|
|
339 |
}
|
|
340 |
||
341 |
dstate_setinfo(varname, "%d", val); |
|
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
342 |
upsdebugx(2, "addhidvalue(%x, %s): obtained %d", |
343 |
query, varname, val); |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
344 |
}
|
345 |
||
346 |
int setvalue(int type, int value) |
|
347 |
{
|
|
348 |
struct hiddev_usage_ref uref; |
|
349 |
struct hiddev_report_info rinfo; |
|
350 |
||
351 |
/* 1) issue a get to retrieve all fields values */
|
|
352 |
memset(&uref, 0, sizeof(uref)); |
|
353 |
uref.report_type = HID_REPORT_TYPE_FEATURE; |
|
354 |
uref.report_id = HID_REPORT_ID_UNKNOWN; |
|
355 |
uref.field_index = 0; |
|
356 |
uref.usage_index = 0; |
|
357 |
uref.usage_code = type; |
|
358 |
uref.value = 0; |
|
359 |
||
360 |
if (ioctl(fd, HIDIOCGUSAGE, &uref) >= 0) { |
|
361 |
||
362 |
upsdebugx(2, "setvalue(%x, %d): obtained %d (report %x))", |
|
363 |
type, value, uref.value, uref.report_id); |
|
364 |
||
365 |
/* get report info */
|
|
366 |
rinfo.report_type = HID_REPORT_TYPE_FEATURE; |
|
367 |
rinfo.report_id = uref.report_id; |
|
368 |
||
369 |
if (ioctl(fd, HIDIOCGREPORT, &rinfo) >= 0) { |
|
370 |
||
371 |
/* set usage in report */
|
|
372 |
uref.report_type = HID_REPORT_TYPE_OUTPUT; |
|
373 |
uref.value = value; |
|
374 |
if (ioctl(fd, HIDIOCSUSAGE, &uref) >= 0) { |
|
375 |
||
376 |
/* write report to device */
|
|
377 |
rinfo.report_type = HID_REPORT_TYPE_OUTPUT; |
|
378 |
rinfo.report_id = uref.report_id; |
|
379 |
if (ioctl(fd, HIDIOCSREPORT, &rinfo) >= 0) { |
|
380 |
||
381 |
upslogx(1, "setvalue(): report sent successfully"); |
|
382 |
return 1; |
|
383 |
}
|
|
384 |
} /* for MGE (maybe others !), use HID_REPORT_TYPE_FEATURE */ |
|
385 |
else { |
|
386 |
uref.report_type = HID_REPORT_TYPE_FEATURE; |
|
387 |
if (ioctl(fd, HIDIOCSUSAGE, &uref) >= 0) { |
|
388 |
||
389 |
/* write report to device */
|
|
390 |
rinfo.report_type = HID_REPORT_TYPE_FEATURE; |
|
391 |
if (ioctl(fd, HIDIOCSREPORT, &rinfo) >= 0) { |
|
392 |
||
393 |
upslogx(1, "setvalue(using type = feature): report sent successfully"); |
|
394 |
return 1; |
|
395 |
}
|
|
396 |
}
|
|
397 |
}
|
|
398 |
}
|
|
399 |
return -1; |
|
400 |
}
|
|
401 |
else
|
|
402 |
return -1; |
|
403 |
}
|
|
404 |
||
405 |
/* handler for settable variables in UPS*/
|
|
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
406 |
int setvar(const char *varname, const char *val) |
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
407 |
{
|
408 |
/* int setvalue(int type, int value) */
|
|
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
409 |
|
410 |
return STAT_SET_UNKNOWN; |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
411 |
}
|
412 |
||
413 |
void upsdrv_initinfo(void) |
|
414 |
{
|
|
415 |
int val; |
|
416 |
char *str, *ptr; |
|
417 |
||
418 |
dstate_setinfo("ups.mfr", "Generic"); |
|
419 |
||
420 |
str = getstring(UPS_IMFR); |
|
421 |
||
422 |
if (str) |
|
423 |
dstate_setinfo("ups.mfr", "%s", str); |
|
424 |
||
425 |
str = getstring(UPS_IPRODUCT); |
|
426 |
||
427 |
if (str) { |
|
428 |
||
429 |
/* try to trim this back to something reasonable */
|
|
430 |
if ((!strncmp(str, "BackUPS Pro", 11)) || |
|
431 |
(!strncmp(str, "Back-UPS ES", 11)) || |
|
432 |
(!strncmp(str, "Smart-UPS", 9))) { |
|
433 |
ptr = strstr(str, "FW"); |
|
434 |
if (ptr) |
|
435 |
*(ptr - 1) = '\0'; |
|
436 |
||
437 |
/* we can be pretty sure of this now */
|
|
438 |
dstate_setinfo("ups.mfr", "%s", "APC"); |
|
439 |
}
|
|
440 |
||
441 |
/* non-Pro models seem to have USB ports too now */
|
|
442 |
if ((!strncmp(str, "BackUPS ", 8)) && (isdigit(str[8]))) { |
|
443 |
ptr = strstr(&str[8], " "); |
|
444 |
if (ptr) |
|
445 |
*(ptr - 1) = '\0'; |
|
446 |
||
447 |
/* we can be pretty sure of this now */
|
|
448 |
dstate_setinfo("ups.mfr", "%s", "APC"); |
|
449 |
}
|
|
450 |
||
451 |
dstate_setinfo("ups.model", "%s", str); |
|
452 |
}
|
|
453 |
else
|
|
454 |
dstate_setinfo("ups.model", "Generic USB UPS"); |
|
455 |
||
456 |
str = getstring(UPS_ISERIAL); |
|
457 |
||
458 |
if (str) |
|
459 |
dstate_setinfo("ups.serial", "%s", str); |
|
460 |
||
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
461 |
str = getstring(BATT_ICHEMISTRY); |
462 |
if (str) |
|
463 |
dstate_setinfo("battery.chemistry", "%s", str); |
|
464 |
||
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
465 |
/* seed the status register */
|
466 |
||
467 |
val = getvalue(BATT_AC_PRESENT); |
|
468 |
||
469 |
if (val == 1) |
|
470 |
dstate_setinfo("ups.status", "OL"); |
|
471 |
else
|
|
472 |
dstate_setinfo("ups.status", "OB"); |
|
473 |
||
474 |
val = getvalue(UPS_BATTVOLT); |
|
475 |
||
476 |
if (val > 0) |
|
477 |
dstate_setinfo("battery.voltage", "%2.1f", val / 100.0); |
|
478 |
||
479 |
/* XXX: set capacitymode to percent */
|
|
480 |
addhidvalue(BATT_REMAINING_CAPACITY, "battery.charge"); |
|
481 |
addhidvalue(BATT_RUNTIME_TO_EMPTY, "battery.runtime"); |
|
482 |
addhidvalue(UPS_LOADPCT, "ups.load"); |
|
483 |
||
484 |
addhidvalue(UPS_WAKEDELAY, "ups.delay.start"); |
|
485 |
addhidvalue(UPS_GRACEDELAY, "ups.delay.shutdown"); |
|
486 |
||
487 |
/* install handlers */
|
|
488 |
upsh.setvar = setvar; |
|
489 |
||
490 |
dstate_dataok(); |
|
491 |
}
|
|
492 |
||
493 |
static void open_usb(const char *port, int flags) |
|
494 |
{
|
|
495 |
#ifdef HIDIOCGPHYS
|
|
496 |
int i; |
|
497 |
#endif
|
|
498 |
||
499 |
/* device paths, like /dev/usb/hiddevN, are unstable in any
|
|
500 |
* hotplug-oriented bus (like usb) except in exotic cases
|
|
501 |
* where device enumeration order is predictable.
|
|
502 |
*/
|
|
503 |
if (strncmp (port, "usb-", 4) != 0) { |
|
504 |
if ((fd = open (port, flags)) < 0) |
|
505 |
fatal("hiddev path open %s", port); |
|
506 |
return; |
|
507 |
}
|
|
508 |
||
509 |
#ifdef HIDIOCGPHYS
|
|
510 |
/* better: stable ids, which don't change unless usb (or pci)
|
|
511 |
* topology morphs. like usb-00:02.3-3.4:0, where
|
|
512 |
* - '00:02.3' is a usb host controller id, in this case a
|
|
513 |
* pci slot name which is stable enough for most purposes.
|
|
514 |
* - '3.4' walks the usb tree: start at the third root hub
|
|
515 |
* port, which has another hub. then go to the ups on the
|
|
516 |
* fourth port of that hub.
|
|
517 |
* - ':0' says interface zero on that ups
|
|
518 |
*
|
|
519 |
* to use those we need to scan all the hid device paths, but
|
|
520 |
* when we're done we know that a usb mouse or keyboard config
|
|
521 |
* change won't have broken our ups config.
|
|
522 |
*/
|
|
523 |
if (getval ("HidDevPrefix")) |
|
524 |
hiddev_prefix = getval ("HidDevPrefix"); |
|
525 |
if (getval ("HidDevLimit")) |
|
526 |
hiddev_limit = atoi (getval ("HidDevLimit")); |
|
527 |
for (i = 0; i < hiddev_limit; i++) { |
|
528 |
char tmp [128]; |
|
529 |
||
530 |
snprintf (tmp, sizeof tmp, "%s%d", hiddev_prefix, i); |
|
531 |
if ((fd = open (tmp, flags)) < 0) |
|
532 |
continue; |
|
533 |
||
534 |
if (ioctl (fd, HIDIOCGPHYS(sizeof tmp), &tmp) > 0) { |
|
535 |
if (strcmp (port, tmp) == 0) |
|
536 |
return; |
|
537 |
}
|
|
538 |
close (fd); |
|
539 |
}
|
|
540 |
fatalx("can't match %s, tried up to %s%d", |
|
541 |
port, hiddev_prefix, hiddev_limit); |
|
542 |
#else
|
|
543 |
#warning "kernel doesn't return HIDDEV physical port paths"
|
|
544 |
fatalx("can't understand %s, no kernel support", port); |
|
545 |
#endif /* HIDIOCGPHYS */ |
|
546 |
}
|
|
547 |
||
548 |
void upsdrv_initups(void) |
|
549 |
{
|
|
550 |
char name[256]; |
|
551 |
||
552 |
open_usb(device_path, O_RDONLY); |
|
553 |
||
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
554 |
if ((!find_application(fd, UPS_USAGE)) && |
555 |
(!find_application(fd, POWER_USAGE))) |
|
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
556 |
fatalx("%s is not a UPS\n", device_path); |
557 |
||
558 |
ioctl(fd, HIDIOCGNAME(sizeof(name)), name); |
|
559 |
printf("Detected %s\n", name); |
|
560 |
printf("on port %s\n", device_path); |
|
561 |
||
1.1.2
by Reinhard Tartler
Import upstream version 2.0.2 |
562 |
ioctl(fd, HIDIOCINITREPORT, 0); |
1.1.1
by Arnaud Quette
Import upstream version 1.4.2 |
563 |
}
|
564 |
||
565 |
void upsdrv_cleanup(void) |
|
566 |
{
|
|
567 |
}
|