1 /* $OpenBSD: upd.c,v 1.10 2014/07/12 18:48:52 tedu 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 struct upd_usage_entry { 43 uint8_t usage_pg; 44 uint8_t usage_id; 45 enum sensor_type senstype; 46 char *usage_name; /* sensor string */ 47 }; 48 49 static struct upd_usage_entry upd_usage_table[] = { 50 { HUP_BATTERY, HUB_REL_STATEOF_CHARGE, 51 SENSOR_PERCENT, "RelativeStateOfCharge" }, 52 { HUP_BATTERY, HUB_ABS_STATEOF_CHARGE, 53 SENSOR_PERCENT, "AbsoluteStateOfCharge" }, 54 { HUP_BATTERY, HUB_REM_CAPACITY, 55 SENSOR_PERCENT, "RemainingCapacity" }, 56 { HUP_BATTERY, HUB_FULLCHARGE_CAPACITY, 57 SENSOR_PERCENT, "FullChargeCapacity" }, 58 { HUP_BATTERY, HUB_CHARGING, 59 SENSOR_INDICATOR, "Charging" }, 60 { HUP_BATTERY, HUB_DISCHARGING, 61 SENSOR_INDICATOR, "Discharging" }, 62 { HUP_BATTERY, HUB_BATTERY_PRESENT, 63 SENSOR_INDICATOR, "BatteryPresent" }, 64 { HUP_POWER, HUP_SHUTDOWN_IMMINENT, 65 SENSOR_INDICATOR, "ShutdownImminent" }, 66 { HUP_BATTERY, HUB_AC_PRESENT, 67 SENSOR_INDICATOR, "ACPresent" }, 68 { HUP_BATTERY, HUB_ATRATE_TIMETOFULL, 69 SENSOR_TIMEDELTA, "AtRateTimeToFull" } 70 }; 71 72 struct upd_report { 73 size_t size; 74 int enabled; 75 }; 76 77 struct upd_sensor { 78 struct ksensor ksensor; 79 struct hid_item hitem; 80 int attached; 81 }; 82 83 struct upd_softc { 84 struct uhidev sc_hdev; 85 int sc_num_sensors; 86 u_int sc_max_repid; 87 u_int sc_max_sensors; 88 89 /* sensor framework */ 90 struct ksensordev sc_sensordev; 91 struct sensor_task *sc_sensortask; 92 struct upd_report *sc_reports; 93 struct upd_sensor *sc_sensors; 94 }; 95 96 int upd_match(struct device *, void *, void *); 97 void upd_attach(struct device *, struct device *, void *); 98 int upd_detach(struct device *, int); 99 100 void upd_refresh(void *); 101 void upd_update_sensors(struct upd_softc *, uint8_t *, unsigned int, int); 102 void upd_intr(struct uhidev *, void *, uint); 103 struct upd_usage_entry *upd_lookup_usage_entry(const struct hid_item *); 104 struct upd_sensor *upd_lookup_sensor(struct upd_softc *, int, int); 105 106 struct cfdriver upd_cd = { 107 NULL, "upd", DV_DULL 108 }; 109 110 const struct cfattach upd_ca = { 111 sizeof(struct upd_softc), 112 upd_match, 113 upd_attach, 114 upd_detach 115 }; 116 117 int 118 upd_match(struct device *parent, void *match, void *aux) 119 { 120 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 121 int size; 122 void *desc; 123 struct hid_data *hdata; 124 struct hid_item item; 125 int ret = UMATCH_NONE; 126 127 if (uha->reportid != UHIDEV_CLAIM_ALLREPORTID) 128 return (ret); 129 130 DPRINTF(("upd: vendor=0x%04x, product=0x%04x\n", uha->uaa->vendor, 131 uha->uaa->product)); 132 133 /* 134 * look for at least one sensor of our table 135 */ 136 uhidev_get_report_desc(uha->parent, &desc, &size); 137 for (hdata = hid_start_parse(desc, size, hid_feature); 138 hid_get_item(hdata, &item); ) { 139 if (upd_lookup_usage_entry(&item) != NULL) { 140 ret = UMATCH_VENDOR_PRODUCT; 141 break; 142 } 143 } 144 hid_end_parse(hdata); 145 146 return (ret); 147 } 148 149 void 150 upd_attach(struct device *parent, struct device *self, void *aux) 151 { 152 struct upd_softc *sc = (struct upd_softc *)self; 153 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 154 struct hid_item item; 155 struct hid_data *hdata; 156 struct upd_usage_entry *entry; 157 struct upd_sensor *sensor; 158 int size; 159 void *desc; 160 161 sc->sc_hdev.sc_intr = upd_intr; 162 sc->sc_hdev.sc_parent = uha->parent; 163 sc->sc_reports = NULL; 164 sc->sc_sensors = NULL; 165 sc->sc_max_sensors = nitems(upd_usage_table); 166 167 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 168 sizeof(sc->sc_sensordev.xname)); 169 170 sc->sc_max_repid = uha->parent->sc_nrepid; 171 DPRINTF(("\nupd: devname=%s sc_max_repid=%d\n", 172 sc->sc_hdev.sc_dev.dv_xname, sc->sc_max_repid)); 173 174 sc->sc_reports = malloc(sc->sc_max_repid * sizeof(struct upd_report), 175 M_USBDEV, M_WAITOK | M_ZERO); 176 size = sc->sc_max_sensors * sizeof(struct upd_sensor); 177 sc->sc_sensors = malloc(size, M_USBDEV, M_WAITOK | M_ZERO); 178 sc->sc_num_sensors = 0; 179 uhidev_get_report_desc(uha->parent, &desc, &size); 180 for (hdata = hid_start_parse(desc, size, hid_feature); 181 hid_get_item(hdata, &item) && 182 sc->sc_num_sensors < sc->sc_max_sensors; ) { 183 DPRINTF(("upd: repid=%d\n", item.report_ID)); 184 if (item.kind != hid_feature || 185 item.report_ID < 0) 186 continue; 187 188 if ((entry = upd_lookup_usage_entry(&item)) == NULL) 189 continue; 190 191 /* filter repeated usages, avoid duplicated sensors */ 192 sensor = upd_lookup_sensor(sc, entry->usage_pg, 193 entry->usage_id); 194 if (sensor && sensor->attached) 195 continue; 196 197 sensor = &sc->sc_sensors[sc->sc_num_sensors]; 198 /* keep our copy of hid_item */ 199 memcpy(&sensor->hitem, &item, sizeof(struct hid_item)); 200 strlcpy(sensor->ksensor.desc, entry->usage_name, 201 sizeof(sensor->ksensor.desc)); 202 sensor->ksensor.type = entry->senstype; 203 sensor->ksensor.flags |= SENSOR_FINVALID; 204 sensor->ksensor.status = SENSOR_S_UNKNOWN; 205 sensor->ksensor.value = 0; 206 sensor_attach(&sc->sc_sensordev, &sensor->ksensor); 207 sensor->attached = 1; 208 sc->sc_num_sensors++; 209 210 if (item.report_ID >= sc->sc_max_repid || 211 sc->sc_reports[item.report_ID].enabled) 212 continue; 213 214 sc->sc_reports[item.report_ID].size = hid_report_size(desc, 215 size, item.kind, item.report_ID); 216 sc->sc_reports[item.report_ID].enabled = 1; 217 } 218 hid_end_parse(hdata); 219 DPRINTF(("upd: sc_num_sensors=%d\n", sc->sc_num_sensors)); 220 221 sc->sc_sensortask = sensor_task_register(sc, upd_refresh, 6); 222 if (sc->sc_sensortask == NULL) { 223 printf(", unable to register update task\n"); 224 return; 225 } 226 sensordev_install(&sc->sc_sensordev); 227 228 printf("\n"); 229 230 DPRINTF(("upd_attach: complete\n")); 231 } 232 233 int 234 upd_detach(struct device *self, int flags) 235 { 236 struct upd_softc *sc = (struct upd_softc *)self; 237 struct upd_sensor *sensor; 238 int i; 239 240 if (sc->sc_sensortask != NULL) { 241 wakeup(&sc->sc_sensortask); 242 sensor_task_unregister(sc->sc_sensortask); 243 } 244 245 sensordev_deinstall(&sc->sc_sensordev); 246 247 for (i = 0; i < sc->sc_num_sensors; i++) { 248 sensor = &sc->sc_sensors[i]; 249 if (sensor->attached) 250 sensor_detach(&sc->sc_sensordev, &sensor->ksensor); 251 DPRINTF(("upd_detach: %s\n", sensor->ksensor.desc)); 252 } 253 254 free(sc->sc_reports, M_USBDEV, 0); 255 free(sc->sc_sensors, M_USBDEV, 0); 256 DPRINTF(("upd_detach: complete\n")); 257 return (0); 258 } 259 260 void 261 upd_refresh(void *arg) 262 { 263 struct upd_softc *sc = (struct upd_softc *)arg; 264 struct upd_report *report; 265 uint8_t buf[256]; 266 int repid, err; 267 268 for (repid = 0; repid < sc->sc_max_repid; repid++) { 269 report = &sc->sc_reports[repid]; 270 if (!report->enabled) 271 continue; 272 273 memset(buf, 0x0, sizeof(buf)); 274 /* 275 * XXX uhidev_get_report() is not clever enough to handle 276 * non-NUl reportID, so add an extra byte for it. 277 */ 278 err = uhidev_get_report(&sc->sc_hdev, UHID_FEATURE_REPORT, 279 repid, buf, report->size + 1); 280 if (err) { 281 DPRINTF(("read failure: reportid=%02x err=%d\n", repid, 282 err)); 283 continue; 284 } 285 286 upd_update_sensors(sc, buf, report->size, repid); 287 } 288 } 289 290 struct upd_usage_entry * 291 upd_lookup_usage_entry(const struct hid_item *hitem) 292 { 293 struct upd_usage_entry *entry = NULL; 294 int i; 295 296 for (i = 0; i < nitems(upd_usage_table); i++) { 297 entry = &upd_usage_table[i]; 298 if (entry->usage_pg == HID_GET_USAGE_PAGE(hitem->usage) && 299 entry->usage_id == HID_GET_USAGE(hitem->usage)) 300 return (entry); 301 } 302 return (NULL); 303 } 304 305 struct upd_sensor * 306 upd_lookup_sensor(struct upd_softc *sc, int page, int usage) 307 { 308 struct upd_sensor *sensor = NULL; 309 int i; 310 311 for (i = 0; i < sc->sc_num_sensors; i++) { 312 sensor = &sc->sc_sensors[i]; 313 if (page == HID_GET_USAGE_PAGE(sensor->hitem.usage) && 314 usage == HID_GET_USAGE(sensor->hitem.usage)) 315 return (sensor); 316 } 317 return (NULL); 318 } 319 320 void 321 upd_update_sensors(struct upd_softc *sc, uint8_t *buf, unsigned int len, 322 int repid) 323 { 324 struct upd_sensor *sensor; 325 ulong hdata, batpres; 326 ulong adjust; 327 int i; 328 329 sensor = upd_lookup_sensor(sc, HUP_BATTERY, HUB_BATTERY_PRESENT); 330 batpres = sensor ? sensor->ksensor.value : -1; 331 332 for (i = 0; i < sc->sc_num_sensors; i++) { 333 sensor = &sc->sc_sensors[i]; 334 if (!(sensor->hitem.report_ID == repid && sensor->attached)) 335 continue; 336 337 /* invalidate battery dependent sensors */ 338 if (HID_GET_USAGE_PAGE(sensor->hitem.usage) == HUP_BATTERY && 339 batpres <= 0) { 340 /* exception to the battery sensor itself */ 341 if (HID_GET_USAGE(sensor->hitem.usage) != 342 HUB_BATTERY_PRESENT) { 343 sensor->ksensor.status = SENSOR_S_UNKNOWN; 344 sensor->ksensor.flags |= SENSOR_FINVALID; 345 continue; 346 } 347 } 348 349 switch (HID_GET_USAGE(sensor->hitem.usage)) { 350 case HUB_REL_STATEOF_CHARGE: 351 case HUB_ABS_STATEOF_CHARGE: 352 case HUB_REM_CAPACITY: 353 case HUB_FULLCHARGE_CAPACITY: 354 adjust = 1000; /* scale adjust */ 355 break; 356 default: 357 adjust = 1; /* no scale adjust */ 358 break; 359 } 360 361 /* XXX first byte which is the report id */ 362 hdata = hid_get_data(buf + 1, len, &sensor->hitem.loc); 363 364 sensor->ksensor.value = hdata * adjust; 365 sensor->ksensor.status = SENSOR_S_OK; 366 sensor->ksensor.flags &= ~SENSOR_FINVALID; 367 DPRINTF(("%s: hidget data: %d\n", 368 sc->sc_sensordev.xname, hdata)); 369 } 370 } 371 372 373 void 374 upd_intr(struct uhidev *uh, void *p, uint len) 375 { 376 /* noop */ 377 } 378