xref: /openbsd-src/sys/dev/usb/utwitch.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: utwitch.c,v 1.5 2011/07/03 15:47:17 matthew Exp $ */
2 
3 /*
4  * Copyright (c) 2010 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 Maywa-Denki & KAYAC YUREX BBU sensor */
20 /* this driver was previously known as uyurex(4). */
21 
22 #include <sys/param.h>
23 #include <sys/proc.h>
24 #include <sys/systm.h>
25 #include <sys/kernel.h>
26 #include <sys/malloc.h>
27 #include <sys/device.h>
28 #include <sys/conf.h>
29 #include <sys/sensors.h>
30 
31 #include <dev/usb/usb.h>
32 #include <dev/usb/usbhid.h>
33 #include <dev/usb/usbdi.h>
34 #include <dev/usb/usbdi_util.h>
35 #include <dev/usb/usbdevs.h>
36 #include <dev/usb/uhidev.h>
37 #include <dev/usb/hid.h>
38 
39 #ifdef USB_DEBUG
40 #define UYUREX_DEBUG
41 #endif
42 
43 #define	CMD_NONE	0xf0
44 #define CMD_EOF		0x0d
45 #define CMD_ACK		0x21
46 #define	CMD_MODE	0x41 /* XXX */
47 #define	CMD_VALUE	0x43
48 #define CMD_READ	0x52
49 #define CMD_WRITE	0x53
50 #define CMD_PADDING	0xff
51 
52 #define UPDATE_TICK	5 /* sec */
53 
54 #ifdef UYUREX_DEBUG
55 int	utwitchdebug = 0;
56 #define DPRINTFN(n, x)	do { if (utwitchdebug > (n)) printf x; } while (0)
57 #else
58 #define DPRINTFN(n, x)
59 #endif
60 
61 #define DPRINTF(x) DPRINTFN(0, x)
62 
63 struct utwitch_softc {
64 	struct uhidev		 sc_hdev;
65 	usbd_device_handle	 sc_udev;
66 	u_char			 sc_dying;
67 	uint16_t		 sc_flag;
68 
69 	/* uhidev parameters */
70 	size_t			 sc_flen;	/* feature report length */
71 	size_t			 sc_ilen;	/* input report length */
72 	size_t			 sc_olen;	/* output report length */
73 
74 	uint8_t			*sc_ibuf;
75 
76 	/* sensor framework */
77 	struct ksensor		 sc_sensor_val;
78 	struct ksensor		 sc_sensor_delta;
79 	struct ksensordev	 sc_sensordev;
80 	struct sensor_task	*sc_sensortask;
81 
82 	/* device private */
83 	int			 sc_initialized;
84 	uint8_t			 issueing_cmd;
85 	uint8_t			 accepted_cmd;
86 
87 	uint32_t		 sc_curval;
88 	uint32_t		 sc_oldval;
89 };
90 
91 const struct usb_devno utwitch_devs[] = {
92 	{ USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_YUREX},
93 };
94 
95 int utwitch_match(struct device *, void *, void *);
96 void utwitch_attach(struct device *, struct device *, void *);
97 int utwitch_detach(struct device *, int);
98 int utwitch_activate(struct device *, int);
99 
100 void utwitch_set_mode(struct utwitch_softc *, uint8_t);
101 void utwitch_read_value_request(struct utwitch_softc *);
102 void utwitch_write_value_request(struct utwitch_softc *, uint32_t);
103 
104 void utwitch_intr(struct uhidev *, void *, u_int);
105 void utwitch_refresh(void *);
106 
107 struct cfdriver utwitch_cd = {
108 	NULL, "utwitch", DV_DULL
109 };
110 
111 const struct cfattach utwitch_ca = {
112 	sizeof(struct utwitch_softc),
113 	utwitch_match,
114 	utwitch_attach,
115 	utwitch_detach,
116 	utwitch_activate,
117 };
118 
119 int
120 utwitch_match(struct device *parent, void *match, void *aux)
121 {
122 	struct usb_attach_arg *uaa = aux;
123 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
124 
125 	return (usb_lookup(utwitch_devs, uha->uaa->vendor, uha->uaa->product) != NULL ?
126 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
127 }
128 
129 void
130 utwitch_attach(struct device *parent, struct device *self, void *aux)
131 {
132 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
133 	struct usb_attach_arg *uaa = aux;
134 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
135 	usbd_device_handle dev = uha->parent->sc_udev;
136 	int size, repid, err;
137 	void *desc;
138 
139 	sc->sc_udev = dev;
140 	sc->sc_hdev.sc_intr = utwitch_intr;
141 	sc->sc_hdev.sc_parent = uha->parent;
142 	sc->sc_hdev.sc_report_id = uha->reportid;
143 
144 	uhidev_get_report_desc(uha->parent, &desc, &size);
145 	repid = uha->reportid;
146 	sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
147 	sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
148 	sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
149 
150 	err = uhidev_open(&sc->sc_hdev);
151 	if (err) {
152 		printf("utwitch_open: uhidev_open %d\n", err);
153 		return;
154 	}
155 	sc->sc_ibuf = malloc(sc->sc_ilen, M_USBDEV, M_WAITOK);
156 
157 	printf("\n");
158 
159 
160 	/* attach sensor */
161 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
162 	    sizeof(sc->sc_sensordev.xname));
163 
164 	/* add BBU sensor */
165 	sc->sc_sensor_val.type = SENSOR_INTEGER;
166 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_val);
167 	strlcpy(sc->sc_sensor_val.desc, "BBU",
168 		sizeof(sc->sc_sensor_val.desc));
169 
170 	/* add BBU delta sensor */
171 	sc->sc_sensor_delta.type = SENSOR_INTEGER;
172 	sensor_attach(&sc->sc_sensordev, &sc->sc_sensor_delta);
173 	strlcpy(sc->sc_sensor_delta.desc, "mBBU/sec",
174 		sizeof(sc->sc_sensor_delta.desc));
175 
176 	sc->sc_sensortask = sensor_task_register(sc, utwitch_refresh, UPDATE_TICK);
177 	if (sc->sc_sensortask == NULL) {
178 		printf(", unable to register update task\n");
179 		return;
180 	}
181 	sensordev_install(&sc->sc_sensordev);
182 
183 	DPRINTF(("utwitch_attach: complete\n"));
184 
185 	/* init device */ /* XXX */
186 	utwitch_set_mode(sc, 0);
187 }
188 
189 int
190 utwitch_detach(struct device *self, int flags)
191 {
192 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
193 	int rv = 0;
194 
195 	sc->sc_dying = 1;
196 
197 	wakeup(&sc->sc_sensortask);
198 	sensordev_deinstall(&sc->sc_sensordev);
199 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_val);
200 	sensor_detach(&sc->sc_sensordev, &sc->sc_sensor_delta);
201 	if (sc->sc_sensortask != NULL)
202 		sensor_task_unregister(sc->sc_sensortask);
203 
204 	if (sc->sc_ibuf != NULL) {
205 		free(sc->sc_ibuf, M_USBDEV);
206 		sc->sc_ibuf = NULL;
207 	}
208 
209 	return (rv);
210 }
211 
212 int
213 utwitch_activate(struct device *self, int act)
214 {
215 	struct utwitch_softc *sc = (struct utwitch_softc *)self;
216 
217 	switch (act) {
218 	case DVACT_DEACTIVATE:
219 		sc->sc_dying = 1;
220 		break;
221 	}
222 	return (0);
223 }
224 
225 void
226 utwitch_intr(struct uhidev *addr, void *ibuf, u_int len)
227 {
228 	struct utwitch_softc *sc = (struct utwitch_softc *)addr;
229 	uint8_t buf[8];
230 	uint32_t val;
231 
232 	if (sc->sc_ibuf == NULL)
233 		return;
234 
235 	/* process requests */
236 	memcpy(buf, ibuf, 8);
237 	DPRINTF(("intr: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
238 		buf[0], buf[1], buf[2], buf[3],
239 		buf[4], buf[5], buf[6], buf[7]));
240 
241 
242 	switch (buf[0]) {
243 	case CMD_ACK:
244 		if (buf[1] == sc->issueing_cmd) {
245 			DPRINTF(("ack received for cmd 0x%.2x\n", buf[1]));
246 			sc->accepted_cmd = buf[1];
247 		} else {
248 			DPRINTF(("cmd-ack mismatch: recved 0x%.2x, expect 0x%.2x\n",
249 				buf[1], sc->issueing_cmd));
250 			/* discard previous command */
251 			sc->accepted_cmd = CMD_NONE;
252 			sc->issueing_cmd = CMD_NONE;
253 		}
254 		break;
255 	case CMD_READ:
256 	case CMD_VALUE:
257 		val = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8)  + buf[5];
258 		if (!sc->sc_initialized) {
259 			sc->sc_oldval = val;
260 			sc->sc_initialized = 1;
261 		}
262 		sc->sc_sensor_val.value = val;
263 		sc->sc_curval = val;
264 		DPRINTF(("recv value update message: %d\n", val));
265 		break;
266 	default:
267 		DPRINTF(("unknown message: 0x%.2x\n", buf[0]));
268 	}
269 
270 	return;
271 }
272 
273 void
274 utwitch_refresh(void *arg)
275 {
276 	struct utwitch_softc *sc = arg;
277 
278 	if (!sc->sc_initialized) {
279 		utwitch_read_value_request(sc);
280 	} else {
281 		/* calculate delta value */
282 		sc->sc_sensor_delta.value =
283 			(1000 * (sc->sc_curval - sc->sc_oldval)) / UPDATE_TICK;
284 		sc->sc_oldval = sc->sc_curval;
285 	}
286 }
287 
288 void
289 utwitch_set_mode(struct utwitch_softc *sc, uint8_t val)
290 {
291 	uint8_t req[8];
292 	usbd_status err;
293 
294 	memset(req, CMD_PADDING, sizeof(req));
295 	req[0] = CMD_MODE;
296 	req[1] = val;
297 	req[2] = CMD_EOF;
298 	err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, req,
299 		sc->sc_olen);
300 	if (err) {
301 		printf("uhidev_set_report error:EIO\n");
302 		return;
303 	}
304 
305 	/* wait ack */
306 	tsleep(&sc->sc_sensortask, 0, "utwitch", (1000*hz+999)/1000 + 1);
307 }
308 
309 void
310 utwitch_read_value_request(struct utwitch_softc *sc)
311 {
312 	uint8_t req[8];
313 
314 	memset(req, CMD_PADDING, sizeof(req));
315 	req[0] = CMD_READ;
316 	req[1] = CMD_EOF;
317 	sc->issueing_cmd = CMD_READ;
318 	sc->accepted_cmd = CMD_NONE;
319 	if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, req,
320 		sc->sc_olen))
321 		return;
322 
323 	/* wait till sensor data are updated, 500ms will be enough */
324 	tsleep(&sc->sc_sensortask, 0, "utwitch", (500*hz+999)/1000 + 1);
325 }
326 
327 void
328 utwitch_write_value_request(struct utwitch_softc *sc, uint32_t val)
329 {
330 	uint32_t v;
331 	uint8_t req[8];
332 
333 	req[0] = CMD_WRITE;
334 	req[1] = 0;
335 	req[6] = CMD_EOF;
336 	req[7] = CMD_PADDING;
337 	v = htobe32(val);
338 	memcpy(req + 2, &v, sizeof(uint32_t));
339 
340 	sc->issueing_cmd = CMD_WRITE;
341 	sc->accepted_cmd = CMD_NONE;
342 	if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, req,
343 		sc->sc_olen))
344 		return;
345 
346 	/* wait till sensor data are updated, 250ms will be enough */
347 	tsleep(&sc->sc_sensortask, 0, "utwitch", (250*hz+999)/1000 + 1);
348 }
349