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