gpiorfkill.c revision 1.1
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
45static int	gpiorfkill_match(device_t, cfdata_t, void *);
46static void	gpiorfkill_attach(device_t, device_t, void *);
47
48struct 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
60static void	gpiorfkill_enable(struct gpiorfkill_softc *, int);
61static void	gpiorfkill_sysctl_init(struct gpiorfkill_softc *);
62static int	gpiorfkill_enable_helper(SYSCTLFN_PROTO);
63
64CFATTACH_DECL_NEW(gpiorfkill, sizeof(struct gpiorfkill_softc),
65	gpiorfkill_match, gpiorfkill_attach, NULL, NULL);
66
67static int
68gpiorfkill_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
81static void
82gpiorfkill_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
112static void
113gpiorfkill_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
119static void
120gpiorfkill_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
141sysctl_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
147static int
148gpiorfkill_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