xref: /openbsd/sys/dev/i2c/ikbd.c (revision 3cab2bb3)
1 /*	$OpenBSD: ikbd.c,v 1.1 2016/01/14 21:01:49 kettenis Exp $	*/
2 /*
3  * HID-over-i2c keyboard driver
4  *
5  * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/device.h>
24 #include <sys/ioctl.h>
25 #include <sys/timeout.h>
26 
27 #include <dev/i2c/i2cvar.h>
28 #include <dev/i2c/ihidev.h>
29 
30 #include <dev/wscons/wsconsio.h>
31 #include <dev/wscons/wskbdvar.h>
32 #include <dev/wscons/wsksymdef.h>
33 
34 #include <dev/hid/hid.h>
35 #include <dev/hid/hidkbdsc.h>
36 
37 struct ikbd_softc {
38 	struct ihidev	sc_hdev;
39 	struct hidkbd	sc_kbd;
40 };
41 
42 void	ikbd_intr(struct ihidev *addr, void *ibuf, u_int len);
43 
44 int	ikbd_enable(void *, int);
45 void	ikbd_set_leds(void *, int);
46 int	ikbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
47 
48 const struct wskbd_accessops ikbd_accessops = {
49 	ikbd_enable,
50 	ikbd_set_leds,
51 	ikbd_ioctl,
52 };
53 
54 int	ikbd_match(struct device *, void *, void *);
55 void	ikbd_attach(struct device *, struct device *, void *);
56 int	ikbd_detach(struct device *, int);
57 
58 struct cfdriver ikbd_cd = {
59 	NULL, "ikbd", DV_DULL
60 };
61 
62 const struct cfattach ikbd_ca = {
63 	sizeof(struct ikbd_softc),
64 	ikbd_match,
65 	ikbd_attach,
66 	ikbd_detach
67 };
68 
69 int
70 ikbd_match(struct device *parent, void *match, void *aux)
71 {
72 	struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux;
73 	int size;
74 	void *desc;
75 
76 	ihidev_get_report_desc(iha->parent, &desc, &size);
77 	if (!hid_is_collection(desc, size, iha->reportid,
78 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
79 		return (IMATCH_NONE);
80 
81 	return (IMATCH_IFACECLASS);
82 }
83 
84 void
85 ikbd_attach(struct device *parent, struct device *self, void *aux)
86 {
87 	struct ikbd_softc *sc = (struct ikbd_softc *)self;
88 	struct hidkbd *kbd = &sc->sc_kbd;
89 	struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux;
90 	int dlen, repid;
91 	void *desc;
92 
93 	sc->sc_hdev.sc_intr = ikbd_intr;
94 	sc->sc_hdev.sc_parent = iha->parent;
95 	sc->sc_hdev.sc_report_id = iha->reportid;
96 
97 	ihidev_get_report_desc(iha->parent, &desc, &dlen);
98 	repid = iha->reportid;
99 	sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid);
100 	sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid);
101 	sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid);
102 
103 	if (hidkbd_attach(self, kbd, 0, 0, repid, desc, dlen) != 0)
104 		return;
105 
106 	printf("\n");
107 
108 	hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &ikbd_accessops);
109 }
110 
111 int
112 ikbd_detach(struct device *self, int flags)
113 {
114 	struct ikbd_softc *sc = (struct ikbd_softc *)self;
115 	struct hidkbd *kbd = &sc->sc_kbd;
116 
117 	return hidkbd_detach(kbd, flags);
118 }
119 
120 void
121 ikbd_intr(struct ihidev *addr, void *ibuf, u_int len)
122 {
123 	struct ikbd_softc *sc = (struct ikbd_softc *)addr;
124 	struct hidkbd *kbd = &sc->sc_kbd;
125 
126 	if (kbd->sc_enabled != 0)
127 		hidkbd_input(kbd, (uint8_t *)ibuf, len);
128 }
129 
130 int
131 ikbd_enable(void *v, int on)
132 {
133 	struct ikbd_softc *sc = v;
134 	struct hidkbd *kbd = &sc->sc_kbd;
135 	int rv;
136 
137 	if ((rv = hidkbd_enable(kbd, on)) != 0)
138 		return rv;
139 
140 	if (on) {
141 		return ihidev_open(&sc->sc_hdev);
142 	} else {
143 		ihidev_close(&sc->sc_hdev);
144 		return 0;
145 	}
146 }
147 
148 void
149 ikbd_set_leds(void *v, int leds)
150 {
151 }
152 
153 int
154 ikbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
155 {
156 	struct ikbd_softc *sc = v;
157 	struct hidkbd *kbd = &sc->sc_kbd;
158 	int rc;
159 
160 	switch (cmd) {
161 	case WSKBDIO_GTYPE:
162 		/* XXX: should we set something else? */
163 		*(u_int *)data = WSKBD_TYPE_USB;
164 		return 0;
165 	default:
166 		rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p);
167 		if (rc != -1)
168 			return rc;
169 		else
170 			return hidkbd_ioctl(kbd, cmd, data, flag, p);
171 	}
172 }
173