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