1 /* $NetBSD: gpiokeys.c,v 1.5 2017/09/23 23:54:30 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: gpiokeys.c,v 1.5 2017/09/23 23:54:30 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/systm.h> 35 #include <sys/device.h> 36 #include <sys/kmem.h> 37 #include <sys/bus.h> 38 #include <sys/gpio.h> 39 40 #include <dev/sysmon/sysmonvar.h> 41 #include <dev/sysmon/sysmon_taskq.h> 42 43 #include <dev/wscons/wsconsio.h> 44 #include <dev/wscons/wskbdvar.h> 45 #include <dev/wscons/wsksymdef.h> 46 #include <dev/wscons/wsksymvar.h> 47 #include <dev/wscons/linux_keymap.h> 48 49 #include <dev/fdt/fdtvar.h> 50 51 #define GPIOKEYS_POLL_INTERVAL mstohz(200) 52 53 #define KEY_POWER 116 54 #define KEY_SLEEP 142 55 56 static int gpiokeys_match(device_t, cfdata_t, void *); 57 static void gpiokeys_attach(device_t, device_t, void *); 58 59 static void gpiokeys_tick(void *); 60 static void gpiokeys_task(void *); 61 62 extern const struct wscons_keydesc ukbd_keydesctab[]; 63 static const struct wskbd_mapdata gpiokeys_keymapdata = { 64 ukbd_keydesctab, 65 KB_US, 66 }; 67 68 struct gpiokeys_softc; 69 70 struct gpiokeys_key { 71 struct gpiokeys_softc *key_sc; 72 int key_phandle; 73 char *key_label; 74 struct fdtbus_gpio_pin *key_pin; 75 u_int key_debounce; 76 u_int key_code; 77 struct sysmon_pswitch key_pswitch; 78 uint8_t key_usbcode; 79 u_int key_state; 80 81 struct gpiokeys_key *key_next; 82 }; 83 84 struct gpiokeys_softc { 85 device_t sc_dev; 86 int sc_phandle; 87 88 struct fdtbus_gpio_pin *sc_pin; 89 bool sc_always_on; 90 bool sc_enable_val; 91 92 struct gpiokeys_key *sc_keys; 93 callout_t sc_tick; 94 95 device_t sc_wskbddev; 96 int sc_enabled; 97 }; 98 99 CFATTACH_DECL_NEW(gpiokeys, sizeof(struct gpiokeys_softc), 100 gpiokeys_match, gpiokeys_attach, NULL, NULL); 101 102 static int 103 gpiokeys_enable(void *v, int on) 104 { 105 struct gpiokeys_softc * const sc = v; 106 107 sc->sc_enabled = on; 108 109 return 0; 110 } 111 112 static void 113 gpiokeys_set_leds(void *v, int leds) 114 { 115 } 116 117 static int 118 gpiokeys_ioctl(void *v, u_long cmd, void *data, int flag, lwp_t *l) 119 { 120 switch (cmd) { 121 case WSKBDIO_GTYPE: 122 *(int *)data = WSKBD_TYPE_USB; 123 return 0; 124 } 125 126 return EPASSTHROUGH; 127 } 128 129 static const struct wskbd_accessops gpiokeys_accessops = { 130 .enable = gpiokeys_enable, 131 .set_leds = gpiokeys_set_leds, 132 .ioctl = gpiokeys_ioctl 133 }; 134 135 static int 136 gpiokeys_match(device_t parent, cfdata_t cf, void *aux) 137 { 138 const char * const compatible[] = { "gpio-keys", NULL }; 139 const struct fdt_attach_args *faa = aux; 140 141 return of_match_compatible(faa->faa_phandle, compatible); 142 } 143 144 static void 145 gpiokeys_attach(device_t parent, device_t self, void *aux) 146 { 147 struct gpiokeys_softc * const sc = device_private(self); 148 const struct fdt_attach_args *faa = aux; 149 const int phandle = faa->faa_phandle; 150 struct gpiokeys_key *key; 151 u_int debounce, code; 152 int use_wskbddev = 0; 153 int child, len; 154 155 sc->sc_dev = self; 156 sc->sc_phandle = phandle; 157 158 aprint_naive("\n"); 159 aprint_normal(":"); 160 161 for (child = OF_child(phandle); child; child = OF_peer(child)) { 162 if (of_getprop_uint32(child, "linux,code", &code)) 163 continue; 164 if (of_getprop_uint32(child, "debounce-interval", &debounce)) 165 debounce = 5; /* default */ 166 len = OF_getproplen(child, "label"); 167 if (len <= 0) { 168 continue; 169 } 170 key = kmem_zalloc(sizeof(*key), KM_SLEEP); 171 key->key_sc = sc; 172 key->key_phandle = child; 173 key->key_code = code; 174 key->key_label = kmem_zalloc(len, KM_SLEEP); 175 if (OF_getprop(child, "label", key->key_label, len) != len) { 176 kmem_free(key->key_label, len); 177 kmem_free(key, sizeof(*key)); 178 continue; 179 } 180 key->key_debounce = debounce; 181 key->key_pin = fdtbus_gpio_acquire(child, "gpios", 182 GPIO_PIN_INPUT); 183 if (key->key_pin) 184 key->key_state = fdtbus_gpio_read(key->key_pin); 185 186 switch (code) { 187 case KEY_POWER: 188 key->key_pswitch.smpsw_name = key->key_label; 189 key->key_pswitch.smpsw_type = PSWITCH_TYPE_POWER; 190 break; 191 case KEY_SLEEP: 192 key->key_pswitch.smpsw_name = key->key_label; 193 key->key_pswitch.smpsw_type = PSWITCH_TYPE_SLEEP; 194 break; 195 default: 196 key->key_usbcode = linux_key_to_usb(code); 197 if (key->key_usbcode != 0) { 198 use_wskbddev++; 199 } else { 200 key->key_pswitch.smpsw_name = key->key_label; 201 key->key_pswitch.smpsw_type = PSWITCH_TYPE_HOTKEY; 202 } 203 break; 204 } 205 206 if (key->key_pswitch.smpsw_name != NULL && 207 sysmon_pswitch_register(&key->key_pswitch) != 0) { 208 aprint_error(" %s:ERROR", key->key_label); 209 kmem_free(key->key_label, len); 210 kmem_free(key, sizeof(*key)); 211 continue; 212 } 213 214 if (sc->sc_keys) { 215 aprint_normal(", %s", key->key_label); 216 } else { 217 aprint_normal(" %s", key->key_label); 218 } 219 220 key->key_next = sc->sc_keys; 221 sc->sc_keys = key; 222 } 223 224 if (sc->sc_keys == NULL) { 225 aprint_normal(" no keys configured\n"); 226 return; 227 } 228 229 aprint_normal("\n"); 230 231 if (use_wskbddev > 0) { 232 struct wskbddev_attach_args a; 233 memset(&a, 0, sizeof(a)); 234 a.console = false; 235 a.keymap = &gpiokeys_keymapdata; 236 a.accessops = &gpiokeys_accessops; 237 a.accesscookie = sc; 238 sc->sc_wskbddev = config_found_ia(self, "wskbddev", &a, 239 wskbddevprint); 240 } 241 242 callout_init(&sc->sc_tick, CALLOUT_MPSAFE); 243 callout_setfunc(&sc->sc_tick, gpiokeys_tick, sc); 244 245 gpiokeys_tick(sc); 246 } 247 248 static void 249 gpiokeys_tick(void *priv) 250 { 251 struct gpiokeys_softc * const sc = priv; 252 struct gpiokeys_key *key; 253 254 for (key = sc->sc_keys; key; key = key->key_next) { 255 if (key->key_pin == NULL) { 256 continue; 257 } 258 const int new_state = fdtbus_gpio_read(key->key_pin); 259 if (new_state != key->key_state) { 260 key->key_state = new_state; 261 sysmon_task_queue_sched(0, gpiokeys_task, key); 262 } 263 } 264 callout_schedule(&sc->sc_tick, GPIOKEYS_POLL_INTERVAL); 265 } 266 267 static void 268 gpiokeys_task(void *priv) 269 { 270 struct gpiokeys_key *key = priv; 271 struct gpiokeys_softc *sc = key->key_sc; 272 273 if (key->key_pswitch.smpsw_name) { 274 sysmon_pswitch_event(&key->key_pswitch, 275 key->key_state ? PSWITCH_EVENT_PRESSED : PSWITCH_EVENT_RELEASED); 276 } else if (sc->sc_enabled && sc->sc_wskbddev != NULL && key->key_usbcode != 0) { 277 wskbd_input(sc->sc_wskbddev, 278 key->key_state ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP, 279 key->key_usbcode); 280 } 281 } 282