/* * Copyright (c) 2010-2012 Douglas Gilbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /***************************************************************** * gpio_ioctl.c * * Utility for testing AT91SAM9G20 (G20) and AT91SAM9G45 (G45) GPIO pins. * Can scan, read, set and toggle GPIO lines. The G20 has 3 banks of 32 * pins: PA0-PA31, PB0-PB31 and PC0-PC31 while the G45 has 2 additional * banks: PD0-31 and PE0-31 . Some GPIO pins may be committed to other * uses (e.g. on the FoxG20 PB4 is TXD0 (part of a serial port)). * * Uses ioctls defined the the gpio_dev driver and assumes the /dev/gpio * char device is available. If use may need to do 'modprobe gpio_dev' * and, if necessary mknod to create a /dev/gpio with the correct major * number (which the kernel allocates dynamically). * ****************************************************/ #define _POSIX_C_SOURCE 199309 #include #include #include #include #include #include #include #include #include #include #include /* If the gpio_dev driver is in the kernel source (and possibly * the glibc) headers than '#define ' will * work. To lessen the pain if the header is not there, use a * copy of it in this directory. */ #include "gpio_dev.h" /* Local directory possibly */ static const char * version_str = "1.06 20121114"; #define DEF_NUM_TOGGLE 1000000 #define GPIO_BANKS_G20 3 /* PA0-31,PB0-31 and PC0-31 */ #define GPIO_BANKS_G45 5 /* as for G20 plus PD0-31 and PE0-31 */ #define LINES_PER_BANK 32 static int verbose; static struct timespec delay_req = { .tv_sec = 0, .tv_nsec = 0 }; static void usage(void) { fprintf(stderr, "Usage: " "gpio_ioctl [-b ] [-d ] [-e] [-f] [-h] [-n ]\n" " [-p ] [-r] [-s 0|1] [-S] [-t] [-u] " "[-v] [-V]\n" " where:\n" " -b bit number within port (0 to 31)\n" " -d delay between toggling gpio levels (def: 0)\n" " -e enumerate pin names with corresponding " "kernel pin\n" " numbers (once for G20, twice for G45)\n" " -f Use GPIO_SET and GPIO_CLEAR to toggle (default " "use\n" " GPIO_DIR_IN and GPIO_DIR_OUT (rely on pull-up " "for high))\n" " Used twice: toggle with GPIO_DIR_HIGH and " "GPIO_DIR_OUT\n" " -h print usage message\n" " -n number of cycles to toggle gpio line (def: " "1000000)\n" " -p port bank ('A' to 'E') or gpio kernel line " "number\n" " -r read gpio line and send value to stdout\n" " used twice: exit value 0 for low, 1 for " "high\n" " -s 0|1 set GPIO line to 0 (low) or 1 (high)\n" " -S scan all pins for availability\n" " use once for G20, twice for G45\n" " -t toggle gpio line times\n" " -u unexport gpio line (with GPIO_FREE)\n" " -v increase verbosity (multiple times for more)\n" " -V print version string then exit\n\n" #if GPIO_SPARE1 " -z call GPIO_SPARE1 ioctl\n" " twice: call GPIO_SPARE2; thrice: call " "GPIO_SPARE3\n" #endif "AT91SAM9G20 (and G45) GPIO test program. Uses gpio_dev ioctl " "interface.\n" "Can scan ('-S'), set and read lines. Can toggle line ('-t') " " times\nwith delay prior to each transition.\n"); } int main(int argc, char ** argv) { int opt, k, j, n, num, res2, fd, avail; int delay = 0; int enumerate = 0; int force = 0; int knum = -1; int num_toggle = DEF_NUM_TOGGLE; int read_val = 0; int scan = 0; int state = -1; int toggle = 0; int unexport = 0; #if GPIO_SPARE1 int do_zulu = 0; #endif int need_free = 0; int res = 0; int bit_num = -1; char ch; char bank = '\0'; while ((opt = getopt(argc, argv, "b:d:efhn:p:rs:StuvVz")) != -1) { switch (opt) { case 'b': k = atoi(optarg); if ((k < 0) || (k > 31)) { fprintf(stderr, "'-b' expects a bit number from 0 to 31\n"); exit(EXIT_FAILURE); } bit_num = k; break; case 'd': k = atoi(optarg); if (k < 0) { fprintf(stderr, "'-d' expects a delay in microseconds from " "0 to 2147483647\n"); exit(EXIT_FAILURE); } delay_req.tv_sec = k / 1000000; /* seconds component */ delay_req.tv_nsec = (k % 1000000) * 1000; /* nanoseconds */ delay = (delay_req.tv_sec || delay_req.tv_nsec); break; case 'e': ++enumerate; break; case 'f': ++force; break; case 'h': usage(); exit(EXIT_SUCCESS); break; case 'n': num_toggle = atoi(optarg); break; case 'p': if (isalpha(*optarg)) { ch = toupper(*optarg); if ((ch >= 'A') && (ch <= 'E')) bank = ch; else { fprintf(stderr, "'-p' expects a letter ('A' to 'E')\n"); exit(EXIT_FAILURE); } } else if (isdigit(*optarg)) { k = atoi(optarg); if ((k < 32) || (k > 511)) { fprintf(stderr, "'-p' expects a letter or a number " "32 or greater\n"); exit(EXIT_FAILURE); } knum = k; } else { fprintf(stderr, "'-p' expects a letter ('A' to 'E') or " "a number\n"); exit(EXIT_FAILURE); } break; case 'r': ++read_val; break; case 's': state = atoi(optarg); if ((state < 0) || (state > 1)) { fprintf(stderr, "'-s' expects '0' or '1'\n"); exit(EXIT_FAILURE); } break; case 'S': ++scan; break; case 't': ++toggle; break; case 'u': ++unexport; break; case 'v': ++verbose; break; case 'V': printf("%s\n", version_str); exit(EXIT_SUCCESS); #if GPIO_SPARE1 case 'z': ++do_zulu; break; #endif default: /* '?' */ usage(); exit(EXIT_FAILURE); } } if (optind < argc) { if (optind < argc) { for (; optind < argc; ++optind) fprintf(stderr, "Unexpected extra argument: %s\n", argv[optind]); usage(); exit(EXIT_FAILURE); } } if (enumerate) { num = (1 == enumerate) ? GPIO_BANKS_G20 : GPIO_BANKS_G45; for (k = 0; k < LINES_PER_BANK; ++k) { for (j = 0; j < num; ++j) { n = ((j + 1) * 32) + k; printf("%sP%c%d: %d ", (j ? "\t" : ""), 'A' + j, k, n); } printf("\n"); } return 0; } if ((knum >= 0) || (scan)) { if (bit_num >= 0) { fprintf(stderr, "Give either '-p ' or ('-b ' and " "'-p ') but not both\n"); exit(EXIT_FAILURE); } } else if (bank) { if (bit_num < 0) { fprintf(stderr, "If '-p ' given then also need " "'-b '\n"); exit(EXIT_FAILURE); } else knum = ((bank + 1 - 'A') * 32) + bit_num; } else { fprintf(stderr, "Need to give gpio line with '-p ' and/or " "'-b '\n"); usage(); exit(EXIT_FAILURE); } #if GPIO_SPARE1 if (0 == do_zulu) { #endif if (! (scan || toggle || unexport || read_val || (state >= 0))) { fprintf(stderr, "Expect at least '-r', '-s', '-S', '-t' or '-u' " "option, use '-h' for usage\n"); usage(); exit(EXIT_FAILURE); } if (read_val && (toggle || scan || (state >= 0))) { fprintf(stderr, "Can't have '-r' with '-s', -S' or '-t'\n"); usage(); exit(EXIT_FAILURE); } #if GPIO_SPARE1 } #endif fd = open("/dev/gpio", O_RDWR); if (fd < 0) { fprintf(stderr, "open /dev/gpio failed: %s\n", strerror(errno)); res = 1; goto the_end; } #if GPIO_SPARE1 if (do_zulu) { if ((res = ioctl(fd, GPIO_REQUEST, knum))) { fprintf(stderr, "ioctl(GPIO_REQUEST, %d) ret=%d err: %s\n", knum, res, strerror(errno)); goto the_end; } need_free = 1; if (1 == do_zulu) { if ((res = ioctl(fd, GPIO_SPARE1, knum))) fprintf(stderr, "ioctl(GPIO_SPARE1) on knum=%d ret=%d err: " "%s\n", knum, res, strerror(errno)); goto the_end; } else if (2 == do_zulu) { if ((res = ioctl(fd, GPIO_SPARE2, knum))) fprintf(stderr, "ioctl(GPIO_SPARE2) on knum=%d ret=%d err: " "%s\n", knum, res, strerror(errno)); goto the_end; } else { if ((res = ioctl(fd, GPIO_SPARE3, knum))) fprintf(stderr, "ioctl(GPIO_SPARE3) on knum=%d ret=%d err: " "%s\n", knum, res, strerror(errno)); goto the_end; } } #endif if (unexport) { if ((res = ioctl(fd, GPIO_FREE, knum))) { fprintf(stderr, "ioctl(GPIO_FREE) on knum=%d ret=%d err: %s\n", knum, res, strerror(errno)); fprintf(stderr, "continue ...\n"); } } if (scan) { num = (1 == scan) ? GPIO_BANKS_G20 : GPIO_BANKS_G45; for (k = 0, avail = 0; k < LINES_PER_BANK; ++k) { for (j = 0; j < num; ++j) { n = ((j + 1) * 32) + k; res = ioctl(fd, GPIO_REQUEST, n); printf("%sP%c%d %s", (j ? "\t" : ""), 'A' + j, k, ((0 == res) ? "AVAIL" : "occup")); if (0 == res) { ++avail; ioctl(fd, GPIO_FREE, n); } } printf("\n"); } printf("%d GPIO lines are available via /dev/gpio ioctl\n", avail); goto the_end; } if (toggle ) { if (verbose) fprintf(stderr, "Toggling knum=%d %d times\n", knum, num_toggle); if (delay && verbose) fprintf(stderr, "After each edge delay for %d.%06ld seconds\n", (int)delay_req.tv_sec, (delay_req.tv_nsec / 1000)); if ((res = ioctl(fd, GPIO_REQUEST, knum))) { fprintf(stderr, "ioctl(GPIO_REQUEST, %d) ret=%d err: %s\n", knum, res, strerror(errno)); goto the_end; } need_free = 1; ioctl(fd, GPIO_DIR_OUT, knum); for (k = 0; k < num_toggle; ++k) { if (0 == force) { ioctl(fd, GPIO_DIR_IN, knum); if (delay) nanosleep(&delay_req, NULL); ioctl(fd, GPIO_DIR_OUT, knum); if (delay) nanosleep(&delay_req, NULL); } else if (1 == force) { ioctl(fd, GPIO_SET, knum); if (delay) nanosleep(&delay_req, NULL); ioctl(fd, GPIO_CLEAR, knum); if (delay) nanosleep(&delay_req, NULL); } else { ioctl(fd, GPIO_DIR_HIGH, knum); if (delay) nanosleep(&delay_req, NULL); ioctl(fd, GPIO_DIR_OUT, knum); if (delay) nanosleep(&delay_req, NULL); } } if (state >= 0) { if (0 == state) res = ioctl(fd, GPIO_DIR_OUT, knum); else res = ioctl(fd, GPIO_DIR_HIGH, knum); if (res < 0) fprintf(stderr, "unable set set knum=%d ret=%d err: %s\n", knum, res, strerror(errno)); } } else if (state >= 0) { if ((res = ioctl(fd, GPIO_REQUEST, knum))) { fprintf(stderr, "ioctl(GPIO_REQUEST, %d) ret=%d err: %s\n", knum, res, strerror(errno)); goto the_end; } need_free = 1; if (0 == state) res = ioctl(fd, GPIO_DIR_OUT, knum); else res = ioctl(fd, GPIO_DIR_HIGH, knum); if (res < 0) fprintf(stderr, "unable set set knum=%d ret=%d err: %s\n", knum, res, strerror(errno)); } else if (read_val) { if ((res = ioctl(fd, GPIO_REQUEST, knum))) { fprintf(stderr, "ioctl(GPIO_REQUEST, %d) ret=%d err: %s\n", knum, res, strerror(errno)); goto the_end; } need_free = 1; if (ioctl(fd, GPIO_DIR_IN, knum) < 0) { fprintf(stderr, "ioctl(GPIO_DIR_IN, %d) errno=%d err: %s\n", knum, errno, strerror(errno)); goto the_end; } if ((res = ioctl(fd, GPIO_GET, knum)) < 0) { fprintf(stderr, "ioctl(GPIO_GET, %d) errno=%d err: %s\n", knum, errno, strerror(errno)); goto the_end; } printf("%d\n", res); if (1 == read_val) res = 0; } the_end: if (need_free) { if ((res2 = ioctl(fd, GPIO_FREE, knum))) fprintf(stderr, "ioctl(GPIO_FREE) ret=%d err: %s\n", res2, strerror(errno)); } close(fd); return (res ? 1 : 0); }