1/* $OpenBSD: ikbd.c,v 1.2 2022/09/03 15:48:16 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 37struct ikbd_softc { 38 struct ihidev sc_hdev; 39 struct hidkbd sc_kbd; 40 int sc_spl; 41}; 42 43void ikbd_intr(struct ihidev *addr, void *ibuf, u_int len); 44 45void ikbd_cngetc(void *, u_int *, int *); 46void ikbd_cnpollc(void *, int); 47void ikbd_cnbell(void *, u_int, u_int, u_int); 48 49const struct wskbd_consops ikbd_consops = { 50 ikbd_cngetc, 51 ikbd_cnpollc, 52 ikbd_cnbell, 53}; 54 55int ikbd_enable(void *, int); 56void ikbd_set_leds(void *, int); 57int ikbd_ioctl(void *, u_long, caddr_t, int, struct proc *); 58 59const struct wskbd_accessops ikbd_accessops = { 60 ikbd_enable, 61 ikbd_set_leds, 62 ikbd_ioctl, 63}; 64 65int ikbd_match(struct device *, void *, void *); 66void ikbd_attach(struct device *, struct device *, void *); 67int ikbd_detach(struct device *, int); 68 69struct cfdriver ikbd_cd = { 70 NULL, "ikbd", DV_DULL 71}; 72 73const struct cfattach ikbd_ca = { 74 sizeof(struct ikbd_softc), 75 ikbd_match, 76 ikbd_attach, 77 ikbd_detach 78}; 79 80int 81ikbd_match(struct device *parent, void *match, void *aux) 82{ 83 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 84 int size; 85 void *desc; 86 87 ihidev_get_report_desc(iha->parent, &desc, &size); 88 if (!hid_is_collection(desc, size, iha->reportid, 89 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 90 return (IMATCH_NONE); 91 92 return (IMATCH_IFACECLASS); 93} 94 95void 96ikbd_attach(struct device *parent, struct device *self, void *aux) 97{ 98 struct ikbd_softc *sc = (struct ikbd_softc *)self; 99 struct hidkbd *kbd = &sc->sc_kbd; 100 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 101 int dlen, repid; 102 void *desc; 103 104 sc->sc_hdev.sc_intr = ikbd_intr; 105 sc->sc_hdev.sc_parent = iha->parent; 106 sc->sc_hdev.sc_report_id = iha->reportid; 107 108 ihidev_get_report_desc(iha->parent, &desc, &dlen); 109 repid = iha->reportid; 110 sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid); 111 sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid); 112 sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid); 113 114 if (hidkbd_attach(self, kbd, 1, 0, repid, desc, dlen) != 0) 115 return; 116 117 printf("\n"); 118 119 if (kbd->sc_console_keyboard) { 120 extern struct wskbd_mapdata ukbd_keymapdata; 121 122 ukbd_keymapdata.layout = KB_US | KB_DEFAULT; 123 wskbd_cnattach(&ikbd_consops, sc, &ukbd_keymapdata); 124 ikbd_enable(sc, 1); 125 } 126 127 hidkbd_attach_wskbd(kbd, KB_US | KB_DEFAULT, &ikbd_accessops); 128} 129 130int 131ikbd_detach(struct device *self, int flags) 132{ 133 struct ikbd_softc *sc = (struct ikbd_softc *)self; 134 struct hidkbd *kbd = &sc->sc_kbd; 135 136 return hidkbd_detach(kbd, flags); 137} 138 139void 140ikbd_intr(struct ihidev *addr, void *ibuf, u_int len) 141{ 142 struct ikbd_softc *sc = (struct ikbd_softc *)addr; 143 struct hidkbd *kbd = &sc->sc_kbd; 144 145 if (kbd->sc_enabled != 0) 146 hidkbd_input(kbd, (uint8_t *)ibuf, len); 147} 148 149int 150ikbd_enable(void *v, int on) 151{ 152 struct ikbd_softc *sc = v; 153 struct hidkbd *kbd = &sc->sc_kbd; 154 int rv; 155 156 if ((rv = hidkbd_enable(kbd, on)) != 0) 157 return rv; 158 159 if (on) { 160 return ihidev_open(&sc->sc_hdev); 161 } else { 162 ihidev_close(&sc->sc_hdev); 163 return 0; 164 } 165} 166 167void 168ikbd_set_leds(void *v, int leds) 169{ 170} 171 172int 173ikbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 174{ 175 struct ikbd_softc *sc = v; 176 struct hidkbd *kbd = &sc->sc_kbd; 177 int rc; 178 179 switch (cmd) { 180 case WSKBDIO_GTYPE: 181 /* XXX: should we set something else? */ 182 *(u_int *)data = WSKBD_TYPE_USB; 183 return 0; 184 default: 185 rc = ihidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 186 if (rc != -1) 187 return rc; 188 else 189 return hidkbd_ioctl(kbd, cmd, data, flag, p); 190 } 191} 192 193/* Console interface. */ 194void 195ikbd_cngetc(void *v, u_int *type, int *data) 196{ 197 struct ikbd_softc *sc = v; 198 struct hidkbd *kbd = &sc->sc_kbd; 199 200 kbd->sc_polling = 1; 201 while (kbd->sc_npollchar <= 0) { 202 ihidev_poll(sc->sc_hdev.sc_parent); 203 delay(1000); 204 } 205 kbd->sc_polling = 0; 206 hidkbd_cngetc(kbd, type, data); 207} 208 209void 210ikbd_cnpollc(void *v, int on) 211{ 212 struct ikbd_softc *sc = v; 213 214 if (on) 215 sc->sc_spl = spltty(); 216 else 217 splx(sc->sc_spl); 218} 219 220void 221ikbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 222{ 223 hidkbd_bell(pitch, period, volume, 1); 224} 225