1 /* $OpenBSD: ugold.c,v 1.6 2014/04/29 12:53:33 mpi Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Takayoshi SASANO <sasano@openbsd.org> 5 * Copyright (c) 2013 Martin Pieuchot <mpi@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Driver for Microdia's HID base TEMPer Temperature sensor */ 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/sensors.h> 28 29 #include <dev/usb/usb.h> 30 #include <dev/usb/usbhid.h> 31 32 #include <dev/usb/usbdi.h> 33 #include <dev/usb/usbdevs.h> 34 #include <dev/usb/uhidev.h> 35 #include <dev/usb/hid.h> 36 37 #define UGOLD_INNER 0 38 #define UGOLD_OUTER 1 39 #define UGOLD_MAX_SENSORS 2 40 41 #define UGOLD_CMD_DATA 0x80 42 #define UGOLD_CMD_INIT 0x82 43 44 /* 45 * This driver only uses two of the three known commands for the 46 * TEMPerV1.2 device. 47 * 48 * The first byte of the answer corresponds to the command and the 49 * second one seems to be the size (in bytes) of the answer. 50 * 51 * The device always sends 8 bytes and if the length of the answer 52 * is less than that, it just leaves the last bytes untouched. That 53 * is why most of the time the last n bytes of the answers are the 54 * same. 55 * 56 * The third command below seems to generate two answers with a 57 * string corresponding to the device, for example: 58 * 'TEMPer1F' and '1.1Per1F' (here Per1F is repeated). 59 */ 60 static uint8_t cmd_data[8] = { 0x01, 0x80, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00 }; 61 static uint8_t cmd_init[8] = { 0x01, 0x82, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00 }; 62 #if 0 63 static uint8_t cmd_type[8] = { 0x01, 0x86, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00 }; 64 #endif 65 66 struct ugold_softc { 67 struct uhidev sc_hdev; 68 struct usbd_device *sc_udev; 69 70 int sc_num_sensors; 71 72 struct ksensor sc_sensor[UGOLD_MAX_SENSORS]; 73 struct ksensordev sc_sensordev; 74 struct sensor_task *sc_sensortask; 75 }; 76 77 const struct usb_devno ugold_devs[] = { 78 { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_TEMPER }, 79 }; 80 81 int ugold_match(struct device *, void *, void *); 82 void ugold_attach(struct device *, struct device *, void *); 83 int ugold_detach(struct device *, int); 84 85 void ugold_intr(struct uhidev *, void *, u_int); 86 void ugold_refresh(void *); 87 88 int ugold_issue_cmd(struct ugold_softc *, uint8_t *, int); 89 90 struct cfdriver ugold_cd = { 91 NULL, "ugold", DV_DULL 92 }; 93 94 const struct cfattach ugold_ca = { 95 sizeof(struct ugold_softc), ugold_match, ugold_attach, ugold_detach, 96 }; 97 98 int 99 ugold_match(struct device *parent, void *match, void *aux) 100 { 101 struct uhidev_attach_arg *uha = aux; 102 int size; 103 void *desc; 104 105 if (uha->reportid == UHIDEV_CLAIM_ALLREPORTID) 106 return (UMATCH_NONE); 107 108 if (usb_lookup(ugold_devs, uha->uaa->vendor, uha->uaa->product) == NULL) 109 return (UMATCH_NONE); 110 111 /* 112 * XXX Only match the sensor interface. 113 * 114 * Does it makes sense to attach various uhidev(4) to these 115 * non-standard HID devices? 116 */ 117 uhidev_get_report_desc(uha->parent, &desc, &size); 118 if (hid_is_collection(desc, size, uha->reportid, 119 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 120 return (UMATCH_NONE); 121 122 return (UMATCH_VENDOR_PRODUCT); 123 124 } 125 126 void 127 ugold_attach(struct device *parent, struct device *self, void *aux) 128 { 129 struct ugold_softc *sc = (struct ugold_softc *)self; 130 struct uhidev_attach_arg *uha = aux; 131 int size, repid; 132 void *desc; 133 134 sc->sc_udev = uha->parent->sc_udev; 135 sc->sc_hdev.sc_intr = ugold_intr; 136 sc->sc_hdev.sc_parent = uha->parent; 137 sc->sc_hdev.sc_report_id = uha->reportid; 138 139 uhidev_get_report_desc(uha->parent, &desc, &size); 140 repid = uha->reportid; 141 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 142 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 143 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 144 145 if (uhidev_open(&sc->sc_hdev)) { 146 printf(", unable to open interrupt pipe\n"); 147 return; 148 } 149 150 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 151 sizeof(sc->sc_sensordev.xname)); 152 153 sc->sc_sensor[UGOLD_OUTER].type = SENSOR_TEMP; 154 strlcpy(sc->sc_sensor[UGOLD_OUTER].desc, "outer", 155 sizeof(sc->sc_sensor[UGOLD_OUTER].desc)); 156 157 sc->sc_sensor[UGOLD_INNER].type = SENSOR_TEMP; 158 strlcpy(sc->sc_sensor[UGOLD_INNER].desc, "inner", 159 sizeof(sc->sc_sensor[UGOLD_INNER].desc)); 160 161 /* 0.1Hz */ 162 sc->sc_sensortask = sensor_task_register(sc, ugold_refresh, 6); 163 if (sc->sc_sensortask == NULL) { 164 printf(", unable to register update task\n"); 165 return; 166 } 167 printf("\n"); 168 169 sensordev_install(&sc->sc_sensordev); 170 } 171 172 int 173 ugold_detach(struct device *self, int flags) 174 { 175 struct ugold_softc *sc = (struct ugold_softc *)self; 176 int i; 177 178 if (sc->sc_sensortask != NULL) { 179 sensor_task_unregister(sc->sc_sensortask); 180 sensordev_deinstall(&sc->sc_sensordev); 181 } 182 183 for (i = 0; i < sc->sc_num_sensors; i++) 184 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]); 185 186 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 187 uhidev_close(&sc->sc_hdev); 188 189 return (0); 190 } 191 192 static int 193 ugold_ds75_temp(uint8_t msb, uint8_t lsb) 194 { 195 /* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */ 196 return (((msb * 100) + ((lsb >> 4) * 25 / 4)) * 10000) + 273150000; 197 } 198 199 void 200 ugold_intr(struct uhidev *addr, void *ibuf, u_int len) 201 { 202 struct ugold_softc *sc = (struct ugold_softc *)addr; 203 uint8_t *buf = ibuf; 204 int i, temp; 205 206 switch (buf[0]) { 207 case UGOLD_CMD_INIT: 208 if (sc->sc_num_sensors) 209 break; 210 211 sc->sc_num_sensors = min(buf[1], UGOLD_MAX_SENSORS) /* XXX */; 212 213 for (i = 0; i < sc->sc_num_sensors; i++) { 214 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 215 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 216 } 217 218 printf("%s: %d sensor%s type ds75/12bit (temperature)\n", 219 sc->sc_hdev.sc_dev.dv_xname, sc->sc_num_sensors, 220 (sc->sc_num_sensors == 1) ? "" : "s"); 221 break; 222 case UGOLD_CMD_DATA: 223 switch (buf[1]) { 224 case 4: 225 temp = ugold_ds75_temp(buf[4], buf[5]); 226 sc->sc_sensor[UGOLD_OUTER].value = temp; 227 sc->sc_sensor[UGOLD_OUTER].flags &= ~SENSOR_FINVALID; 228 /* FALLTHROUGH */ 229 case 2: 230 temp = ugold_ds75_temp(buf[2], buf[3]); 231 sc->sc_sensor[UGOLD_INNER].value = temp; 232 sc->sc_sensor[UGOLD_INNER].flags &= ~SENSOR_FINVALID; 233 break; 234 default: 235 printf("%s: invalid data length (%d bytes)\n", 236 sc->sc_hdev.sc_dev.dv_xname, buf[1]); 237 } 238 break; 239 default: 240 printf("%s: unknown command 0x%02x\n", 241 sc->sc_hdev.sc_dev.dv_xname, buf[0]); 242 } 243 } 244 245 void 246 ugold_refresh(void *arg) 247 { 248 struct ugold_softc *sc = arg; 249 int i; 250 251 if (sc->sc_num_sensors == 0) 252 ugold_issue_cmd(sc, cmd_init, sizeof(cmd_init)); 253 254 if (ugold_issue_cmd(sc, cmd_data, sizeof(cmd_data))) { 255 for (i = 0; i < sc->sc_num_sensors; i++) 256 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 257 } 258 } 259 260 int 261 ugold_issue_cmd(struct ugold_softc *sc, uint8_t *cmd, int len) 262 { 263 return uhidev_set_report_async(&sc->sc_hdev, UHID_OUTPUT_REPORT, 264 sc->sc_hdev.sc_report_id, cmd, len); 265 } 266