1 /* $NetBSD: ikbd.c,v 1.1 2024/12/09 22:05:17 jmcneill Exp $ */ 2 3 /* $OpenBSD: ikbd.c,v 1.2 2022/09/03 15:48:16 kettenis Exp $ */ 4 /* 5 * HID-over-i2c keyboard driver 6 * 7 * Copyright (c) 2016 Mark Kettenis <kettenis@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22 #include <sys/param.h> 23 #include <sys/systm.h> 24 #include <sys/kernel.h> 25 #include <sys/device.h> 26 #include <sys/ioctl.h> 27 28 #include <dev/i2c/i2cvar.h> 29 #include <dev/i2c/ihidev.h> 30 31 #include <dev/wscons/wsconsio.h> 32 #include <dev/wscons/wskbdvar.h> 33 #include <dev/wscons/wsksymdef.h> 34 35 #include <dev/hid/hid.h> 36 #include <dev/hid/hidkbdsc.h> 37 38 extern const struct wscons_keydesc hidkbd_keydesctab[]; 39 static struct wskbd_mapdata ikbd_keymapdata = { 40 hidkbd_keydesctab, 41 KB_US, 42 }; 43 44 struct ikbd_softc { 45 struct ihidev sc_hdev; 46 struct hidkbd sc_kbd; 47 int sc_spl; 48 }; 49 50 void ikbd_intr(struct ihidev *addr, void *ibuf, u_int len); 51 52 void ikbd_cngetc(void *, u_int *, int *); 53 void ikbd_cnpollc(void *, int); 54 55 const struct wskbd_consops ikbd_consops = { 56 .getc = ikbd_cngetc, 57 .pollc = ikbd_cnpollc, 58 .bell = NULL, 59 }; 60 61 int ikbd_enable(void *, int); 62 void ikbd_set_leds(void *, int); 63 int ikbd_ioctl(void *, u_long, void *, int, lwp_t *); 64 65 const struct wskbd_accessops ikbd_accessops = { 66 .enable = ikbd_enable, 67 .set_leds = ikbd_set_leds, 68 .ioctl = ikbd_ioctl, 69 }; 70 71 int ikbd_match(device_t, cfdata_t, void *); 72 void ikbd_attach(device_t, device_t, void *); 73 int ikbd_detach(device_t, int); 74 75 CFATTACH_DECL_NEW(ikbd, sizeof(struct ikbd_softc), 76 ikbd_match, ikbd_attach, ikbd_detach, NULL); 77 78 int 79 ikbd_match(device_t parent, cfdata_t match, void *aux) 80 { 81 struct ihidev_attach_arg *iha = aux; 82 int size; 83 void *desc; 84 85 ihidev_get_report_desc(iha->parent, &desc, &size); 86 if (!hid_is_collection(desc, size, iha->reportid, 87 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 88 return (IMATCH_NONE); 89 90 return (IMATCH_IFACECLASS); 91 } 92 93 void 94 ikbd_attach(device_t parent, device_t self, void *aux) 95 { 96 struct ikbd_softc *sc = device_private(self); 97 struct hidkbd *kbd = &sc->sc_kbd; 98 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 99 int dlen, repid; 100 void *desc; 101 102 sc->sc_hdev.sc_idev = self; 103 sc->sc_hdev.sc_intr = ikbd_intr; 104 sc->sc_hdev.sc_parent = iha->parent; 105 sc->sc_hdev.sc_report_id = iha->reportid; 106 107 ihidev_get_report_desc(iha->parent, &desc, &dlen); 108 repid = iha->reportid; 109 sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid); 110 sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid); 111 sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid); 112 113 if (hidkbd_attach(self, kbd, 1, 0, repid, desc, dlen) != 0) 114 return; 115 116 aprint_naive("\n"); 117 aprint_normal("\n"); 118 119 if (kbd->sc_console_keyboard) { 120 wskbd_cnattach(&ikbd_consops, sc, &ikbd_keymapdata); 121 ikbd_enable(sc, 1); 122 } 123 124 hidkbd_attach_wskbd(kbd, KB_US, &ikbd_accessops); 125 } 126 127 int 128 ikbd_detach(device_t self, int flags) 129 { 130 struct ikbd_softc *sc = device_private(self); 131 struct hidkbd *kbd = &sc->sc_kbd; 132 133 return hidkbd_detach(kbd, flags); 134 } 135 136 void 137 ikbd_intr(struct ihidev *addr, void *ibuf, u_int len) 138 { 139 struct ikbd_softc *sc = (struct ikbd_softc *)addr; 140 struct hidkbd *kbd = &sc->sc_kbd; 141 142 if (kbd->sc_enabled != 0) 143 hidkbd_input(kbd, (uint8_t *)ibuf, len); 144 } 145 146 int 147 ikbd_enable(void *v, int on) 148 { 149 struct ikbd_softc *sc = v; 150 struct hidkbd *kbd = &sc->sc_kbd; 151 int rv; 152 153 if ((rv = hidkbd_enable(kbd, on)) != 0) 154 return rv; 155 156 if (on) { 157 return ihidev_open(&sc->sc_hdev); 158 } else { 159 ihidev_close(&sc->sc_hdev); 160 return 0; 161 } 162 } 163 164 void 165 ikbd_set_leds(void *v, int leds) 166 { 167 } 168 169 int 170 ikbd_ioctl(void *v, u_long cmd, void *data, int flag, lwp_t *l) 171 { 172 struct ikbd_softc *sc = v; 173 struct hidkbd *kbd = &sc->sc_kbd; 174 175 switch (cmd) { 176 case WSKBDIO_GTYPE: 177 /* XXX: should we set something else? */ 178 *(u_int *)data = WSKBD_TYPE_USB; 179 return 0; 180 default: 181 return hidkbd_ioctl(kbd, cmd, data, flag, l); 182 } 183 } 184 185 /* Console interface. */ 186 void 187 ikbd_cngetc(void *v, u_int *type, int *data) 188 { 189 struct ikbd_softc *sc = v; 190 struct hidkbd *kbd = &sc->sc_kbd; 191 192 kbd->sc_polling = 1; 193 #if notyet 194 while (kbd->sc_npollchar <= 0) { 195 ihidev_poll(sc->sc_hdev.sc_parent); 196 delay(1000); 197 } 198 #endif 199 kbd->sc_polling = 0; 200 hidkbd_cngetc(kbd, type, data); 201 } 202 203 void 204 ikbd_cnpollc(void *v, int on) 205 { 206 struct ikbd_softc *sc = v; 207 208 if (on) 209 sc->sc_spl = spltty(); 210 else 211 splx(sc->sc_spl); 212 } 213