arm: make signal handlers work
[minix.git] / drivers / gpio / gpio.c
blob724b2664c991bba4278850276c10179b73ef7d23
1 /*
2 * GPIO driver. This driver acts as a file system to allow
3 * reading and toggling of GPIO's.
4 */
5 /* kernel headers */
6 #include <minix/driver.h>
7 #include <minix/drvlib.h>
8 #include <minix/vtreefs.h>
10 /* system headers */
11 #include <sys/stat.h>
12 #include <sys/queue.h>
14 /* usr headers */
15 #include <assert.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdarg.h>
19 #include <signal.h>
20 #include <unistd.h>
21 #include <string.h>
23 /* local headers */
24 #include "log.h"
25 #include "mmio.h"
26 #include "gpio.h"
28 /* used for logging */
29 static struct log log = {
30 .name = "gpio",
31 .log_level = LEVEL_INFO,
32 .log_func = default_log
35 #define GPIO_CB_READ 0
36 #define GPIO_CB_ON 1
37 #define GPIO_CB_OFF 2
39 /* The vtreefs library provides callback data when calling
40 * the read function of inode. gpio_cbdata is used here to
41 * map between inodes and gpio's. VTreeFS is read-only. to work
42 * around that issue for a single GPIO we create multiple virtual
43 * files that can be *read* to read the gpio value and power on
44 * and off the gpio.
46 struct gpio_cbdata
48 struct gpio *gpio; /* obtained from the driver */
49 int type; /* read=0/on=1/off=2 */
50 TAILQ_ENTRY(gpio_cbdata) next;
53 /* list of inodes used in this driver */
54 TAILQ_HEAD(gpio_cbdata_head, gpio_cbdata)
55 gpio_cbdata_list = TAILQ_HEAD_INITIALIZER(gpio_cbdata_list);
57 static struct gpio_driver drv;
59 /* Sane file stats for a directory */
60 static struct inode_stat default_file_stat = {
61 .mode = S_IFREG | 04,
62 .uid = 0,
63 .gid = 0,
64 .size = 0,
65 .dev = NO_DEV,
68 int
69 add_gpio_inode(char *name, int nr, int mode)
71 /* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
72 * set values as we don't support writing yet */
73 char tmpname[200];
74 struct gpio_cbdata *cb;
75 struct gpio *gpio;
77 /* claim and configure the gpio */
78 if (drv.claim("gpiofs", nr, &gpio)) {
79 log_warn(&log, "Failed to claim GPIO %d\n", nr);
80 return EIO;
82 assert(gpio != NULL);
84 if (drv.pin_mode(gpio, mode)) {
85 log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr,
86 mode);
87 return EIO;
90 /* read value */
91 cb = malloc(sizeof(struct gpio_cbdata));
92 if (cb == NULL) {
93 return ENOMEM;
95 memset(cb, 0, sizeof(*cb));
97 cb->type = GPIO_CB_READ;
98 cb->gpio = gpio;
100 snprintf(tmpname, 200, "%s", name);
101 add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0,
102 (cbdata_t) cb);
103 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
105 if (mode == GPIO_MODE_OUTPUT) {
106 /* if we configured the GPIO pin as output mode also create
107 * two additional files to turn on and off the GPIO. */
108 /* turn on */
109 cb = malloc(sizeof(struct gpio_cbdata));
110 if (cb == NULL) {
111 return ENOMEM;
113 memset(cb, 0, sizeof(*cb));
115 cb->type = GPIO_CB_ON;
116 cb->gpio = gpio;
118 snprintf(tmpname, 200, "%son", name);
119 add_inode(get_root_inode(), tmpname, NO_INDEX,
120 &default_file_stat, 0, (cbdata_t) cb);
121 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
123 /* turn off */
124 cb = malloc(sizeof(struct gpio_cbdata));
125 if (cb == NULL) {
126 return ENOMEM;
128 memset(cb, 0, sizeof(*cb));
130 cb->type = GPIO_CB_OFF;
131 cb->gpio = gpio;
133 snprintf(tmpname, 200, "%soff", name);
134 add_inode(get_root_inode(), tmpname, NO_INDEX,
135 &default_file_stat, 0, (cbdata_t) cb);
136 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
138 return OK;
141 static void
142 init_hook(void)
144 /* This hook will be called once, after VTreeFS has initialized. */
145 if (omap_gpio_init(&drv)) {
146 log_warn(&log, "Failed to init gpio driver\n");
148 add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT);
149 add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT);
150 add_gpio_inode("Button", 4, GPIO_MODE_INPUT);
152 #if 0
153 add_gpio_inode("input1", 139, GPIO_MODE_INPUT);
154 add_gpio_inode("input2", 144, GPIO_MODE_INPUT);
155 #endif
158 static int
159 read_hook
160 (struct inode *inode, off_t offset, char **ptr, size_t * len,
161 cbdata_t cbdata)
163 /* This hook will be called every time a regular file is read. We use
164 * it to dyanmically generate the contents of our file. */
165 static char data[26];
166 int value;
167 struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
168 assert(gpio_cbdata->gpio != NULL);
170 if (gpio_cbdata->type == GPIO_CB_ON) {
171 /* turn on */
172 if (drv.set(gpio_cbdata->gpio, 1)) {
173 *len = 0;
174 return EIO;
176 *len = 0;
177 return OK;
178 } else if (gpio_cbdata->type == GPIO_CB_OFF) {
179 /* turn off */
180 if (drv.set(gpio_cbdata->gpio, 0)) {
181 *len = 0;
182 return EIO;
184 *len = 0;
185 return OK;
188 /* reading */
189 if (drv.read(gpio_cbdata->gpio, &value)) {
190 *len = 0;
191 return EIO;
193 snprintf(data, 26, "%d\n", value);
195 /* If the offset is beyond the end of the string, return EOF. */
196 if (offset > strlen(data)) {
197 *len = 0;
199 return OK;
202 /* Otherwise, return a pointer into 'data'. If necessary, bound the
203 * returned length to the length of the rest of the string. Note that
204 * 'data' has to be static, because it will be used after this
205 * function returns. */
206 *ptr = data + offset;
208 if (*len > strlen(data) - offset)
209 *len = strlen(data) - offset;
211 return OK;
215 main(int argc, char **argv)
218 struct fs_hooks hooks;
219 struct inode_stat root_stat;
221 /* Set and apply the environment */
222 env_setargs(argc, argv);
224 /* fill in the hooks */
225 memset(&hooks, 0, sizeof(hooks));
226 hooks.init_hook = init_hook;
227 hooks.read_hook = read_hook;
229 root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
230 root_stat.uid = 0;
231 root_stat.gid = 0;
232 root_stat.size = 0;
233 root_stat.dev = NO_DEV;
235 /* limit the number of indexed entries */
236 start_vtreefs(&hooks, 10, &root_stat, 0);
238 return EXIT_SUCCESS;