xref: /openbsd-src/sys/dev/usb/utrh.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: utrh.c,v 1.27 2024/05/23 03:21:09 jsg Exp $   */
2c9388e18Syuo 
3c9388e18Syuo /*
4c9388e18Syuo  * Copyright (c) 2009 Yojiro UO <yuo@nui.org>
5c9388e18Syuo  *
6c9388e18Syuo  * Permission to use, copy, modify, and distribute this software for any
7c9388e18Syuo  * purpose with or without fee is hereby granted, provided that the above
8c9388e18Syuo  * copyright notice and this permission notice appear in all copies.
9c9388e18Syuo  *
10c9388e18Syuo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11c9388e18Syuo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12c9388e18Syuo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13c9388e18Syuo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14c9388e18Syuo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15c9388e18Syuo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16c9388e18Syuo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17c9388e18Syuo  */
18c9388e18Syuo 
194b1a56afSjsg /* Driver for Strawberry linux USBRH Temperature/Humidity sensor */
20c9388e18Syuo 
21c9388e18Syuo #include <sys/param.h>
22c9388e18Syuo #include <sys/systm.h>
23c9388e18Syuo #include <sys/malloc.h>
24c9388e18Syuo #include <sys/device.h>
25c9388e18Syuo #include <sys/sensors.h>
26c9388e18Syuo 
27c9388e18Syuo #include <dev/usb/usb.h>
28c9388e18Syuo #include <dev/usb/usbhid.h>
29c9388e18Syuo #include <dev/usb/usbdi.h>
30c9388e18Syuo #include <dev/usb/usbdevs.h>
31c9388e18Syuo #include <dev/usb/uhidev.h>
328a83145eSjcs 
33c9388e18Syuo #ifdef UTRH_DEBUG
349117c2b1Ssthen #define DPRINTF(x)	do { printf x; } while (0)
35c9388e18Syuo #else
369117c2b1Ssthen #define DPRINTF(x)
37c9388e18Syuo #endif
38c9388e18Syuo 
39c9388e18Syuo /* sensors */
40c9388e18Syuo #define UTRH_TEMP		0
41c9388e18Syuo #define UTRH_HUMIDITY		1
42c9388e18Syuo #define UTRH_MAX_SENSORS	2
43c9388e18Syuo 
44c9388e18Syuo struct utrh_softc {
45c9388e18Syuo 	struct uhidev		 sc_hdev;
46ab0b1be7Smglocker 	struct usbd_device	*sc_udev;
47c9388e18Syuo 
48c9388e18Syuo 	/* uhidev parameters */
49c9388e18Syuo 	size_t			 sc_flen;	/* feature report length */
50c9388e18Syuo 	size_t			 sc_ilen;	/* input report length */
51c9388e18Syuo 	size_t			 sc_olen;	/* output report length */
52c9388e18Syuo 
53c9388e18Syuo 	uint8_t			*sc_ibuf;
54c9388e18Syuo 
55c9388e18Syuo 	/* sensor framework */
56c9388e18Syuo 	struct ksensor		 sc_sensor[UTRH_MAX_SENSORS];
57c9388e18Syuo 	struct ksensordev	 sc_sensordev;
58c9388e18Syuo 	struct sensor_task	*sc_sensortask;
59c9388e18Syuo 
60c9388e18Syuo 	uint8_t			 sc_num_sensors;
61c9388e18Syuo };
62c9388e18Syuo 
63c9388e18Syuo const struct usb_devno utrh_devs[] = {
64c9388e18Syuo 	{ USB_VENDOR_STRAWBERRYLINUX, USB_PRODUCT_STRAWBERRYLINUX_USBRH},
65c9388e18Syuo };
66c9388e18Syuo 
67c9388e18Syuo int utrh_match(struct device *, void *, void *);
68c9388e18Syuo void utrh_attach(struct device *, struct device *, void *);
69c9388e18Syuo int utrh_detach(struct device *, int);
70c9388e18Syuo 
71c9388e18Syuo int utrh_sht1x_temp(unsigned int);
72c9388e18Syuo int utrh_sht1x_rh(unsigned int, int);
73c9388e18Syuo 
74c9388e18Syuo void utrh_intr(struct uhidev *, void *, u_int);
75c9388e18Syuo void utrh_refresh(void *);
76c9388e18Syuo 
77c9388e18Syuo struct cfdriver utrh_cd = {
78c9388e18Syuo 	NULL, "utrh", DV_DULL
79c9388e18Syuo };
80c9388e18Syuo 
81c9388e18Syuo const struct cfattach utrh_ca = {
82c9388e18Syuo 	sizeof(struct utrh_softc),
83c9388e18Syuo 	utrh_match,
84c9388e18Syuo 	utrh_attach,
859117c2b1Ssthen 	utrh_detach
86c9388e18Syuo };
87c9388e18Syuo 
88c9388e18Syuo int
utrh_match(struct device * parent,void * match,void * aux)89c9388e18Syuo utrh_match(struct device *parent, void *match, void *aux)
90c9388e18Syuo {
91f964a65cSmpi 	struct uhidev_attach_arg *uha = aux;
92f964a65cSmpi 
93faac88c0Santon 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
94f964a65cSmpi 		return (UMATCH_NONE);
95c9388e18Syuo 
96230ab8cdSjasper 	return (usb_lookup(utrh_devs, uha->uaa->vendor, uha->uaa->product) != NULL ?
97230ab8cdSjasper 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
98c9388e18Syuo }
99c9388e18Syuo 
100c9388e18Syuo void
utrh_attach(struct device * parent,struct device * self,void * aux)101c9388e18Syuo utrh_attach(struct device *parent, struct device *self, void *aux)
102c9388e18Syuo {
103c9388e18Syuo 	struct utrh_softc *sc = (struct utrh_softc *)self;
104c9388e18Syuo 	struct usb_attach_arg *uaa = aux;
105c9388e18Syuo 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
106ab0b1be7Smglocker 	struct usbd_device *dev = uha->parent->sc_udev;
107c9388e18Syuo 	int size, repid, err;
108c9388e18Syuo 	void *desc;
109c9388e18Syuo 
110c9388e18Syuo 	sc->sc_udev = dev;
111c9388e18Syuo 	sc->sc_hdev.sc_intr = utrh_intr;
112c9388e18Syuo 	sc->sc_hdev.sc_parent = uha->parent;
113c9388e18Syuo 	sc->sc_hdev.sc_report_id = uha->reportid;
114c9388e18Syuo 	sc->sc_num_sensors = 0;
115c9388e18Syuo 
116c9388e18Syuo 	uhidev_get_report_desc(uha->parent, &desc, &size);
117c9388e18Syuo 	repid = uha->reportid;
118c9388e18Syuo 	sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
119c9388e18Syuo 	sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
120c9388e18Syuo 	sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
121c9388e18Syuo 
122c9388e18Syuo 	err = uhidev_open(&sc->sc_hdev);
123c9388e18Syuo 	if (err) {
124c9388e18Syuo 		printf("utrh_open: uhidev_open %d\n", err);
125c9388e18Syuo 		return;
126c9388e18Syuo 	}
127c9388e18Syuo 	sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK);
128c9388e18Syuo 
129c9388e18Syuo 	printf("\n");
130c9388e18Syuo 
131c9388e18Syuo 	/* attach sensor */
132c9388e18Syuo 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
133c9388e18Syuo 	    sizeof(sc->sc_sensordev.xname));
134c9388e18Syuo 
135c9388e18Syuo 	sc->sc_sensor[UTRH_TEMP].type = SENSOR_TEMP;
136c9388e18Syuo 	sc->sc_sensor[UTRH_TEMP].flags = SENSOR_FINVALID;
137c9388e18Syuo 
13851c0063cSyuo 	strlcpy(sc->sc_sensor[UTRH_HUMIDITY].desc, "RH",
139c9388e18Syuo 	    sizeof(sc->sc_sensor[UTRH_HUMIDITY].desc));
14051c0063cSyuo 	sc->sc_sensor[UTRH_HUMIDITY].type = SENSOR_HUMIDITY;
141c9388e18Syuo 	sc->sc_sensor[UTRH_HUMIDITY].flags = SENSOR_FINVALID;
142c9388e18Syuo 
143c9388e18Syuo 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[UTRH_TEMP]);
144c9388e18Syuo 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[UTRH_HUMIDITY]);
145c9388e18Syuo 	sc->sc_num_sensors = 2;
146c9388e18Syuo 
147c9388e18Syuo 	if (sc->sc_num_sensors > 0) {
148c9388e18Syuo 		sc->sc_sensortask = sensor_task_register(sc, utrh_refresh, 6);
149c9388e18Syuo 		if (sc->sc_sensortask == NULL) {
150c9388e18Syuo 			printf(", unable to register update task\n");
151c9388e18Syuo 			return;
152c9388e18Syuo 		}
153c9388e18Syuo 		sensordev_install(&sc->sc_sensordev);
154c9388e18Syuo 	}
155c9388e18Syuo 
156c9388e18Syuo 	DPRINTF(("utrh_attach: complete\n"));
157c9388e18Syuo }
158c9388e18Syuo 
159c9388e18Syuo int
utrh_detach(struct device * self,int flags)160c9388e18Syuo utrh_detach(struct device *self, int flags)
161c9388e18Syuo {
162c9388e18Syuo 	struct utrh_softc *sc = (struct utrh_softc *)self;
163c9388e18Syuo 	int i, rv = 0;
164c9388e18Syuo 
165c9388e18Syuo 	if (sc->sc_num_sensors > 0) {
166c9388e18Syuo 		wakeup(&sc->sc_sensortask);
167c9388e18Syuo 		sensordev_deinstall(&sc->sc_sensordev);
168c9388e18Syuo 		for (i = 0; i < sc->sc_num_sensors; i++)
169c9388e18Syuo 			sensor_detach(&sc->sc_sensordev, &sc->sc_sensor[i]);
170c9388e18Syuo 		if (sc->sc_sensortask != NULL)
171c9388e18Syuo 			sensor_task_unregister(sc->sc_sensortask);
172c9388e18Syuo 	}
173c9388e18Syuo 
1749dcdfc61Smpi 	if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
1759dcdfc61Smpi 		uhidev_close(&sc->sc_hdev);
1769dcdfc61Smpi 
177c9388e18Syuo 	if (sc->sc_ibuf != NULL) {
178234dfda1Sderaadt 		free(sc->sc_ibuf, M_USBDEV, sc->sc_ilen);
179c9388e18Syuo 		sc->sc_ibuf = NULL;
180c9388e18Syuo 	}
181c9388e18Syuo 
182c9388e18Syuo 	return (rv);
183c9388e18Syuo }
184c9388e18Syuo 
185c9388e18Syuo void
utrh_intr(struct uhidev * addr,void * ibuf,u_int len)186c9388e18Syuo utrh_intr(struct uhidev *addr, void *ibuf, u_int len)
187c9388e18Syuo {
188c9388e18Syuo 	struct utrh_softc *sc = (struct utrh_softc *)addr;
189c9388e18Syuo 
190c9388e18Syuo 	if (sc->sc_ibuf == NULL)
191c9388e18Syuo 		return;
192c9388e18Syuo 
193c9388e18Syuo 	/* receive sensor data */
194c9388e18Syuo 	memcpy(sc->sc_ibuf, ibuf, len);
195c9388e18Syuo 	return;
196c9388e18Syuo }
197c9388e18Syuo 
198c9388e18Syuo void
utrh_refresh(void * arg)199c9388e18Syuo utrh_refresh(void *arg)
200c9388e18Syuo {
201c9388e18Syuo 	struct utrh_softc *sc = arg;
202c9388e18Syuo 	unsigned int temp_tick, humidity_tick;
203cc1678f7Smpi 	int temp, rh, flen, olen;
20451c0063cSyuo 	uint8_t ledbuf[7];
20551c0063cSyuo 
206cc1678f7Smpi 	flen = MIN(sc->sc_flen, sizeof(ledbuf));
207cc1678f7Smpi 
20851c0063cSyuo 	/* turn on LED 1*/
20951c0063cSyuo 	bzero(ledbuf, sizeof(ledbuf));
21051c0063cSyuo 	ledbuf[0] = 0x3;
21151c0063cSyuo 	ledbuf[1] = 0x1;
212e6a02383Smpi 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
213cc1678f7Smpi 	    sc->sc_hdev.sc_report_id, ledbuf, flen) != flen)
21451c0063cSyuo 		printf("LED request failed\n");
215c9388e18Syuo 
216c9388e18Syuo 	/* issue query */
217c9388e18Syuo 	uint8_t cmdbuf[] = {0x31, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00};
218cc1678f7Smpi 	olen = MIN(sc->sc_olen, sizeof(cmdbuf));
219e6a02383Smpi 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
220cc1678f7Smpi 	    sc->sc_hdev.sc_report_id, cmdbuf, olen) != olen)
221c9388e18Syuo 		return;
222c9388e18Syuo 
223b001f793Syuo 	/* wait till sensor data are updated, 1s will be enough */
22400f6cb32Smpi 	tsleep_nsec(&sc->sc_sensortask, 0, "utrh", SEC_TO_NSEC(1));
225c9388e18Syuo 
22651c0063cSyuo 	/* turn off LED 1 */
22751c0063cSyuo 	ledbuf[1] = 0x0;
228e6a02383Smpi 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
229cc1678f7Smpi 	    sc->sc_hdev.sc_report_id, ledbuf, flen) != flen)
23051c0063cSyuo 		printf("LED request failed\n");
23151c0063cSyuo 
232c9388e18Syuo 	temp_tick = (sc->sc_ibuf[2] * 256 + sc->sc_ibuf[3]) & 0x3fff;
233c9388e18Syuo 	humidity_tick = (sc->sc_ibuf[0] * 256 + sc->sc_ibuf[1]) & 0x0fff;
234c9388e18Syuo 
235c9388e18Syuo 	temp = utrh_sht1x_temp(temp_tick);
236c9388e18Syuo 	rh = utrh_sht1x_rh(humidity_tick, temp);
237c9388e18Syuo 
238c9388e18Syuo 	sc->sc_sensor[UTRH_TEMP].value = (temp * 10000) + 273150000;
239c9388e18Syuo 	sc->sc_sensor[UTRH_TEMP].flags &= ~SENSOR_FINVALID;
240c9388e18Syuo 	sc->sc_sensor[UTRH_HUMIDITY].value = rh;
241c9388e18Syuo 	sc->sc_sensor[UTRH_HUMIDITY].flags &= ~SENSOR_FINVALID;
242c9388e18Syuo }
243c9388e18Syuo 
244c9388e18Syuo /* return C-degree * 100 value */
245c9388e18Syuo int
utrh_sht1x_temp(unsigned int nticks)246446594ceSmpi utrh_sht1x_temp(unsigned int nticks)
247c9388e18Syuo {
248446594ceSmpi 	return (nticks - 4010);
249c9388e18Syuo }
250c9388e18Syuo 
251c9388e18Syuo /* return %RH * 1000 */
252c9388e18Syuo int
utrh_sht1x_rh(unsigned int nticks,int temp)253446594ceSmpi utrh_sht1x_rh(unsigned int nticks, int temp)
254c9388e18Syuo {
255c9388e18Syuo 	int rh_l, rh;
256c9388e18Syuo 
257446594ceSmpi 	rh_l = (-40000 + 405 * nticks) - ((7 * nticks * nticks) / 250);
258446594ceSmpi 	rh = ((temp - 2500) * (1 + (nticks >> 7)) + rh_l) / 10;
259c9388e18Syuo 	return rh;
260c9388e18Syuo }
261