xref: /openbsd/sys/dev/usb/utrh.c (revision 3d8817e4)
1 /*	$OpenBSD: utrh.c,v 1.7 2011/01/25 20:03:36 jakemsr 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_ACTIVATE:
203 		break;
204 
205 	case DVACT_DEACTIVATE:
206 		sc->sc_dying = 1;
207 		break;
208 	}
209 	return (0);
210 }
211 
212 void
213 utrh_intr(struct uhidev *addr, void *ibuf, u_int len)
214 {
215 	struct utrh_softc *sc = (struct utrh_softc *)addr;
216 
217 	if (sc->sc_ibuf == NULL)
218 		return;
219 
220 	/* receive sensor data */
221 	memcpy(sc->sc_ibuf, ibuf, len);
222 	return;
223 }
224 
225 void
226 utrh_refresh(void *arg)
227 {
228 	struct utrh_softc *sc = arg;
229 	unsigned int temp_tick, humidity_tick;
230 	int temp, rh;
231 	uint8_t ledbuf[7];
232 
233 	/* turn on LED 1*/
234 	bzero(ledbuf, sizeof(ledbuf));
235 	ledbuf[0] = 0x3;
236 	ledbuf[1] = 0x1;
237 	if (uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
238 	    ledbuf, sc->sc_flen))
239 		printf("LED request failed\n");
240 
241 	/* issue query */
242 	uint8_t cmdbuf[] = {0x31, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00};
243 	if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT,
244 	    cmdbuf, sc->sc_olen))
245 		return;
246 
247 	/* wait till sensor data are updated, 1s will be enough */
248 	tsleep(&sc->sc_sensortask, 0, "utrh", (1*hz));
249 
250 	/* turn off LED 1 */
251 	ledbuf[1] = 0x0;
252 	if (uhidev_set_report(&sc->sc_hdev, UHID_FEATURE_REPORT,
253 	    ledbuf, sc->sc_flen))
254 		printf("LED request failed\n");
255 
256 	temp_tick = (sc->sc_ibuf[2] * 256 + sc->sc_ibuf[3]) & 0x3fff;
257 	humidity_tick = (sc->sc_ibuf[0] * 256 + sc->sc_ibuf[1]) & 0x0fff;
258 
259 	temp = utrh_sht1x_temp(temp_tick);
260 	rh = utrh_sht1x_rh(humidity_tick, temp);
261 
262 	sc->sc_sensor[UTRH_TEMP].value = (temp * 10000) + 273150000;
263 	sc->sc_sensor[UTRH_TEMP].flags &= ~SENSOR_FINVALID;
264 	sc->sc_sensor[UTRH_HUMIDITY].value = rh;
265 	sc->sc_sensor[UTRH_HUMIDITY].flags &= ~SENSOR_FINVALID;
266 }
267 
268 /* return C-degree * 100 value */
269 int
270 utrh_sht1x_temp(unsigned int ticks)
271 {
272 	return (ticks - 4010);
273 }
274 
275 /* return %RH * 1000 */
276 int
277 utrh_sht1x_rh(unsigned int ticks, int temp)
278 {
279 	int rh_l, rh;
280 
281 	rh_l = (-40000 + 405 * ticks) - ((7 * ticks * ticks) / 250);
282 	rh = ((temp - 2500) * (1 + (ticks >> 7)) + rh_l) / 10;
283 	return rh;
284 }
285