1 /* $OpenBSD: utwitch.c,v 1.17 2017/04/08 02:57:25 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Yojiro UO <yuo@nui.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 Maywa-Denki & KAYAC YUREX BBU sensor */ 20 /* this driver was previously known as uyurex(4). */ 21 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/kernel.h> 25 #include <sys/malloc.h> 26 #include <sys/device.h> 27 #include <sys/conf.h> 28 #include <sys/sensors.h> 29 30 #include <dev/usb/usb.h> 31 #include <dev/usb/usbhid.h> 32 #include <dev/usb/usbdi.h> 33 #include <dev/usb/usbdi_util.h> 34 #include <dev/usb/usbdevs.h> 35 #include <dev/usb/uhidev.h> 36 37 #define CMD_NONE 0xf0 38 #define CMD_EOF 0x0d 39 #define CMD_ACK 0x21 40 #define CMD_MODE 0x41 /* XXX */ 41 #define CMD_VALUE 0x43 42 #define CMD_READ 0x52 43 #define CMD_WRITE 0x53 44 #define CMD_PADDING 0xff 45 46 #define UPDATE_TICK 5 /* sec */ 47 48 #ifdef UYUREX_DEBUG 49 #define DPRINTF(x) do { printf x; } while (0) 50 #else 51 #define DPRINTF(x) 52 #endif 53 54 struct utwitch_softc { 55 struct uhidev sc_hdev; 56 struct usbd_device *sc_udev; 57 58 /* uhidev parameters */ 59 size_t sc_flen; /* feature report length */ 60 size_t sc_ilen; /* input report length */ 61 size_t sc_olen; /* output report length */ 62 63 uint8_t *sc_ibuf; 64 65 /* sensor framework */ 66 struct ksensor sc_sensor_val; 67 struct ksensor sc_sensor_delta; 68 struct ksensordev sc_sensordev; 69 struct sensor_task *sc_sensortask; 70 71 /* device private */ 72 int sc_initialized; 73 uint8_t issueing_cmd; 74 uint8_t accepted_cmd; 75 76 uint32_t sc_curval; 77 uint32_t sc_oldval; 78 }; 79 80 const struct usb_devno utwitch_devs[] = { 81 { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_YUREX}, 82 }; 83 84 int utwitch_match(struct device *, void *, void *); 85 void utwitch_attach(struct device *, struct device *, void *); 86 int utwitch_detach(struct device *, int); 87 88 void utwitch_set_mode(struct utwitch_softc *, uint8_t); 89 void utwitch_read_value_request(struct utwitch_softc *); 90 void utwitch_write_value_request(struct utwitch_softc *, uint32_t); 91 92 void utwitch_intr(struct uhidev *, void *, u_int); 93 void utwitch_refresh(void *); 94 95 struct cfdriver utwitch_cd = { 96 NULL, "utwitch", DV_DULL 97 }; 98 99 const struct cfattach utwitch_ca = { 100 sizeof(struct utwitch_softc), 101 utwitch_match, 102 utwitch_attach, 103 utwitch_detach 104 }; 105 106 int 107 utwitch_match(struct device *parent, void *match, void *aux) 108 { 109 struct uhidev_attach_arg *uha = aux; 110 111 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 112 return (UMATCH_NONE); 113 114 return (usb_lookup(utwitch_devs, uha->uaa->vendor, uha->uaa->product) != NULL ? 115 UMATCH_VENDOR_PRODUCT : UMATCH_NONE); 116 } 117 118 void 119 utwitch_attach(struct device *parent, struct device *self, void *aux) 120 { 121 struct utwitch_softc *sc = (struct utwitch_softc *)self; 122 struct usb_attach_arg *uaa = aux; 123 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 124 struct usbd_device *dev = uha->parent->sc_udev; 125 int size, repid, err; 126 void *desc; 127 128 sc->sc_udev = dev; 129 sc->sc_hdev.sc_intr = utwitch_intr; 130 sc->sc_hdev.sc_parent = uha->parent; 131 sc->sc_hdev.sc_report_id = uha->reportid; 132 133 uhidev_get_report_desc(uha->parent, &desc, &size); 134 repid = uha->reportid; 135 sc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 136 sc->sc_olen = hid_report_size(desc, size, hid_output, repid); 137 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 138 139 err = uhidev_open(&sc->sc_hdev); 140 if (err) { 141 printf("utwitch_open: uhidev_open %d\n", err); 142 return; 143 } 144 sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK); 145 146 printf("\n"); 147 148 149 /* attach sensor */ 150 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 151 sizeof(sc->sc_sensordev.xname)); 152 153 /* add BBU sensor */ 154 sc->sc_sensor_val.type = SENSOR_INTEGER; 155 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_val); 156 strlcpy(sc->sc_sensor_val.desc, "BBU", 157 sizeof(sc->sc_sensor_val.desc)); 158 159 /* add BBU delta sensor */ 160 sc->sc_sensor_delta.type = SENSOR_INTEGER; 161 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_delta); 162 strlcpy(sc->sc_sensor_delta.desc, "mBBU/sec", 163 sizeof(sc->sc_sensor_delta.desc)); 164 165 sc->sc_sensortask = sensor_task_register(sc, utwitch_refresh, UPDATE_TICK); 166 if (sc->sc_sensortask == NULL) { 167 printf(", unable to register update task\n"); 168 return; 169 } 170 sensordev_install(&sc->sc_sensordev); 171 172 DPRINTF(("utwitch_attach: complete\n")); 173 174 /* init device */ /* XXX */ 175 utwitch_set_mode(sc, 0); 176 } 177 178 int 179 utwitch_detach(struct device *self, int flags) 180 { 181 struct utwitch_softc *sc = (struct utwitch_softc *)self; 182 int rv = 0; 183 184 wakeup(&sc->sc_sensortask); 185 sensordev_deinstall(&sc->sc_sensordev); 186 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_val); 187 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_delta); 188 if (sc->sc_sensortask != NULL) 189 sensor_task_unregister(sc->sc_sensortask); 190 191 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 192 uhidev_close(&sc->sc_hdev); 193 194 if (sc->sc_ibuf != NULL) { 195 free(sc->sc_ibuf, M_USBDEV, sc->sc_ilen); 196 sc->sc_ibuf = NULL; 197 } 198 199 return (rv); 200 } 201 202 void 203 utwitch_intr(struct uhidev *addr, void *ibuf, u_int len) 204 { 205 struct utwitch_softc *sc = (struct utwitch_softc *)addr; 206 uint8_t buf[8]; 207 uint32_t val; 208 209 if (sc->sc_ibuf == NULL) 210 return; 211 212 /* process requests */ 213 memcpy(buf, ibuf, 8); 214 DPRINTF(("intr: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", 215 buf[0], buf[1], buf[2], buf[3], 216 buf[4], buf[5], buf[6], buf[7])); 217 218 219 switch (buf[0]) { 220 case CMD_ACK: 221 if (buf[1] == sc->issueing_cmd) { 222 DPRINTF(("ack received for cmd 0x%.2x\n", buf[1])); 223 sc->accepted_cmd = buf[1]; 224 } else { 225 DPRINTF(("cmd-ack mismatch: recved 0x%.2x, expect 0x%.2x\n", 226 buf[1], sc->issueing_cmd)); 227 /* discard previous command */ 228 sc->accepted_cmd = CMD_NONE; 229 sc->issueing_cmd = CMD_NONE; 230 } 231 break; 232 case CMD_READ: 233 case CMD_VALUE: 234 val = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8) + buf[5]; 235 if (!sc->sc_initialized) { 236 sc->sc_oldval = val; 237 sc->sc_initialized = 1; 238 } 239 sc->sc_sensor_val.value = val; 240 sc->sc_curval = val; 241 DPRINTF(("recv value update message: %d\n", val)); 242 break; 243 default: 244 DPRINTF(("unknown message: 0x%.2x\n", buf[0])); 245 } 246 247 return; 248 } 249 250 void 251 utwitch_refresh(void *arg) 252 { 253 struct utwitch_softc *sc = arg; 254 255 if (!sc->sc_initialized) { 256 utwitch_read_value_request(sc); 257 } else { 258 /* calculate delta value */ 259 sc->sc_sensor_delta.value = 260 (1000 * (sc->sc_curval - sc->sc_oldval)) / UPDATE_TICK; 261 sc->sc_oldval = sc->sc_curval; 262 } 263 } 264 265 void 266 utwitch_set_mode(struct utwitch_softc *sc, uint8_t val) 267 { 268 uint8_t req[8]; 269 270 memset(req, CMD_PADDING, sizeof(req)); 271 req[0] = CMD_MODE; 272 req[1] = val; 273 req[2] = CMD_EOF; 274 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 275 sc->sc_hdev.sc_report_id, req, sc->sc_olen) != sc->sc_olen) { 276 printf("uhidev_set_report error:EIO\n"); 277 return; 278 } 279 280 /* wait ack */ 281 tsleep(&sc->sc_sensortask, 0, "utwitch", (1000*hz+999)/1000 + 1); 282 } 283 284 void 285 utwitch_read_value_request(struct utwitch_softc *sc) 286 { 287 uint8_t req[8]; 288 289 memset(req, CMD_PADDING, sizeof(req)); 290 req[0] = CMD_READ; 291 req[1] = CMD_EOF; 292 sc->issueing_cmd = CMD_READ; 293 sc->accepted_cmd = CMD_NONE; 294 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 295 sc->sc_hdev.sc_report_id, req, sc->sc_olen) != sc->sc_olen) 296 return; 297 298 /* wait till sensor data are updated, 500ms will be enough */ 299 tsleep(&sc->sc_sensortask, 0, "utwitch", (500*hz+999)/1000 + 1); 300 } 301 302 void 303 utwitch_write_value_request(struct utwitch_softc *sc, uint32_t val) 304 { 305 uint32_t v; 306 uint8_t req[8]; 307 308 req[0] = CMD_WRITE; 309 req[1] = 0; 310 req[6] = CMD_EOF; 311 req[7] = CMD_PADDING; 312 v = htobe32(val); 313 memcpy(req + 2, &v, sizeof(uint32_t)); 314 315 sc->issueing_cmd = CMD_WRITE; 316 sc->accepted_cmd = CMD_NONE; 317 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 318 sc->sc_hdev.sc_report_id, req, sc->sc_olen) != sc->sc_olen) 319 return; 320 321 /* wait till sensor data are updated, 250ms will be enough */ 322 tsleep(&sc->sc_sensortask, 0, "utwitch", (250*hz+999)/1000 + 1); 323 } 324