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> 16 17 /* system headers */ 18 #include <sys/stat.h> 19 #include <sys/queue.h> 20 #include <sys/queue.h> 21 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> 30 31 /* local headers */ 32 33 /* used for logging */ 34 static struct log log = { 35 .name = "gpio", 36 .log_level = LEVEL_INFO, 37 .log_func = default_log 38 }; 39 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 44 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. 51 */ 52 struct gpio_cbdata 53 { 54 struct gpio *gpio; /* obtained from the driver */ 55 int type; /* read=0/on=1/off=2 */ 56 TAILQ_ENTRY(gpio_cbdata) next; 57 }; 58 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* */ 64 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, 72 }; 73 74 /* Buffer size for read requests */ 75 #define DATA_SIZE 26 76 77 int 78 add_gpio_inode(char *name, int nr, int mode) 79 { 80 /* Create 2 files nodes for "name" "nameon" and "nameoff" to read and 81 * set values as we don't support writing yet */ 82 char tmpname[200]; 83 struct gpio_cbdata *cb; 84 struct gpio *gpio; 85 86 /* claim and configure the gpio */ 87 if (gpio_claim("gpiofs", nr, &gpio)) { 88 log_warn(&log, "Failed to claim GPIO %d\n", nr); 89 return EIO; 90 } 91 assert(gpio != NULL); 92 93 if (gpio_pin_mode(gpio, mode)) { 94 log_warn(&log, "Failed to switch GPIO %d to mode %d\n", nr, 95 mode); 96 return EIO; 97 } 98 99 /* read value */ 100 cb = malloc(sizeof(struct gpio_cbdata)); 101 if (cb == NULL) { 102 return ENOMEM; 103 } 104 memset(cb, 0, sizeof(*cb)); 105 106 cb->type = GPIO_CB_READ; 107 cb->gpio = gpio; 108 109 snprintf(tmpname, 200, "%s", name); 110 add_inode(get_root_inode(), tmpname, NO_INDEX, &default_file_stat, 0, 111 (cbdata_t) cb); 112 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next); 113 114 if (mode == GPIO_MODE_OUTPUT) { 115 /* if we configured the GPIO pin as output mode also create 116 * two additional files to turn on and off the GPIO. */ 117 /* turn on */ 118 cb = malloc(sizeof(struct gpio_cbdata)); 119 if (cb == NULL) { 120 return ENOMEM; 121 } 122 memset(cb, 0, sizeof(*cb)); 123 124 cb->type = GPIO_CB_ON; 125 cb->gpio = gpio; 126 127 snprintf(tmpname, 200, "%sOn", name); 128 add_inode(get_root_inode(), tmpname, NO_INDEX, 129 &default_file_stat, 0, (cbdata_t) cb); 130 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next); 131 132 /* turn off */ 133 cb = malloc(sizeof(struct gpio_cbdata)); 134 if (cb == NULL) { 135 return ENOMEM; 136 } 137 memset(cb, 0, sizeof(*cb)); 138 139 cb->type = GPIO_CB_OFF; 140 cb->gpio = gpio; 141 142 snprintf(tmpname, 200, "%sOff", name); 143 add_inode(get_root_inode(), tmpname, NO_INDEX, 144 &default_file_stat, 0, (cbdata_t) cb); 145 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next); 146 } else { 147 /* read interrupt */ 148 cb = malloc(sizeof(struct gpio_cbdata)); 149 if (cb == NULL) { 150 return ENOMEM; 151 } 152 memset(cb, 0, sizeof(*cb)); 153 154 cb->type = GPIO_CB_INTR_READ; 155 cb->gpio = gpio; 156 157 snprintf(tmpname, 200, "%sIntr", name); 158 add_inode(get_root_inode(), tmpname, NO_INDEX, 159 &default_file_stat, 0, (cbdata_t) cb); 160 TAILQ_INSERT_HEAD(&gpio_cbdata_list, cb, next); 161 } 162 return OK; 163 } 164 165 static void 166 init_hook(void) 167 { 168 /* This hook will be called once, after VTreeFS has initialized. */ 169 if (gpio_init()) { 170 log_warn(&log, "Failed to init gpio driver\n"); 171 } 172 173 struct machine machine ; 174 sys_getmachine(&machine); 175 176 if (BOARD_IS_BBXM(machine.board_id)){ 177 add_gpio_inode("USR0", 149, GPIO_MODE_OUTPUT); 178 add_gpio_inode("USR1", 150, GPIO_MODE_OUTPUT); 179 add_gpio_inode("Button", 4, GPIO_MODE_INPUT); 180 181 /* configure GPIO_144 to be exported */ 182 sys_padconf(CONTROL_PADCONF_UART2_CTS, 0x0000ffff, 183 PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN | 184 PADCONF_INPUT_ENABLE(1)); 185 sys_padconf(CONTROL_PADCONF_MMC2_DAT6, 0xffff0000, 186 (PADCONF_MUXMODE(4) | PADCONF_PULL_MODE_PD_EN | 187 PADCONF_INPUT_ENABLE(1)) << 16); 188 189 /* Added for demo purposes */ 190 add_gpio_inode("BigRedButton", 144, GPIO_MODE_INPUT); 191 add_gpio_inode("BigRedButtonLed", 139, GPIO_MODE_OUTPUT); 192 } else if ( BOARD_IS_BB(machine.board_id)){ 193 194 /* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */ 195 196 sys_padconf(CONTROL_CONF_MCASP0_FSR, 0xffffffff, 197 (CONTROL_CONF_PUTYPESEL | CONTROL_CONF_MUXMODE(7))); 198 199 add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT); 200 201 /* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */ 202 203 /* assumes external pull-up resistor (10K) */ 204 sys_padconf(CONTROL_CONF_SPI0_D0, 0xffffffff, (CONTROL_CONF_RXACTIVE | 205 CONTROL_CONF_PUDEN | CONTROL_CONF_MUXMODE(7))); 206 207 add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT); 208 209 } 210 } 211 212 static ssize_t 213 read_hook 214 (struct inode *inode, char *ptr, size_t len, off_t offset, cbdata_t cbdata) 215 { 216 /* This hook will be called every time a regular file is read. We use 217 * it to dyanmically generate the contents of our file. */ 218 int value; 219 struct gpio_cbdata *gpio_cbdata = (struct gpio_cbdata *) cbdata; 220 assert(gpio_cbdata->gpio != NULL); 221 222 if (gpio_cbdata->type == GPIO_CB_ON 223 || gpio_cbdata->type == GPIO_CB_OFF) { 224 /* turn on or off */ 225 if (gpio_set(gpio_cbdata->gpio, 226 (gpio_cbdata->type == GPIO_CB_ON) ? 1 : 0)) 227 return EIO; 228 return 0; 229 } 230 231 if (gpio_cbdata->type == GPIO_CB_INTR_READ) { 232 /* reading interrupt */ 233 if (gpio_intr_read(gpio_cbdata->gpio, &value)) 234 return EIO; 235 } else { 236 /* reading */ 237 if (gpio_read(gpio_cbdata->gpio, &value)) 238 return EIO; 239 } 240 snprintf(ptr, DATA_SIZE, "%d\n", value); 241 len = strlen(ptr); 242 243 /* If the offset is at or beyond the end of the string, return EOF. */ 244 if (offset >= len) 245 return 0; 246 247 /* Otherwise, we may have to move the data to the start of ptr. */ 248 if (offset > 0) { 249 len -= offset; 250 251 memmove(ptr, &ptr[offset], len); 252 } 253 254 /* Return the resulting length. */ 255 return len; 256 } 257 258 static void 259 message_hook(message * m, int __unused ipc_status) 260 { 261 gpio_intr_message(m); 262 } 263 264 int 265 main(int argc, char **argv) 266 { 267 268 struct fs_hooks hooks; 269 struct inode_stat root_stat; 270 271 /* Set and apply the environment */ 272 env_setargs(argc, argv); 273 274 /* fill in the hooks */ 275 memset(&hooks, 0, sizeof(hooks)); 276 hooks.init_hook = init_hook; 277 hooks.read_hook = read_hook; 278 hooks.message_hook = message_hook; 279 280 root_stat.mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; 281 root_stat.uid = 0; 282 root_stat.gid = 0; 283 root_stat.size = 0; 284 root_stat.dev = NO_DEV; 285 286 /* run VTreeFS */ 287 run_vtreefs(&hooks, 30, 0, &root_stat, 0, DATA_SIZE); 288 289 return EXIT_SUCCESS; 290 } 291