Home | History | Annotate | Line # | Download | only in i2c
      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 (at) 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