1/*-
2 * Copyright (c) 2013-2014 Rui Paulo <rpaulo@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26#include <stdio.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32
33#include <sys/ioctl.h>
34#include <sys/types.h>
35
36#include <libgpio.h>
37
38gpio_handle_t
39gpio_open(unsigned int unit)
40{
41	char device[16];
42
43	snprintf(device, sizeof(device), "/dev/gpioc%u", unit);
44
45	return (gpio_open_device(device));
46}
47
48gpio_handle_t
49gpio_open_device(const char *device)
50{
51	int fd, maxpins;
52	int serr;
53
54	fd = open(device, O_RDONLY);
55	if (fd < 0)
56		return (GPIO_INVALID_HANDLE);
57	/*
58	 * Check whether a simple ioctl works.
59	 */
60	if (ioctl(fd, GPIOMAXPIN, &maxpins) < 0) {
61		serr = errno;
62		close(fd);
63		errno = serr;
64		return (GPIO_INVALID_HANDLE);
65	}
66
67	return (fd);
68}
69
70void
71gpio_close(gpio_handle_t handle)
72{
73	close(handle);
74}
75
76int
77gpio_pin_list(gpio_handle_t handle, gpio_config_t **pcfgs)
78{
79	int maxpins, i;
80	gpio_config_t *cfgs;
81
82	*pcfgs = NULL;
83	if (ioctl(handle, GPIOMAXPIN, &maxpins) < 0)
84		return (-1);
85	/* Reasonable values. */
86	if (maxpins < 0 || maxpins > 4096) {
87		errno = EINVAL;
88		return (-1);
89	}
90	cfgs = calloc(maxpins + 1, sizeof(*cfgs));
91	if (cfgs == NULL)
92		return (-1);
93	for (i = 0; i <= maxpins; i++) {
94		cfgs[i].g_pin = i;
95		gpio_pin_config(handle, &cfgs[i]);
96	}
97	*pcfgs = cfgs;
98
99	return (maxpins);
100}
101
102int
103gpio_pin_config(gpio_handle_t handle, gpio_config_t *cfg)
104{
105	struct gpio_pin gppin;
106
107	if (cfg == NULL)
108		return (-1);
109	gppin.gp_pin = cfg->g_pin;
110	if (ioctl(handle, GPIOGETCONFIG, &gppin) < 0)
111		return (-1);
112	strlcpy(cfg->g_name, gppin.gp_name, GPIOMAXNAME);
113	cfg->g_caps = gppin.gp_caps;
114	cfg->g_flags = gppin.gp_flags;
115
116	return (0);
117}
118
119int
120gpio_pin_set_name(gpio_handle_t handle, gpio_pin_t pin, char *name)
121{
122	struct gpio_pin gppin;
123
124	if (name == NULL)
125		return (-1);
126	bzero(&gppin, sizeof(gppin));
127	gppin.gp_pin = pin;
128	strlcpy(gppin.gp_name, name, GPIOMAXNAME);
129	if (ioctl(handle, GPIOSETNAME, &gppin) < 0)
130		return (-1);
131
132	return (0);
133}
134
135int
136gpio_pin_set_flags(gpio_handle_t handle, gpio_config_t *cfg)
137{
138	struct gpio_pin gppin;
139
140	if (cfg == NULL)
141		return (-1);
142	gppin.gp_pin = cfg->g_pin;
143	gppin.gp_flags = cfg->g_flags;
144	if (ioctl(handle, GPIOSETCONFIG, &gppin) < 0)
145		return (-1);
146
147	return (0);
148}
149
150gpio_value_t
151gpio_pin_get(gpio_handle_t handle, gpio_pin_t pin)
152{
153	struct gpio_req gpreq;
154
155	bzero(&gpreq, sizeof(gpreq));
156	gpreq.gp_pin = pin;
157	if (ioctl(handle, GPIOGET, &gpreq) < 0)
158		return (GPIO_VALUE_INVALID);
159
160	return (gpreq.gp_value);
161}
162
163int
164gpio_pin_set(gpio_handle_t handle, gpio_pin_t pin, gpio_value_t value)
165{
166	struct gpio_req gpreq;
167
168	if (value == GPIO_VALUE_INVALID)
169		return (-1);
170	bzero(&gpreq, sizeof(gpreq));
171	gpreq.gp_pin = pin;
172	gpreq.gp_value = value;
173	if (ioctl(handle, GPIOSET, &gpreq) < 0)
174		return (-1);
175
176	return (0);
177}
178
179int
180gpio_pin_toggle(gpio_handle_t handle, gpio_pin_t pin)
181{
182	struct gpio_req gpreq;
183
184	bzero(&gpreq, sizeof(gpreq));
185	gpreq.gp_pin = pin;
186	if (ioctl(handle, GPIOTOGGLE, &gpreq) < 0)
187		return (-1);
188
189	return (0);
190}
191
192int
193gpio_pin_low(gpio_handle_t handle, gpio_pin_t pin)
194{
195	return (gpio_pin_set(handle, pin, GPIO_VALUE_LOW));
196}
197
198int
199gpio_pin_high(gpio_handle_t handle, gpio_pin_t pin)
200{
201	return (gpio_pin_set(handle, pin, GPIO_VALUE_HIGH));
202}
203
204static int
205gpio_pin_set_flag(gpio_handle_t handle, gpio_pin_t pin, uint32_t flag)
206{
207	gpio_config_t cfg;
208
209	bzero(&cfg, sizeof(cfg));
210	cfg.g_pin = pin;
211	if (gpio_pin_config(handle, &cfg) < 0)
212		return (-1);
213	cfg.g_flags = flag;
214
215	return (gpio_pin_set_flags(handle, &cfg));
216}
217
218int
219gpio_pin_input(gpio_handle_t handle, gpio_pin_t pin)
220{
221	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INPUT));
222}
223
224int
225gpio_pin_output(gpio_handle_t handle, gpio_pin_t pin)
226{
227	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_OUTPUT));
228}
229
230int
231gpio_pin_opendrain(gpio_handle_t handle, gpio_pin_t pin)
232{
233	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_OPENDRAIN));
234}
235
236int
237gpio_pin_pushpull(gpio_handle_t handle, gpio_pin_t pin)
238{
239	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PUSHPULL));
240}
241
242int
243gpio_pin_tristate(gpio_handle_t handle, gpio_pin_t pin)
244{
245	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_TRISTATE));
246}
247
248int
249gpio_pin_pullup(gpio_handle_t handle, gpio_pin_t pin)
250{
251	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULLUP));
252}
253
254int
255gpio_pin_pulldown(gpio_handle_t handle, gpio_pin_t pin)
256{
257	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULLDOWN));
258}
259
260int
261gpio_pin_invin(gpio_handle_t handle, gpio_pin_t pin)
262{
263	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INVIN));
264}
265
266int
267gpio_pin_invout(gpio_handle_t handle, gpio_pin_t pin)
268{
269	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_INVOUT));
270}
271
272int
273gpio_pin_pulsate(gpio_handle_t handle, gpio_pin_t pin)
274{
275	return (gpio_pin_set_flag(handle, pin, GPIO_PIN_PULSATE));
276}
277