xref: /openbsd/sys/dev/usb/utrh.c (revision 81508fe3)
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
utrh_match(struct device * parent,void * match,void * aux)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
utrh_attach(struct device * parent,struct device * self,void * aux)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
utrh_detach(struct device * self,int flags)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
utrh_intr(struct uhidev * addr,void * ibuf,u_int len)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
utrh_refresh(void * arg)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
utrh_sht1x_temp(unsigned int nticks)246 utrh_sht1x_temp(unsigned int nticks)
247 {
248 	return (nticks - 4010);
249 }
250 
251 /* return %RH * 1000 */
252 int
utrh_sht1x_rh(unsigned int nticks,int temp)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