xref: /netbsd-src/sys/dev/gpio/gpio.c (revision 6deb2c22d20de1d75d538e8a5c57b573926fd157)
1 /* $NetBSD: gpio.c,v 1.28 2009/08/23 12:08:56 mbalmer Exp $ */
2 /*	$OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $	*/
3 
4 /*
5  * Copyright (c) 2008, 2009 Marc Balmer <marc@msys.ch>
6  * Copyright (c) 2004, 2006 Alexander Yurchenko <grange@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/cdefs.h>
22 __KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.28 2009/08/23 12:08:56 mbalmer Exp $");
23 
24 /*
25  * General Purpose Input/Output framework.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/conf.h>
31 #include <sys/device.h>
32 #include <sys/fcntl.h>
33 #include <sys/ioctl.h>
34 #include <sys/gpio.h>
35 #include <sys/vnode.h>
36 #include <sys/kmem.h>
37 #include <sys/queue.h>
38 #include <sys/kauth.h>
39 
40 #include <dev/gpio/gpiovar.h>
41 
42 #include "locators.h"
43 
44 #ifdef GPIO_DEBUG
45 #define DPRINTFN(n, x)	do { if (gpiodebug > (n)) printf x; } while (0)
46 int gpiodebug = 0;
47 #else
48 #define DPRINTFN(n, x)
49 #endif
50 #define DPRINTF(x)	DPRINTFN(0, x)
51 
52 struct gpio_softc {
53 	device_t		 sc_dev;
54 
55 	gpio_chipset_tag_t	 sc_gc;		/* GPIO controller */
56 	gpio_pin_t		*sc_pins;	/* pins array */
57 	int			 sc_npins;	/* number of pins */
58 
59 	int			 sc_opened;
60 	LIST_HEAD(, gpio_dev)	 sc_devs;	/* devices */
61 	LIST_HEAD(, gpio_name)	 sc_names;	/* named pins */
62 };
63 
64 int	gpio_match(device_t, cfdata_t, void *);
65 int	gpio_submatch(device_t, cfdata_t, const int *, void *);
66 void	gpio_attach(device_t, device_t, void *);
67 bool	gpio_resume(device_t PMF_FN_PROTO);
68 int	gpio_detach(device_t, int);
69 int	gpio_activate(device_t, enum devact);
70 int	gpio_search(device_t, cfdata_t, const int *, void *);
71 int	gpio_print(void *, const char *);
72 int	gpio_pinbyname(struct gpio_softc *, char *);
73 
74 /* Old API */
75 int	gpio_ioctl_oapi(struct gpio_softc *, u_long, void *, int, kauth_cred_t);
76 
77 CFATTACH_DECL3_NEW(gpio, sizeof(struct gpio_softc),
78     gpio_match, gpio_attach, gpio_detach, gpio_activate, NULL, NULL,
79     DVF_DETACH_SHUTDOWN);
80 
81 dev_type_open(gpioopen);
82 dev_type_close(gpioclose);
83 dev_type_ioctl(gpioioctl);
84 
85 const struct cdevsw gpio_cdevsw = {
86 	gpioopen, gpioclose, noread, nowrite, gpioioctl,
87 	nostop, notty, nopoll, nommap, nokqfilter, D_OTHER,
88 };
89 
90 extern struct cfdriver gpio_cd;
91 
92 int
93 gpio_match(device_t parent, cfdata_t cf, void *aux)
94 {
95 	return 1;
96 }
97 
98 int
99 gpio_submatch(device_t parent, cfdata_t cf, const int *ip, void *aux)
100 {
101 	struct gpio_attach_args *ga = aux;
102 
103 	if (ga->ga_offset == -1)
104 		return 0;
105 
106 	return strcmp(ga->ga_dvname, cf->cf_name) == 0;
107 }
108 
109 bool
110 gpio_resume(device_t self PMF_FN_ARGS)
111 {
112 	struct gpio_softc *sc = device_private(self);
113 	int pin;
114 
115 	for (pin = 0; pin < sc->sc_npins; pin++) {
116 		gpiobus_pin_ctl(sc->sc_gc, pin, sc->sc_pins[pin].pin_flags);
117 		gpiobus_pin_write(sc->sc_gc, pin, sc->sc_pins[pin].pin_state);
118 	}
119 	return true;
120 }
121 
122 void
123 gpio_attach(device_t parent, device_t self, void *aux)
124 {
125 	struct gpio_softc *sc = device_private(self);
126 	struct gpiobus_attach_args *gba = aux;
127 
128 	sc->sc_dev = self;
129 	sc->sc_gc = gba->gba_gc;
130 	sc->sc_pins = gba->gba_pins;
131 	sc->sc_npins = gba->gba_npins;
132 
133 	printf(": %d pins\n", sc->sc_npins);
134 
135 	if (!pmf_device_register(self, NULL, gpio_resume))
136 		aprint_error_dev(self, "couldn't establish power handler\n");
137 
138 	/*
139 	 * Attach all devices that can be connected to the GPIO pins
140 	 * described in the kernel configuration file.
141 	 */
142 	config_search_ia(gpio_search, self, "gpio", sc);
143 }
144 
145 int
146 gpio_detach(device_t self, int flags)
147 {
148 #if 0
149 	int maj, mn;
150 
151 	/* Locate the major number */
152 	for (maj = 0; maj < nchrdev; maj++)
153 		if (cdevsw[maj].d_open == gpioopen)
154 			break;
155 
156 	/* Nuke the vnodes for any open instances (calls close) */
157 	mn = device_unit(self);
158 	vdevgone(maj, mn, mn, VCHR);
159 #endif
160 	return 0;
161 }
162 
163 int
164 gpio_activate(device_t self, enum devact act)
165 {
166 	printf("gpio_active: ");
167 	switch (act) {
168 	case DVACT_ACTIVATE:
169 		DPRINTF(("ACTIVATE\n"));
170 		return EOPNOTSUPP;
171 	case DVACT_DEACTIVATE:
172 		DPRINTF(("DEACTIVATE\n"));
173 		break;
174 	}
175 	return 0;
176 }
177 
178 int
179 gpio_search(device_t parent, cfdata_t cf,
180     const int *ldesc, void *aux)
181 {
182 	struct gpio_attach_args ga;
183 
184 	ga.ga_gpio = aux;
185 	ga.ga_offset = cf->cf_loc[GPIOCF_OFFSET];
186 	ga.ga_mask = cf->cf_loc[GPIOCF_MASK];
187 
188 	if (config_match(parent, cf, &ga) > 0)
189 		config_attach(parent, cf, &ga, gpio_print);
190 
191 	return 0;
192 }
193 
194 int
195 gpio_print(void *aux, const char *pnp)
196 {
197 	struct gpio_attach_args *ga = aux;
198 	int i;
199 
200 	printf(" pins");
201 	for (i = 0; i < 32; i++)
202 		if (ga->ga_mask & (1 << i))
203 			printf(" %d", ga->ga_offset + i);
204 
205 	return UNCONF;
206 }
207 
208 int
209 gpiobus_print(void *aux, const char *pnp)
210 {
211 #if 0
212 	struct gpiobus_attach_args *gba = aux;
213 #endif
214 	if (pnp != NULL)
215 		printf("gpiobus at %s", pnp);
216 
217 	return UNCONF;
218 }
219 
220 /* return 1 if all pins can be mapped, 0 if not */
221 
222 int
223 gpio_pin_can_map(void *gpio, int offset, u_int32_t mask)
224 {
225 	struct gpio_softc *sc = gpio;
226 	int npins, pin, i;
227 
228 	npins = gpio_npins(mask);
229 	if (npins > sc->sc_npins)
230 		return 0;
231 
232 	for (npins = 0, i = 0; i < 32; i++)
233 		if (mask & (1 << i)) {
234 			pin = offset + i;
235 			if (pin < 0 || pin >= sc->sc_npins)
236 				return 0;
237 			if (sc->sc_pins[pin].pin_mapped)
238 				return 0;
239 		}
240 
241 	return 1;
242 }
243 
244 int
245 gpio_pin_map(void *gpio, int offset, u_int32_t mask, struct gpio_pinmap *map)
246 {
247 	struct gpio_softc *sc = gpio;
248 	int npins, pin, i;
249 
250 	npins = gpio_npins(mask);
251 	if (npins > sc->sc_npins)
252 		return 1;
253 
254 	for (npins = 0, i = 0; i < 32; i++)
255 		if (mask & (1 << i)) {
256 			pin = offset + i;
257 			if (pin < 0 || pin >= sc->sc_npins)
258 				return 1;
259 			if (sc->sc_pins[pin].pin_mapped)
260 				return 1;
261 			sc->sc_pins[pin].pin_mapped = 1;
262 			map->pm_map[npins++] = pin;
263 		}
264 	map->pm_size = npins;
265 
266 	return 0;
267 }
268 
269 void
270 gpio_pin_unmap(void *gpio, struct gpio_pinmap *map)
271 {
272 	struct gpio_softc *sc = gpio;
273 	int pin, i;
274 
275 	for (i = 0; i < map->pm_size; i++) {
276 		pin = map->pm_map[i];
277 		sc->sc_pins[pin].pin_mapped = 0;
278 	}
279 }
280 
281 int
282 gpio_pin_read(void *gpio, struct gpio_pinmap *map, int pin)
283 {
284 	struct gpio_softc *sc = gpio;
285 
286 	return gpiobus_pin_read(sc->sc_gc, map->pm_map[pin]);
287 }
288 
289 void
290 gpio_pin_write(void *gpio, struct gpio_pinmap *map, int pin, int value)
291 {
292 	struct gpio_softc *sc = gpio;
293 
294 	gpiobus_pin_write(sc->sc_gc, map->pm_map[pin], value);
295 	sc->sc_pins[map->pm_map[pin]].pin_state = value;
296 }
297 
298 void
299 gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags)
300 {
301 	struct gpio_softc *sc = gpio;
302 
303 	return gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags);
304 }
305 
306 int
307 gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
308 {
309 	struct gpio_softc *sc = gpio;
310 
311 	return sc->sc_pins[map->pm_map[pin]].pin_caps;
312 }
313 
314 int
315 gpio_npins(u_int32_t mask)
316 {
317 	int npins, i;
318 
319 	for (npins = 0, i = 0; i < 32; i++)
320 		if (mask & (1 << i))
321 			npins++;
322 
323 	return npins;
324 }
325 
326 int
327 gpioopen(dev_t dev, int flag, int mode, struct lwp *l)
328 {
329 	struct gpio_softc *sc;
330 	int ret;
331 
332 	sc = device_lookup_private(&gpio_cd, minor(dev));
333 	if (sc == NULL)
334 		return ENXIO;
335 	DPRINTF(("%s: opening\n", device_xname(sc->sc_dev)));
336 	if (sc->sc_opened) {
337 		DPRINTF(("%s: already opened\n", device_xname(sc->sc_dev)));
338 		return EBUSY;
339 	}
340 
341 	if ((ret = gpiobus_open(sc->sc_gc, sc->sc_dev))) {
342 		DPRINTF(("%s: gpiobus_open returned %d\n",
343 		    device_xname(sc->sc_dev),
344 		    ret));
345 		return ret;
346 	}
347 
348 	sc->sc_opened = 1;
349 
350 	return 0;
351 }
352 
353 int
354 gpioclose(dev_t dev, int flag, int mode, struct lwp *l)
355 {
356 	struct gpio_softc *sc;
357 
358 	sc = device_lookup_private(&gpio_cd, minor(dev));
359 	DPRINTF(("%s: closing\n", device_xname(sc->sc_dev)));
360 	gpiobus_close(sc->sc_gc, sc->sc_dev);
361 	sc->sc_opened = 0;
362 
363 	return 0;
364 }
365 
366 int
367 gpio_pinbyname(struct gpio_softc *sc, char *gp_name)
368 {
369         struct gpio_name *nm;
370 
371         LIST_FOREACH(nm, &sc->sc_names, gp_next)
372                 if (!strcmp(nm->gp_name, gp_name))
373                         return nm->gp_pin;
374         return -1;
375 }
376 
377 int
378 gpioioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
379 {
380 	struct gpio_softc *sc;
381 	gpio_chipset_tag_t gc;
382 	struct gpio_info *info;
383 	struct gpio_attach *attach;
384 	struct gpio_attach_args ga;
385 	struct gpio_dev *gdev;
386 	struct gpio_req *req;
387 	struct gpio_name *nm;
388 	struct gpio_set *set;
389 	device_t dv;
390 	cfdata_t cf;
391 	kauth_cred_t cred;
392 	int locs[GPIOCF_NLOCS];
393 	int pin, value, flags, npins;
394 
395 	sc = device_lookup_private(&gpio_cd, minor(dev));
396 	gc = sc->sc_gc;
397 
398 	if (cmd != GPIOINFO && !device_is_active(sc->sc_dev)) {
399 		DPRINTF(("%s: device is not active\n",
400 		    device_xname(sc->sc_dev)));
401 		return EBUSY;
402 	}
403 
404 	cred = kauth_cred_get();
405 
406 	switch (cmd) {
407 	case GPIOINFO:
408 		info = (struct gpio_info *)data;
409 		if (!kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
410 		    NULL, NULL, NULL, NULL))
411 			info->gpio_npins = sc->sc_npins;
412 		else {
413 			for (pin = npins = 0; pin < sc->sc_npins; pin++)
414 				if (sc->sc_pins[pin].pin_flags & GPIO_PIN_SET)
415 					++npins;
416 			info->gpio_npins = npins;
417 		}
418 		break;
419 	case GPIOREAD:
420 		req = (struct gpio_req *)data;
421 
422 		if (req->gp_name[0] != '\0') {
423 			pin = gpio_pinbyname(sc, req->gp_name);
424 			if (pin == -1)
425 				return EINVAL;
426 		} else
427 			pin = req->gp_pin;
428 
429 		if (pin < 0 || pin >= sc->sc_npins)
430 			return EINVAL;
431 
432 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
433 		    kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
434 		    NULL, NULL, NULL, NULL))
435 			return EPERM;
436 
437 		/* return read value */
438 		req->gp_value = gpiobus_pin_read(gc, pin);
439 		break;
440 	case GPIOWRITE:
441 		if ((flag & FWRITE) == 0)
442 			return EBADF;
443 
444 		req = (struct gpio_req *)data;
445 
446 		if (req->gp_name[0] != '\0') {
447 			pin = gpio_pinbyname(sc, req->gp_name);
448 			if (pin == -1)
449 				return EINVAL;
450 		} else
451 			pin = req->gp_pin;
452 
453 		if (pin < 0 || pin >= sc->sc_npins)
454 			return EINVAL;
455 
456 		if (sc->sc_pins[pin].pin_mapped)
457 			return EBUSY;
458 
459 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
460 		    kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
461 		    NULL, NULL, NULL, NULL))
462 			return EPERM;
463 
464 		value = req->gp_value;
465 		if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH)
466 			return EINVAL;
467 
468 		gpiobus_pin_write(gc, pin, value);
469 		/* return old value */
470 		req->gp_value = sc->sc_pins[pin].pin_state;
471 		/* update current value */
472 		sc->sc_pins[pin].pin_state = value;
473 		break;
474 	case GPIOTOGGLE:
475 		if ((flag & FWRITE) == 0)
476 			return EBADF;
477 
478 		req = (struct gpio_req *)data;
479 
480 		if (req->gp_name[0] != '\0') {
481 			pin = gpio_pinbyname(sc, req->gp_name);
482 			if (pin == -1)
483 				return EINVAL;
484 		} else
485 			pin = req->gp_pin;
486 
487 		if (pin < 0 || pin >= sc->sc_npins)
488 			return EINVAL;
489 
490 		if (sc->sc_pins[pin].pin_mapped)
491 			return EBUSY;
492 
493 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
494 		    kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
495 		    NULL, NULL, NULL, NULL))
496 			return EPERM;
497 
498 		value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ?
499 		    GPIO_PIN_HIGH : GPIO_PIN_LOW);
500 		gpiobus_pin_write(gc, pin, value);
501 		/* return old value */
502 		req->gp_value = sc->sc_pins[pin].pin_state;
503 		/* update current value */
504 		sc->sc_pins[pin].pin_state = value;
505 		break;
506 	case GPIOATTACH:
507 		if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
508 		    NULL, NULL, NULL, NULL))
509 			return EPERM;
510 
511 		attach = (struct gpio_attach *)data;
512 
513 		/* do not try to attach if the pins are already mapped */
514 		if (!gpio_pin_can_map(sc, attach->ga_offset, attach->ga_mask))
515 			return EBUSY;
516 
517 		ga.ga_gpio = sc;
518 		ga.ga_dvname = attach->ga_dvname;
519 		ga.ga_offset = attach->ga_offset;
520 		ga.ga_mask = attach->ga_mask;
521 		DPRINTF(("%s: attach %s with offset %d and mask 0x%02x\n",
522 		    device_xname(sc->sc_dev), ga.ga_dvname, ga.ga_offset,
523 		    ga.ga_mask));
524 
525 		locs[GPIOCF_OFFSET] = ga.ga_offset;
526 		locs[GPIOCF_MASK] = ga.ga_mask;
527 
528 		cf = config_search_loc(NULL, sc->sc_dev, "gpio", locs, &ga);
529 		if (cf != NULL) {
530 			dv = config_attach_loc(sc->sc_dev, cf, locs, &ga,
531 			    gpiobus_print);
532 			if (dv != NULL) {
533 				gdev = kmem_alloc(sizeof(struct gpio_dev),
534 				    KM_SLEEP);
535 				gdev->sc_dev = dv;
536 				LIST_INSERT_HEAD(&sc->sc_devs, gdev, sc_next);
537 			} else
538 				return EINVAL;
539 		} else
540 			return EINVAL;
541 		break;
542 	case GPIODETACH:
543 		if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
544 		    NULL, NULL, NULL, NULL))
545 			return EPERM;
546 
547 		attach = (struct gpio_attach *)data;
548 		LIST_FOREACH(gdev, &sc->sc_devs, sc_next) {
549 			if (strcmp(device_xname(gdev->sc_dev),
550 			    attach->ga_dvname) == 0) {
551 				if (config_detach(gdev->sc_dev, 0) == 0) {
552 					LIST_REMOVE(gdev, sc_next);
553 					kmem_free(gdev,
554 					    sizeof(struct gpio_dev));
555 					return 0;
556 				}
557 				break;
558 			}
559 		}
560 		return EINVAL;
561 		break;
562 	case GPIOSET:
563 		if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
564 		    NULL, NULL, NULL, NULL))
565 			return EPERM;
566 
567 		set = (struct gpio_set *)data;
568 
569 		if (set->gp_name[0] != '\0') {
570 			pin = gpio_pinbyname(sc, set->gp_name);
571 			if (pin == -1)
572 				return EINVAL;
573 		} else
574 			pin = set->gp_pin;
575 		if (pin < 0 || pin >= sc->sc_npins)
576 			return EINVAL;
577 		flags = set->gp_flags;
578 
579 		/* check that the controller supports all requested flags */
580 		if ((flags & sc->sc_pins[pin].pin_caps) != flags)
581 			return ENODEV;
582 		flags = set->gp_flags | GPIO_PIN_SET;
583 
584 		set->gp_caps = sc->sc_pins[pin].pin_caps;
585 		/* return old value */
586 		set->gp_flags = sc->sc_pins[pin].pin_flags;
587 		if (flags > 0) {
588 			gpiobus_pin_ctl(gc, pin, flags);
589 			/* update current value */
590 			sc->sc_pins[pin].pin_flags = flags;
591 		}
592 
593 		/* rename pin or new pin? */
594 		if (set->gp_name2[0] != '\0') {
595 			struct gpio_name *gnm;
596 
597 			gnm = NULL;
598 			LIST_FOREACH(nm, &sc->sc_names, gp_next) {
599 				if (!strcmp(nm->gp_name, set->gp_name2) &&
600 				    nm->gp_pin != pin)
601 					return EINVAL;	/* duplicate name */
602 				if (nm->gp_pin == pin)
603 					gnm = nm;
604 			}
605 			if (gnm != NULL)
606 				strlcpy(gnm->gp_name, set->gp_name2,
607 				    sizeof(gnm->gp_name));
608 			else  {
609 				nm = kmem_alloc(sizeof(struct gpio_name),
610 				    KM_SLEEP);
611 				strlcpy(nm->gp_name, set->gp_name2,
612 				    sizeof(nm->gp_name));
613 				nm->gp_pin = set->gp_pin;
614 				LIST_INSERT_HEAD(&sc->sc_names, nm, gp_next);
615 			}
616 		}
617 		break;
618 	case GPIOUNSET:
619 		if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
620 		    NULL, NULL, NULL, NULL))
621 			return EPERM;
622 
623 		set = (struct gpio_set *)data;
624 		if (set->gp_name[0] != '\0') {
625 			pin = gpio_pinbyname(sc, set->gp_name);
626 			if (pin == -1)
627 				return EINVAL;
628 		} else
629 			pin = set->gp_pin;
630 
631 		if (pin < 0 || pin >= sc->sc_npins)
632 			return EINVAL;
633 		if (sc->sc_pins[pin].pin_mapped)
634 			return EBUSY;
635 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET))
636 			return EINVAL;
637 
638 		LIST_FOREACH(nm, &sc->sc_names, gp_next) {
639 			if (nm->gp_pin == pin) {
640 				LIST_REMOVE(nm, gp_next);
641 				kmem_free(nm, sizeof(struct gpio_name));
642 				break;
643 			}
644 		}
645 		sc->sc_pins[pin].pin_flags &= ~GPIO_PIN_SET;
646 		break;
647 	default:
648 		/* Try the old API */
649 		DPRINTF(("%s: trying the old API\n", device_xname(sc->sc_dev)));
650 		return gpio_ioctl_oapi(sc, cmd, data, flag, cred);
651 	}
652 	return 0;
653 }
654 
655 int
656 gpio_ioctl_oapi(struct gpio_softc *sc, u_long cmd, void *data, int flag,
657     kauth_cred_t cred)
658 {
659 	gpio_chipset_tag_t gc;
660 	struct gpio_pin_op *op;
661 	struct gpio_pin_ctl *ctl;
662 	int pin, value, flags;
663 
664 	gc = sc->sc_gc;
665 
666 	switch (cmd) {
667 	case GPIOPINREAD:
668 		op = (struct gpio_pin_op *)data;
669 
670 		pin = op->gp_pin;
671 
672 		if (pin < 0 || pin >= sc->sc_npins)
673 			return EINVAL;
674 
675 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
676 		    kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
677 		    NULL, NULL, NULL, NULL))
678 			return EPERM;
679 
680 		/* return read value */
681 		op->gp_value = gpiobus_pin_read(gc, pin);
682 		break;
683 	case GPIOPINWRITE:
684 		if ((flag & FWRITE) == 0)
685 			return EBADF;
686 
687 		op = (struct gpio_pin_op *)data;
688 
689 		pin = op->gp_pin;
690 
691 		if (pin < 0 || pin >= sc->sc_npins)
692 			return EINVAL;
693 
694 		if (sc->sc_pins[pin].pin_mapped)
695 			return EBUSY;
696 
697 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
698 		    kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
699 		    NULL, NULL, NULL, NULL))
700 			return EPERM;
701 
702 		value = op->gp_value;
703 		if (value != GPIO_PIN_LOW && value != GPIO_PIN_HIGH)
704 			return EINVAL;
705 
706 		gpiobus_pin_write(gc, pin, value);
707 		/* return old value */
708 		op->gp_value = sc->sc_pins[pin].pin_state;
709 		/* update current value */
710 		sc->sc_pins[pin].pin_state = value;
711 		break;
712 	case GPIOPINTOGGLE:
713 		if ((flag & FWRITE) == 0)
714 			return EBADF;
715 
716 		op = (struct gpio_pin_op *)data;
717 
718 		pin = op->gp_pin;
719 
720 		if (pin < 0 || pin >= sc->sc_npins)
721 			return EINVAL;
722 
723 		if (sc->sc_pins[pin].pin_mapped)
724 			return EBUSY;
725 
726 		if (!(sc->sc_pins[pin].pin_flags & GPIO_PIN_SET) &&
727 		    kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
728 		    NULL, NULL, NULL, NULL))
729 			return EPERM;
730 
731 		value = (sc->sc_pins[pin].pin_state == GPIO_PIN_LOW ?
732 		    GPIO_PIN_HIGH : GPIO_PIN_LOW);
733 		gpiobus_pin_write(gc, pin, value);
734 		/* return old value */
735 		op->gp_value = sc->sc_pins[pin].pin_state;
736 		/* update current value */
737 		sc->sc_pins[pin].pin_state = value;
738 		break;
739 	case GPIOPINCTL:
740 		ctl = (struct gpio_pin_ctl *) data;
741 
742 		if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,
743 		    NULL, NULL, NULL, NULL))
744 			return EPERM;
745 
746 		pin = ctl->gp_pin;
747 
748 		if (pin < 0 || pin >= sc->sc_npins)
749 			return EINVAL;
750 		if (sc->sc_pins[pin].pin_mapped)
751 			return EBUSY;
752 		flags = ctl->gp_flags;
753 
754 		/* check that the controller supports all requested flags */
755 		if ((flags & sc->sc_pins[pin].pin_caps) != flags)
756 			return ENODEV;
757 
758 		ctl->gp_caps = sc->sc_pins[pin].pin_caps;
759 		/* return old value */
760 		ctl->gp_flags = sc->sc_pins[pin].pin_flags;
761 		if (flags > 0) {
762 			gpiobus_pin_ctl(gc, pin, flags);
763 			/* update current value */
764 			sc->sc_pins[pin].pin_flags = flags;
765 		}
766 		break;
767 	default:
768 		return ENOTTY;
769 	}
770 	return 0;
771 }
772