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