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