1 /* $NetBSD: gpiorfkill.c,v 1.1 2015/05/29 23:17:13 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 "locators.h" 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: gpiorfkill.c,v 1.1 2015/05/29 23:17:13 jmcneill Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/device.h> 37 #include <sys/intr.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/sysctl.h> 41 #include <sys/gpio.h> 42 43 #include <dev/gpio/gpiovar.h> 44 45 static int gpiorfkill_match(device_t, cfdata_t, void *); 46 static void gpiorfkill_attach(device_t, device_t, void *); 47 48 struct gpiorfkill_softc { 49 device_t sc_dev; 50 void *sc_gpio; 51 struct gpio_pinmap sc_map; 52 int sc_pinmap[1]; 53 54 int sc_state; 55 56 struct sysctllog *sc_sysctllog; 57 int sc_sysctlnode; 58 }; 59 60 static void gpiorfkill_enable(struct gpiorfkill_softc *, int); 61 static void gpiorfkill_sysctl_init(struct gpiorfkill_softc *); 62 static int gpiorfkill_enable_helper(SYSCTLFN_PROTO); 63 64 CFATTACH_DECL_NEW(gpiorfkill, sizeof(struct gpiorfkill_softc), 65 gpiorfkill_match, gpiorfkill_attach, NULL, NULL); 66 67 static int 68 gpiorfkill_match(device_t parent, cfdata_t cf, void *aux) 69 { 70 struct gpio_attach_args * const ga = aux; 71 72 if (strcmp(ga->ga_dvname, cf->cf_name) != 0) 73 return 0; 74 75 if (ga->ga_offset == -1 || gpio_npins(ga->ga_mask) != 1) 76 return 0; 77 78 return 1; 79 } 80 81 static void 82 gpiorfkill_attach(device_t parent, device_t self, void *aux) 83 { 84 struct gpiorfkill_softc * const sc = device_private(self); 85 struct gpio_attach_args * const ga = aux; 86 int caps; 87 88 sc->sc_dev = self; 89 sc->sc_gpio = ga->ga_gpio; 90 sc->sc_map.pm_map = sc->sc_pinmap; 91 if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask, 92 &sc->sc_map)) { 93 aprint_error(": couldn't map pins\n"); 94 return; 95 } 96 97 caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, 0); 98 if ((caps & GPIO_PIN_OUTPUT) == 0) { 99 aprint_error(": pin is not an output pin\n"); 100 return; 101 } 102 103 gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, 0, GPIO_PIN_OUTPUT); 104 105 aprint_naive("\n"); 106 aprint_normal("\n"); 107 108 gpiorfkill_enable(sc, 1); 109 gpiorfkill_sysctl_init(sc); 110 } 111 112 static void 113 gpiorfkill_enable(struct gpiorfkill_softc *sc, int enable) 114 { 115 sc->sc_state = enable; 116 gpio_pin_write(sc->sc_gpio, &sc->sc_map, 0, sc->sc_state); 117 } 118 119 static void 120 gpiorfkill_sysctl_init(struct gpiorfkill_softc *sc) 121 { 122 const struct sysctlnode *node, *devnode; 123 int error; 124 125 error = sysctl_createv(&sc->sc_sysctllog, 0, NULL, &devnode, 126 0, CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 127 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 128 if (error) 129 goto sysctl_failed; 130 131 error = sysctl_createv(&sc->sc_sysctllog, 0, &devnode, &node, 132 CTLFLAG_READWRITE, CTLTYPE_INT, "enable", NULL, 133 gpiorfkill_enable_helper, 0, (void *)sc, 0, 134 CTL_CREATE, CTL_EOL); 135 if (error) 136 goto sysctl_failed; 137 sc->sc_sysctlnode = node->sysctl_num; 138 139 return; 140 141 sysctl_failed: 142 aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes (%d)\n", 143 error); 144 sysctl_teardown(&sc->sc_sysctllog); 145 } 146 147 static int 148 gpiorfkill_enable_helper(SYSCTLFN_ARGS) 149 { 150 struct gpiorfkill_softc *sc; 151 struct sysctlnode node; 152 int error, enable; 153 154 node = *rnode; 155 sc = node.sysctl_data; 156 enable = sc->sc_state; 157 node.sysctl_data = &enable; 158 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 159 if (error || newp == NULL) 160 return error; 161 162 enable = !!enable; 163 gpiorfkill_enable(sc, enable); 164 165 return 0; 166 } 167