1 /* $OpenBSD: utrh.c,v 1.27 2024/05/23 03:21:09 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2009 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 Strawberry linux USBRH Temperature/Humidity sensor */ 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/malloc.h> 24 #include <sys/device.h> 25 #include <sys/sensors.h> 26 27 #include <dev/usb/usb.h> 28 #include <dev/usb/usbhid.h> 29 #include <dev/usb/usbdi.h> 30 #include <dev/usb/usbdevs.h> 31 #include <dev/usb/uhidev.h> 32 33 #ifdef UTRH_DEBUG 34 #define DPRINTF(x) do { printf x; } while (0) 35 #else 36 #define DPRINTF(x) 37 #endif 38 39 /* sensors */ 40 #define UTRH_TEMP 0 41 #define UTRH_HUMIDITY 1 42 #define UTRH_MAX_SENSORS 2 43 44 struct utrh_softc { 45 struct uhidev sc_hdev; 46 struct usbd_device *sc_udev; 47 48 /* uhidev parameters */ 49 size_t sc_flen; /* feature report length */ 50 size_t sc_ilen; /* input report length */ 51 size_t sc_olen; /* output report length */ 52 53 uint8_t *sc_ibuf; 54 55 /* sensor framework */ 56 struct ksensor sc_sensor[UTRH_MAX_SENSORS]; 57 struct ksensordev sc_sensordev; 58 struct sensor_task *sc_sensortask; 59 60 uint8_t sc_num_sensors; 61 }; 62 63 const struct usb_devno utrh_devs[] = { 64 { USB_VENDOR_STRAWBERRYLINUX, USB_PRODUCT_STRAWBERRYLINUX_USBRH}, 65 }; 66 67 int utrh_match(struct device *, void *, void *); 68 void utrh_attach(struct device *, struct device *, void *); 69 int utrh_detach(struct device *, int); 70 71 int utrh_sht1x_temp(unsigned int); 72 int utrh_sht1x_rh(unsigned int, int); 73 74 void utrh_intr(struct uhidev *, void *, u_int); 75 void utrh_refresh(void *); 76 77 struct cfdriver utrh_cd = { 78 NULL, "utrh", DV_DULL 79 }; 80 81 const struct cfattach utrh_ca = { 82 sizeof(struct utrh_softc), 83 utrh_match, 84 utrh_attach, 85 utrh_detach 86 }; 87 88 int 89 utrh_match(struct device *parent, void *match, void *aux) 90 { 91 struct uhidev_attach_arg *uha = aux; 92 93 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 94 return (UMATCH_NONE); 95 96 return (usb_lookup(utrh_devs, uha->uaa->vendor, uha->uaa->product) != NULL ? 97 UMATCH_VENDOR_PRODUCT : UMATCH_NONE); 98 } 99 100 void 101 utrh_attach(struct device *parent, struct device *self, void *aux) 102 { 103 struct utrh_softc *sc = (struct utrh_softc *)self; 104 struct usb_attach_arg *uaa = aux; 105 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 106 struct usbd_device *dev = uha->parent->sc_udev; 107 int size, repid, err; 108 void *desc; 109 110 sc->sc_udev = dev; 111 sc->sc_hdev.sc_intr = utrh_intr; 112 sc->sc_hdev.sc_parent = uha->parent; 113 sc->sc_hdev.sc_report_id = uha->reportid; 114 sc->sc_num_sensors = 0; 115 116 uhidev_get_report_desc(uha->parent, &desc, &size); 117 repid = uha->reportid; 118 sc->sc_ilen = hid_report_size(desc, size, hid_input, repid); 119 sc->sc_olen = hid_report_size(desc, size, hid_output, repid); 120 sc->sc_flen = hid_report_size(desc, size, hid_feature, repid); 121 122 err = uhidev_open(&sc->sc_hdev); 123 if (err) { 124 printf("utrh_open: uhidev_open %d\n", err); 125 return; 126 } 127 sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK); 128 129 printf("\n"); 130 131 /* attach sensor */ 132 strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname, 133 sizeof(sc->sc_sensordev.xname)); 134 135 sc->sc_sensor[UTRH_TEMP].type = SENSOR_TEMP; 136 sc->sc_sensor[UTRH_TEMP].flags = SENSOR_FINVALID; 137 138 strlcpy(sc->sc_sensor[UTRH_HUMIDITY].desc, "RH", 139 sizeof(sc->sc_sensor[UTRH_HUMIDITY].desc)); 140 sc->sc_sensor[UTRH_HUMIDITY].type = SENSOR_HUMIDITY; 141 sc->sc_sensor[UTRH_HUMIDITY].flags = SENSOR_FINVALID; 142 143 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[UTRH_TEMP]); 144 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[UTRH_HUMIDITY]); 145 sc->sc_num_sensors = 2; 146 147 if (sc->sc_num_sensors > 0) { 148 sc->sc_sensortask = sensor_task_register(sc, utrh_refresh, 6); 149 if (sc->sc_sensortask == NULL) { 150 printf(", unable to register update task\n"); 151 return; 152 } 153 sensordev_install(&sc->sc_sensordev); 154 } 155 156 DPRINTF(("utrh_attach: complete\n")); 157 } 158 159 int 160 utrh_detach(struct device *self, int flags) 161 { 162 struct utrh_softc *sc = (struct utrh_softc *)self; 163 int i, rv = 0; 164 165 if (sc->sc_num_sensors > 0) { 166 wakeup(&sc->sc_sensortask); 167 sensordev_deinstall(&sc->sc_sensordev); 168 for (i = 0; i < sc->sc_num_sensors; i++) 169 sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]); 170 if (sc->sc_sensortask != NULL) 171 sensor_task_unregister(sc->sc_sensortask); 172 } 173 174 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 175 uhidev_close(&sc->sc_hdev); 176 177 if (sc->sc_ibuf != NULL) { 178 free(sc->sc_ibuf, M_USBDEV, sc->sc_ilen); 179 sc->sc_ibuf = NULL; 180 } 181 182 return (rv); 183 } 184 185 void 186 utrh_intr(struct uhidev *addr, void *ibuf, u_int len) 187 { 188 struct utrh_softc *sc = (struct utrh_softc *)addr; 189 190 if (sc->sc_ibuf == NULL) 191 return; 192 193 /* receive sensor data */ 194 memcpy(sc->sc_ibuf, ibuf, len); 195 return; 196 } 197 198 void 199 utrh_refresh(void *arg) 200 { 201 struct utrh_softc *sc = arg; 202 unsigned int temp_tick, humidity_tick; 203 int temp, rh, flen, olen; 204 uint8_t ledbuf[7]; 205 206 flen = MIN(sc->sc_flen, sizeof(ledbuf)); 207 208 /* turn on LED 1*/ 209 bzero(ledbuf, sizeof(ledbuf)); 210 ledbuf[0] = 0x3; 211 ledbuf[1] = 0x1; 212 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT, 213 sc->sc_hdev.sc_report_id, ledbuf, flen) != flen) 214 printf("LED request failed\n"); 215 216 /* issue query */ 217 uint8_t cmdbuf[] = {0x31, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00}; 218 olen = MIN(sc->sc_olen, sizeof(cmdbuf)); 219 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT, 220 sc->sc_hdev.sc_report_id, cmdbuf, olen) != olen) 221 return; 222 223 /* wait till sensor data are updated, 1s will be enough */ 224 tsleep_nsec(&sc->sc_sensortask, 0, "utrh", SEC_TO_NSEC(1)); 225 226 /* turn off LED 1 */ 227 ledbuf[1] = 0x0; 228 if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT, 229 sc->sc_hdev.sc_report_id, ledbuf, flen) != flen) 230 printf("LED request failed\n"); 231 232 temp_tick = (sc->sc_ibuf[2] * 256 + sc->sc_ibuf[3]) & 0x3fff; 233 humidity_tick = (sc->sc_ibuf[0] * 256 + sc->sc_ibuf[1]) & 0x0fff; 234 235 temp = utrh_sht1x_temp(temp_tick); 236 rh = utrh_sht1x_rh(humidity_tick, temp); 237 238 sc->sc_sensor[UTRH_TEMP].value = (temp * 10000) + 273150000; 239 sc->sc_sensor[UTRH_TEMP].flags &= ~SENSOR_FINVALID; 240 sc->sc_sensor[UTRH_HUMIDITY].value = rh; 241 sc->sc_sensor[UTRH_HUMIDITY].flags &= ~SENSOR_FINVALID; 242 } 243 244 /* return C-degree * 100 value */ 245 int 246 utrh_sht1x_temp(unsigned int nticks) 247 { 248 return (nticks - 4010); 249 } 250 251 /* return %RH * 1000 */ 252 int 253 utrh_sht1x_rh(unsigned int nticks, int temp) 254 { 255 int rh_l, rh; 256 257 rh_l = (-40000 + 405 * nticks) - ((7 * nticks * nticks) / 250); 258 rh = ((temp - 2500) * (1 + (nticks >> 7)) + rh_l) / 10; 259 return rh; 260 } 261