xref: /openbsd-src/sys/dev/usb/utwitch.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: utwitch.c,v 1.10 2014/03/19 08:59:37 mpi 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_ibuf != NULL) {
193 		free(sc->sc_ibuf, M_USBDEV);
194 		sc->sc_ibuf = NULL;
195 	}
196 
197 	return (rv);
198 }
199 
200 void
201 utwitch_intr(struct uhidev *addr, void *ibuf, u_int len)
202 {
203 	struct utwitch_softc *sc = (struct utwitch_softc *)addr;
204 	uint8_t buf[8];
205 	uint32_t val;
206 
207 	if (sc->sc_ibuf == NULL)
208 		return;
209 
210 	/* process requests */
211 	memcpy(buf, ibuf, 8);
212 	DPRINTF(("intr: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
213 		buf[0], buf[1], buf[2], buf[3],
214 		buf[4], buf[5], buf[6], buf[7]));
215 
216 
217 	switch (buf[0]) {
218 	case CMD_ACK:
219 		if (buf[1] == sc->issueing_cmd) {
220 			DPRINTF(("ack received for cmd 0x%.2x\n", buf[1]));
221 			sc->accepted_cmd = buf[1];
222 		} else {
223 			DPRINTF(("cmd-ack mismatch: recved 0x%.2x, expect 0x%.2x\n",
224 				buf[1], sc->issueing_cmd));
225 			/* discard previous command */
226 			sc->accepted_cmd = CMD_NONE;
227 			sc->issueing_cmd = CMD_NONE;
228 		}
229 		break;
230 	case CMD_READ:
231 	case CMD_VALUE:
232 		val = (buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8)  + buf[5];
233 		if (!sc->sc_initialized) {
234 			sc->sc_oldval = val;
235 			sc->sc_initialized = 1;
236 		}
237 		sc->sc_sensor_val.value = val;
238 		sc->sc_curval = val;
239 		DPRINTF(("recv value update message: %d\n", val));
240 		break;
241 	default:
242 		DPRINTF(("unknown message: 0x%.2x\n", buf[0]));
243 	}
244 
245 	return;
246 }
247 
248 void
249 utwitch_refresh(void *arg)
250 {
251 	struct utwitch_softc *sc = arg;
252 
253 	if (!sc->sc_initialized) {
254 		utwitch_read_value_request(sc);
255 	} else {
256 		/* calculate delta value */
257 		sc->sc_sensor_delta.value =
258 			(1000 * (sc->sc_curval - sc->sc_oldval)) / UPDATE_TICK;
259 		sc->sc_oldval = sc->sc_curval;
260 	}
261 }
262 
263 void
264 utwitch_set_mode(struct utwitch_softc *sc, uint8_t val)
265 {
266 	uint8_t req[8];
267 	usbd_status err;
268 
269 	memset(req, CMD_PADDING, sizeof(req));
270 	req[0] = CMD_MODE;
271 	req[1] = val;
272 	req[2] = CMD_EOF;
273 	err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, req,
274 		sc->sc_olen);
275 	if (err) {
276 		printf("uhidev_set_report error:EIO\n");
277 		return;
278 	}
279 
280 	/* wait ack */
281 	tsleep(&sc->sc_sensortask, 0, "utwitch", (1000*hz+999)/1000 + 1);
282 }
283 
284 void
285 utwitch_read_value_request(struct utwitch_softc *sc)
286 {
287 	uint8_t req[8];
288 
289 	memset(req, CMD_PADDING, sizeof(req));
290 	req[0] = CMD_READ;
291 	req[1] = CMD_EOF;
292 	sc->issueing_cmd = CMD_READ;
293 	sc->accepted_cmd = CMD_NONE;
294 	if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, req,
295 		sc->sc_olen))
296 		return;
297 
298 	/* wait till sensor data are updated, 500ms will be enough */
299 	tsleep(&sc->sc_sensortask, 0, "utwitch", (500*hz+999)/1000 + 1);
300 }
301 
302 void
303 utwitch_write_value_request(struct utwitch_softc *sc, uint32_t val)
304 {
305 	uint32_t v;
306 	uint8_t req[8];
307 
308 	req[0] = CMD_WRITE;
309 	req[1] = 0;
310 	req[6] = CMD_EOF;
311 	req[7] = CMD_PADDING;
312 	v = htobe32(val);
313 	memcpy(req + 2, &v, sizeof(uint32_t));
314 
315 	sc->issueing_cmd = CMD_WRITE;
316 	sc->accepted_cmd = CMD_NONE;
317 	if (uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, req,
318 		sc->sc_olen))
319 		return;
320 
321 	/* wait till sensor data are updated, 250ms will be enough */
322 	tsleep(&sc->sc_sensortask, 0, "utwitch", (250*hz+999)/1000 + 1);
323 }
324