<sys/socket.h>: turn off MSG_NOSIGNAL
[minix3.git] / drivers / gpio / gpio.c
blobb9da4e522a0c34648cd65f84e88c58ec3685567b
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>
9 #include <minix/syslib.h>
10 #include <minix/log.h>
11 #include <minix/mmio.h>
12 #include <minix/gpio.h>
13 #include <minix/padconf.h>
14 #include <minix/type.h>
15 #include <minix/board.h>
17 /* system headers */
18 #include <sys/stat.h>
19 #include <sys/queue.h>
20 #include <sys/queue.h>
22 /* usr headers */
23 #include <assert.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <signal.h>
28 #include <unistd.h>
29 #include <string.h>
31 /* local headers */
33 /* used for logging */
34 static struct log log = {
35 .name = "gpio",
36 .log_level = LEVEL_INFO,
37 .log_func = default_log
40 #define GPIO_CB_READ 0
41 #define GPIO_CB_INTR_READ 1
42 #define GPIO_CB_ON 2
43 #define GPIO_CB_OFF 3
45 /* The vtreefs library provides callback data when calling
46 * the read function of inode. gpio_cbdata is used here to
47 * map between inodes and gpio's. VTreeFS is read-only. to work
48 * around that issue for a single GPIO we create multiple virtual
49 * files that can be *read* to read the gpio value and power on
50 * and off the gpio.
52 struct gpio_cbdata
54 struct gpio *gpio; /* obtained from the driver */
55 int type; /* read=0/on=1/off=2 */
56 TAILQ_ENTRY(gpio_cbdata) next;
59 /* list of inodes used in this driver */
60 /* *INDENT-OFF* */
61 TAILQ_HEAD(gpio_cbdata_head, gpio_cbdata)
62 gpio_cbdata_list = TAILQ_HEAD_INITIALIZER(gpio_cbdata_list);
63 /* *INDENT-ON* */
65 /* Sane file stats for a directory */
66 static struct inode_stat default_file_stat = {
67 .mode = S_IFREG | 04,
68 .uid = 0,
69 .gid = 0,
70 .size = 0,
71 .dev = NO_DEV,
74 int
75 add_gpio_inode(char *name, int nr, int mode)
77 /* Create 2 files nodes for "name" "nameon" and "nameoff" to read and
78 * set values as we don't support writing yet */
79 char tmpname[200];
80 struct gpio_cbdata *cb;
81 struct gpio *gpio;
83 /* claim and configure the gpio */
84 if (gpio_claim("gpiofs", nr, &gpio)) {
85 log_warn(&log, "Failed to claim GPIO %d\n", nr);
86 return EIO;
88 assert(gpio != NULL);
90 if (gpio_pin_mode(gpio, mode)) {
91 log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr,
92 mode);
93 return EIO;
96 /* read value */
97 cb = malloc(sizeof(struct gpio_cbdata));
98 if (cb == NULL) {
99 return ENOMEM;
101 memset(cb, 0, sizeof(*cb));
103 cb->type = GPIO_CB_READ;
104 cb->gpio = gpio;
106 snprintf(tmpname, 200, "%s", name);
107 add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0,
108 (cbdata_t) cb);
109 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
111 if (mode == GPIO_MODE_OUTPUT) {
112 /* if we configured the GPIO pin as output mode also create
113 * two additional files to turn on and off the GPIO. */
114 /* turn on */
115 cb = malloc(sizeof(struct gpio_cbdata));
116 if (cb == NULL) {
117 return ENOMEM;
119 memset(cb, 0, sizeof(*cb));
121 cb->type = GPIO_CB_ON;
122 cb->gpio = gpio;
124 snprintf(tmpname, 200, "%sOn", name);
125 add_inode(get_root_inode(), tmpname, NO_INDEX,
126 &default_file_stat, 0, (cbdata_t) cb);
127 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
129 /* turn off */
130 cb = malloc(sizeof(struct gpio_cbdata));
131 if (cb == NULL) {
132 return ENOMEM;
134 memset(cb, 0, sizeof(*cb));
136 cb->type = GPIO_CB_OFF;
137 cb->gpio = gpio;
139 snprintf(tmpname, 200, "%sOff", name);
140 add_inode(get_root_inode(), tmpname, NO_INDEX,
141 &default_file_stat, 0, (cbdata_t) cb);
142 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
143 } else {
144 /* read interrupt */
145 cb = malloc(sizeof(struct gpio_cbdata));
146 if (cb == NULL) {
147 return ENOMEM;
149 memset(cb, 0, sizeof(*cb));
151 cb->type = GPIO_CB_INTR_READ;
152 cb->gpio = gpio;
154 snprintf(tmpname, 200, "%sIntr", name);
155 add_inode(get_root_inode(), tmpname, NO_INDEX,
156 &default_file_stat, 0, (cbdata_t) cb);
157 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next);
159 return OK;
162 static void
163 init_hook(void)
165 /* This hook will be called once, after VTreeFS has initialized. */
166 if (gpio_init()) {
167 log_warn(&log, "Failed to init gpio driver\n");
170 struct machine machine ;
171 sys_getmachine(&machine);
173 if (BOARD_IS_BBXM(machine.board_id)){
174 add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT);
175 add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT);
176 add_gpio_inode("Button", 4, GPIO_MODE_INPUT);
178 /* configure GPIO_144 to be exported */
179 sys_padconf(CONTROL_PADCONF_UART2_CTS, 0x0000ffff,
180 PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
181 PADCONF_INPUT_ENABLE(1));
182 sys_padconf(CONTROL_PADCONF_MMC2_DAT6, 0xffff0000,
183 (PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN |
184 PADCONF_INPUT_ENABLE(1)) << 16);
186 /* Added for demo purposes */
187 add_gpio_inode("BigRedButton", 144, GPIO_MODE_INPUT);
188 add_gpio_inode("BigRedButtonLed", 139, GPIO_MODE_OUTPUT);
189 } else if ( BOARD_IS_BB(machine.board_id)){
191 /* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */
193 sys_padconf(CONTROL_CONF_MCASP0_FSR, 0xffffffff,
194 (CONTROL_CONF_PUTYPESEL | CONTROL_CONF_MUXMODE(7)));
196 add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT);
198 /* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */
200 /* assumes external pull-up resistor (10K) */
201 sys_padconf(CONTROL_CONF_SPI0_D0, 0xffffffff, (CONTROL_CONF_RXACTIVE |
202 CONTROL_CONF_PUDEN | CONTROL_CONF_MUXMODE(7)));
204 add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT);
209 static int
210 read_hook
211 (struct inode *inode, off_t offset, char **ptr, size_t * len,
212 cbdata_t cbdata)
214 /* This hook will be called every time a regular file is read. We use
215 * it to dyanmically generate the contents of our file. */
216 static char data[26];
217 int value;
218 struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata;
219 assert(gpio_cbdata->gpio != NULL);
221 if (gpio_cbdata->type == GPIO_CB_ON
222 || gpio_cbdata->type == GPIO_CB_OFF) {
223 /* turn on or off */
224 if (gpio_set(gpio_cbdata->gpio,
225 (gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0)) {
226 *len = 0;
227 return EIO;
229 *len = 0;
230 return OK;
233 if (gpio_cbdata->type == GPIO_CB_INTR_READ) {
234 /* reading interrupt */
235 if (gpio_intr_read(gpio_cbdata->gpio, &value)) {
236 *len = 0;
237 return EIO;
239 } else {
240 /* reading */
241 if (gpio_read(gpio_cbdata->gpio, &value)) {
242 *len = 0;
243 return EIO;
246 snprintf(data, 26, "%d\n", value);
248 /* If the offset is beyond the end of the string, return EOF. */
249 if (offset > strlen(data)) {
250 *len = 0;
252 return OK;
255 /* Otherwise, return a pointer into 'data'. If necessary, bound the
256 * returned length to the length of the rest of the string. Note that
257 * 'data' has to be static, because it will be used after this
258 * function returns. */
259 *ptr = data + offset;
261 if (*len > strlen(data) - offset)
262 *len = strlen(data) - offset;
264 return OK;
267 static int
268 message_hook(message * m)
270 gpio_intr_message(m);
271 return OK;
275 main(int argc, char **argv)
278 struct fs_hooks hooks;
279 struct inode_stat root_stat;
281 /* Set and apply the environment */
282 env_setargs(argc, argv);
284 /* fill in the hooks */
285 memset(&hooks, 0, sizeof(hooks));
286 hooks.init_hook = init_hook;
287 hooks.read_hook = read_hook;
288 hooks.message_hook = message_hook;
290 root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
291 root_stat.uid = 0;
292 root_stat.gid = 0;
293 root_stat.size = 0;
294 root_stat.dev = NO_DEV;
296 /* limit the number of indexed entries */
297 start_vtreefs(&hooks, 30, &root_stat, 0);
299 return EXIT_SUCCESS;