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 int 75 add_gpio_inode(char *name, int nr, int mode) 76 { 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; 82 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; 87 } 88 assert(gpio != NULL); 89 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; 94 } 95 96 /* read value */ 97 cb = malloc(sizeof(struct gpio_cbdata)); 98 if (cb == NULL) { 99 return ENOMEM; 100 } 101 memset(cb, 0, sizeof(*cb)); 102 103 cb->type = GPIO_CB_READ; 104 cb->gpio = gpio; 105 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); 110 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; 118 } 119 memset(cb, 0, sizeof(*cb)); 120 121 cb->type = GPIO_CB_ON; 122 cb->gpio = gpio; 123 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); 128 129 /* turn off */ 130 cb = malloc(sizeof(struct gpio_cbdata)); 131 if (cb == NULL) { 132 return ENOMEM; 133 } 134 memset(cb, 0, sizeof(*cb)); 135 136 cb->type = GPIO_CB_OFF; 137 cb->gpio = gpio; 138 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; 148 } 149 memset(cb, 0, sizeof(*cb)); 150 151 cb->type = GPIO_CB_INTR_READ; 152 cb->gpio = gpio; 153 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); 158 } 159 return OK; 160 } 161 162 static void 163 init_hook(void) 164 { 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"); 168 } 169 170 struct machine machine ; 171 sys_getmachine(&machine); 172 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); 177 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); 185 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)){ 190 191 /* Export GPIO3_19 (P9-27 on BBB) output as LCD_EN */ 192 193 sys_padconf(CONTROL_CONF_MCASP0_FSR, 0xffffffff, 194 (CONTROL_CONF_PUTYPESEL | CONTROL_CONF_MUXMODE(7))); 195 196 add_gpio_inode("LCD_EN", (32 * 3) + 19, GPIO_MODE_OUTPUT); 197 198 /* Export GPIO1_17 (P9-23 on BBB) input as RIGHT */ 199 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))); 203 204 add_gpio_inode("RIGHT", (32 * 1) + 17, GPIO_MODE_INPUT); 205 206 } 207 } 208 209 static int 210 read_hook 211 (struct inode *inode, off_t offset, char **ptr, size_t * len, 212 cbdata_t cbdata) 213 { 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); 220 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; 228 } 229 *len = 0; 230 return OK; 231 } 232 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; 238 } 239 } else { 240 /* reading */ 241 if (gpio_read(gpio_cbdata->gpio, &value)) { 242 *len = 0; 243 return EIO; 244 } 245 } 246 snprintf(data, 26, "%d\n", value); 247 248 /* If the offset is beyond the end of the string, return EOF. */ 249 if (offset > strlen(data)) { 250 *len = 0; 251 252 return OK; 253 } 254 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; 260 261 if (*len > strlen(data) - offset) 262 *len = strlen(data) - offset; 263 264 return OK; 265 } 266 267 static int 268 message_hook(message * m) 269 { 270 gpio_intr_message(m); 271 return OK; 272 } 273 274 int 275 main(int argc, char **argv) 276 { 277 278 struct fs_hooks hooks; 279 struct inode_stat root_stat; 280 281 /* Set and apply the environment */ 282 env_setargs(argc, argv); 283 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; 289 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; 295 296 /* limit the number of indexed entries */ 297 start_vtreefs(&hooks, 30, &root_stat, 0); 298 299 return EXIT_SUCCESS; 300 } 301