1 /* $OpenBSD: upd.c,v 1.15 2015/04/27 07:37:19 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2014 Andre de Oliveira <andre@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* Driver for USB Power Devices sensors */ 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/kernel.h> 24 #include <sys/malloc.h> 25 #include <sys/device.h> 26 #include <sys/sensors.h> 27 28 #include <dev/usb/hid.h> 29 #include <dev/usb/usb.h> 30 #include <dev/usb/usbdi.h> 31 #include <dev/usb/usbdevs.h> 32 #include <dev/usb/usbhid.h> 33 #include <dev/usb/uhidev.h> 34 #include <dev/usb/usbdi_util.h> 35 36 #ifdef UPD_DEBUG 37 #define DPRINTF(x) do { printf x; } while (0) 38 #else 39 #define DPRINTF(x) 40 #endif 41 42 #define DEVNAME(sc) ((sc)->sc_hdev.sc_dev.dv_xname) 43 44 struct upd_usage_entry { 45 uint8_t usage_pg; 46 uint8_t usage_id; 47 enum sensor_type senstype; 48 char *usage_name; /* sensor string */ 49 }; 50 51 static struct upd_usage_entry upd_usage_table[] = { 52 { HUP_BATTERY, HUB_REL_STATEOF_CHARGE, 53 SENSOR_PERCENT, "RelativeStateOfCharge" }, 54 { HUP_BATTERY, HUB_ABS_STATEOF_CHARGE, 55 SENSOR_PERCENT, "AbsoluteStateOfCharge" }, 56 { HUP_BATTERY, HUB_REM_CAPACITY, 57 SENSOR_PERCENT, "RemainingCapacity" }, 58 { HUP_BATTERY, HUB_FULLCHARGE_CAPACITY, 59 SENSOR_PERCENT, "FullChargeCapacity" }, 60 { HUP_BATTERY, HUB_CHARGING, 61 SENSOR_INDICATOR, "Charging" }, 62 { HUP_BATTERY, HUB_DISCHARGING, 63 SENSOR_INDICATOR, "Discharging" }, 64 { HUP_BATTERY, HUB_BATTERY_PRESENT, 65 SENSOR_INDICATOR, "BatteryPresent" }, 66 { HUP_POWER, HUP_SHUTDOWN_IMMINENT, 67 SENSOR_INDICATOR, "ShutdownImminent" }, 68 { HUP_BATTERY, HUB_AC_PRESENT, 69 SENSOR_INDICATOR, "ACPresent" }, 70 { HUP_BATTERY, HUB_ATRATE_TIMETOFULL, 71 SENSOR_TIMEDELTA, "AtRateTimeToFull" } 72 }; 73 74 struct upd_report { 75 size_t size; 76 int enabled; 77 }; 78 79 struct upd_sensor { 80 struct ksensor ksensor; 81 struct hid_item hitem; 82 int attached; 83 }; 84 85 struct upd_softc { 86 struct uhidev sc_hdev; 87 int sc_num_sensors; 88 u_int sc_max_repid; 89 u_int sc_max_sensors; 90 91 /* sensor framework */ 92 struct ksensordev sc_sensordev; 93 struct sensor_task *sc_sensortask; 94 struct upd_report *sc_reports; 95 struct upd_sensor *sc_sensors; 96 }; 97 98 int upd_match(struct device *, void *, void *); 99 void upd_attach(struct device *, struct device *, void *); 100 int upd_detach(struct device *, int); 101 102 void upd_refresh(void *); 103 void upd_update_sensors(struct upd_softc *, uint8_t *, unsigned int, int); 104 void upd_intr(struct uhidev *, void *, uint); 105 struct upd_usage_entry *upd_lookup_usage_entry(const struct hid_item *); 106 struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int); 107 108 struct cfdriver upd_cd = { 109 NULL, "upd", DV_DULL 110 }; 111 112 const struct cfattach upd_ca = { 113 sizeof(struct upd_softc), 114 upd_match, 115 upd_attach, 116 upd_detach 117 }; 118 119 int 120 upd_match(struct device *parent, void *match, void *aux) 121 { 122 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 123 int size; 124 void *desc; 125 struct hid_data *hdata; 126 struct hid_item item; 127 int ret = UMATCH_NONE; 128 129 if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID) 130 return (ret); 131 132 DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor, 133 uha->uaa->product)); 134 135 /* 136 * look for at least one sensor of our table 137 */ 138 uhidev_get_report_desc(uha->parent, &desc, &size); 139 for (hdata = hid_start_parse(desc, size, hid_feature); 140 hid_get_item(hdata, &item); ) { 141 if (upd_lookup_usage_entry(&item) != NULL) { 142 ret = UMATCH_VENDOR_PRODUCT; 143 break; 144 } 145 } 146 hid_end_parse(hdata); 147 148 return (ret); 149 } 150 151 void 152 upd_attach(struct device *parent, struct device *self, void *aux) 153 { 154 struct upd_softc *sc = (struct upd_softc *)self; 155 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 156 struct hid_item item; 157 struct hid_data *hdata; 158 struct upd_usage_entry *entry; 159 struct upd_sensor *sensor; 160 int size; 161 void *desc; 162 163 sc->sc_hdev.sc_intr = upd_intr; 164 sc->sc_hdev.sc_parent = uha->parent; 165 sc->sc_reports = NULL; 166 sc->sc_sensors = NULL; 167 sc->sc_max_sensors = nitems(upd_usage_table); 168 169 strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), 170 sizeof(sc->sc_sensordev.xname)); 171 172 sc->sc_max_repid = uha->parent->sc_nrepid; 173 DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n", 174 DEVNAME(sc), sc->sc_max_repid)); 175 176 sc->sc_reports = mallocarray(sc->sc_max_repid, 177 sizeof(struct upd_report), M_USBDEV, M_WAITOK | M_ZERO); 178 sc->sc_sensors = mallocarray(sc->sc_max_sensors, 179 sizeof(struct upd_sensor), M_USBDEV, M_WAITOK | M_ZERO); 180 size = sc->sc_max_sensors * sizeof(struct upd_sensor); 181 sc->sc_num_sensors = 0; 182 uhidev_get_report_desc(uha->parent, &desc, &size); 183 for (hdata = hid_start_parse(desc, size, hid_feature); 184 hid_get_item(hdata, &item) && 185 sc->sc_num_sensors < sc->sc_max_sensors; ) { 186 DPRINTF(("upd: repid=%d\n", item.report_ID)); 187 if (item.kind != hid_feature || 188 item.report_ID < 0 || 189 item.report_ID >= sc->sc_max_repid) 190 continue; 191 192 if ((entry = upd_lookup_usage_entry(&item)) == NULL) 193 continue; 194 195 /* filter repeated usages, avoid duplicated sensors */ 196 sensor = upd_lookup_sensor(sc, entry->usage_pg, 197 entry->usage_id); 198 if (sensor != NULL) 199 continue; 200 201 sensor = &sc->sc_sensors[sc->sc_num_sensors]; 202 memcpy(&sensor->hitem, &item, sizeof(struct hid_item)); 203 strlcpy(sensor->ksensor.desc, entry->usage_name, 204 sizeof(sensor->ksensor.desc)); 205 sensor->ksensor.type = entry->senstype; 206 sensor->ksensor.flags |= SENSOR_FINVALID; 207 sensor->ksensor.status = SENSOR_S_UNKNOWN; 208 sensor->ksensor.value = 0; 209 sensor_attach(&sc->sc_sensordev, &sensor->ksensor); 210 sensor->attached = 1; 211 sc->sc_num_sensors++; 212 213 if (sc->sc_reports[item.report_ID].enabled) 214 continue; 215 216 sc->sc_reports[item.report_ID].size = hid_report_size(desc, 217 size, item.kind, item.report_ID); 218 sc->sc_reports[item.report_ID].enabled = 1; 219 } 220 hid_end_parse(hdata); 221 DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors)); 222 223 sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6); 224 if (sc->sc_sensortask == NULL) { 225 printf(", unable to register update task\n"); 226 return; 227 } 228 sensordev_install(&sc->sc_sensordev); 229 230 printf("\n"); 231 232 DPRINTF(("upd_attach: complete\n")); 233 } 234 235 int 236 upd_detach(struct device *self, int flags) 237 { 238 struct upd_softc *sc = (struct upd_softc *)self; 239 struct upd_sensor *sensor; 240 int i; 241 242 if (sc->sc_sensortask != NULL) { 243 wakeup(&sc->sc_sensortask); 244 sensor_task_unregister(sc->sc_sensortask); 245 } 246 247 sensordev_deinstall(&sc->sc_sensordev); 248 249 for (i = 0; i < sc->sc_num_sensors; i++) { 250 sensor = &sc->sc_sensors[i]; 251 if (sensor->attached) 252 sensor_detach(&sc->sc_sensordev, &sensor->ksensor); 253 DPRINTF(("upd_detach: %s\n", sensor->ksensor.desc)); 254 } 255 256 free(sc->sc_reports, M_USBDEV, 0); 257 free(sc->sc_sensors, M_USBDEV, 0); 258 DPRINTF(("upd_detach: complete\n")); 259 return (0); 260 } 261 262 void 263 upd_refresh(void *arg) 264 { 265 struct upd_softc *sc = (struct upd_softc *)arg; 266 struct upd_report *report; 267 uint8_t buf[256]; 268 int repid, actlen; 269 270 for (repid = 0; repid < sc->sc_max_repid; repid++) { 271 report = &sc->sc_reports[repid]; 272 if (!report->enabled) 273 continue; 274 275 memset(buf, 0x0, sizeof(buf)); 276 actlen = uhidev_get_report(sc->sc_hdev.sc_parent, 277 UHID_FEATURE_REPORT, repid, buf, report->size); 278 279 if (actlen == -1) { 280 DPRINTF(("upd: failed to get report id=%02x\n", repid)); 281 continue; 282 } 283 284 /* Deal with buggy firmwares. */ 285 if (actlen < report->size) 286 report->size = actlen; 287 288 upd_update_sensors(sc, buf, report->size, repid); 289 } 290 } 291 292 struct upd_usage_entry * 293 upd_lookup_usage_entry(const struct hid_item *hitem) 294 { 295 struct upd_usage_entry *entry = NULL; 296 int i; 297 298 for (i = 0; i < nitems(upd_usage_table); i++) { 299 entry = &upd_usage_table[i]; 300 if (entry->usage_pg == HID_GET_USAGE_PAGE(hitem->usage) && 301 entry->usage_id == HID_GET_USAGE(hitem->usage)) 302 return (entry); 303 } 304 return (NULL); 305 } 306 307 struct upd_sensor * 308 upd_lookup_sensor(struct upd_softc *sc, int page, int usage) 309 { 310 struct upd_sensor *sensor = NULL; 311 int i; 312 313 for (i = 0; i < sc->sc_num_sensors; i++) { 314 sensor = &sc->sc_sensors[i]; 315 if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) && 316 usage == HID_GET_USAGE(sensor->hitem.usage)) 317 return (sensor); 318 } 319 return (NULL); 320 } 321 322 void 323 upd_update_sensors(struct upd_softc *sc, uint8_t *buf, unsigned int len, 324 int repid) 325 { 326 struct upd_sensor *sensor; 327 ulong hdata, batpres; 328 ulong adjust; 329 int i; 330 331 sensor = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT); 332 batpres = sensor ? sensor->ksensor.value : -1; 333 334 for (i = 0; i < sc->sc_num_sensors; i++) { 335 sensor = &sc->sc_sensors[i]; 336 if (!(sensor->hitem.report_ID == repid && sensor->attached)) 337 continue; 338 339 /* invalidate battery dependent sensors */ 340 if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY && 341 batpres <= 0) { 342 /* exception to the battery sensor itself */ 343 if (HID_GET_USAGE(sensor->hitem.usage) != 344 HUB_BATTERY_PRESENT) { 345 sensor->ksensor.status = SENSOR_S_UNKNOWN; 346 sensor->ksensor.flags |= SENSOR_FINVALID; 347 continue; 348 } 349 } 350 351 switch (HID_GET_USAGE(sensor->hitem.usage)) { 352 case HUB_REL_STATEOF_CHARGE: 353 case HUB_ABS_STATEOF_CHARGE: 354 case HUB_REM_CAPACITY: 355 case HUB_FULLCHARGE_CAPACITY: 356 adjust = 1000; /* scale adjust */ 357 break; 358 default: 359 adjust = 1; /* no scale adjust */ 360 break; 361 } 362 363 hdata = hid_get_data(buf, len, &sensor->hitem.loc); 364 365 sensor->ksensor.value = hdata * adjust; 366 sensor->ksensor.status = SENSOR_S_OK; 367 sensor->ksensor.flags &= ~SENSOR_FINVALID; 368 DPRINTF(("%s: hidget data: %lu\n", DEVNAME(sc), hdata)); 369 } 370 } 371 372 373 void 374 upd_intr(struct uhidev *uh, void *p, uint len) 375 { 376 /* noop */ 377 } 378