/* * Parport GPIO control * * Copyright (c) 2009 Michael Buesch * * Licensed under the GNU/GPL version 2 or later. * */ #include #include #include #include #include #include #include #include #include #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) enum gpio_type { GPIO_PPDATA, GPIO_PPCONTROL, GPIO_PPSTATUS, }; enum gpio_direction { GPIO_OUT, GPIO_IN, }; struct gpio { enum gpio_type type; enum gpio_direction dir; unsigned int bit; int default_state; }; /* Mask of hardware inverters. */ static const unsigned char ppcontrol_hwinvert_mask = 0x0B; static const unsigned char ppstatus_hwinvert_mask = 0x80; static const struct gpio gpio_table[] = { /* Data outputs */ { /* GPIO 0 = Pin 2 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 0, }, { /* GPIO 1 = Pin 3 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 1, }, { /* GPIO 2 = Pin 4 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 2, }, { /* GPIO 3 = Pin 5 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 3, }, { /* GPIO 4 = Pin 6 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 4, }, { /* GPIO 5 = Pin 7 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 5, }, { /* GPIO 6 = Pin 8 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 6, }, { /* GPIO 7 = Pin 9 */ .type = GPIO_PPDATA, .dir = GPIO_OUT, .bit = 7, }, /* Control outputs */ { /* GPIO 8 = Pin 1 */ .type = GPIO_PPCONTROL, .dir = GPIO_OUT, .bit = 0, }, { /* GPIO 9 = Pin 14 */ .type = GPIO_PPCONTROL, .dir = GPIO_OUT, .bit = 1, }, { /* GPIO 10 = Pin 16 */ .type = GPIO_PPCONTROL, .dir = GPIO_OUT, .bit = 2, }, { /* GPIO 11 = Pin 17 */ .type = GPIO_PPCONTROL, .dir = GPIO_OUT, .bit = 3, }, /* Inputs */ { /* GPIO 12 = Pin 15 */ .type = GPIO_PPSTATUS, .dir = GPIO_IN, .bit = 3, }, { /* GPIO 13 = Pin 13 */ .type = GPIO_PPSTATUS, .dir = GPIO_IN, .bit = 4, }, { /* GPIO 14 = Pin 12 */ .type = GPIO_PPSTATUS, .dir = GPIO_IN, .bit = 5, }, { /* GPIO 15 = Pin 10 */ .type = GPIO_PPSTATUS, .dir = GPIO_IN, .bit = 6, }, { /* GPIO 16 = Pin 11 */ .type = GPIO_PPSTATUS, .dir = GPIO_IN, .bit = 7, }, }; static int gpio_set(int fd, const struct gpio *gpio, int state) { unsigned char data; int err; struct ppdev_frob_struct control_frob; if (gpio->dir != GPIO_OUT) { fprintf(stderr, "GPIO is not an output pin\n"); return -1; } switch (gpio->type) { case GPIO_PPDATA: err = ioctl(fd, PPRDATA, &data); if (err) { fprintf(stderr, "Failed to read parport DATA: %s\n", strerror(errno)); return -1; } data &= ~(1 << gpio->bit); if (state) data |= (1 << gpio->bit); err = ioctl(fd, PPWDATA, &data); if (err) { fprintf(stderr, "Failed to write parport DATA: %s\n", strerror(errno)); return -1; } break; case GPIO_PPCONTROL: control_frob.mask = (1 << gpio->bit); control_frob.val = 0; if (state) control_frob.val = (1 << gpio->bit); control_frob.val ^= ppcontrol_hwinvert_mask; control_frob.val &= control_frob.mask; err = ioctl(fd, PPFCONTROL, &control_frob); if (err) { fprintf(stderr, "Failed to frob parport CONTROL: %s\n", strerror(errno)); return -1; } break; case GPIO_PPSTATUS: fprintf(stderr, "gpio_set: Cannot write status port.\n"); return -1; } return 0; } static int gpio_get(int fd, const struct gpio *gpio) { int err; unsigned char d; switch (gpio->type) { case GPIO_PPDATA: err = ioctl(fd, PPRDATA, &d); if (err) { fprintf(stderr, "Failed to read parport DATA: %s\n", strerror(errno)); return -1; } break; case GPIO_PPCONTROL: err = ioctl(fd, PPRCONTROL, &d); if (err) { fprintf(stderr, "Failed to read parport CONTROL: %s\n", strerror(errno)); return -1; } d ^= ppcontrol_hwinvert_mask; break; case GPIO_PPSTATUS: err = ioctl(fd, PPRSTATUS, &d); if (err) { fprintf(stderr, "Failed to read parport STATUS: %s\n", strerror(errno)); return -1; } d ^= ppstatus_hwinvert_mask; break; } return !!(d & (1 << gpio->bit)); } static int do_setup(int fd) { const struct gpio *gpio; int dir, mode, err; unsigned int i; err = ioctl(fd, PPCLAIM); if (err) { fprintf(stderr, "Failed to claim parport device: %s\n", strerror(errno)); return -1; } mode = IEEE1284_MODE_COMPAT; err = ioctl(fd, PPSETMODE, &mode); if (err) { fprintf(stderr, "Failed to set parport mode: %s\n", strerror(errno)); goto out; } dir = 0; err = ioctl(fd, PPDATADIR, &dir); if (err) { fprintf(stderr, "Failed to turn on DATA output: %s\n", strerror(errno)); goto out; } err = 0; for (i = 0; i < ARRAY_SIZE(gpio_table); i++) { gpio = &(gpio_table[i]); if (gpio->dir == GPIO_OUT) { err = gpio_set(fd, gpio, gpio->default_state); if (err) break; } } out: ioctl(fd, PPRELEASE); return err; } static int do_change_gpio_state(int fd, unsigned int gpionr, int newstate) { const struct gpio *gpio; int err; if (gpionr >= ARRAY_SIZE(gpio_table)) { fprintf(stderr, "GPIO number is too big\n"); return -1; } gpio = &(gpio_table[gpionr]); err = ioctl(fd, PPCLAIM); if (err) { fprintf(stderr, "Failed to claim parport device: %s\n", strerror(errno)); return -1; } err = gpio_set(fd, gpio, newstate); ioctl(fd, PPRELEASE); return err; } static int do_get_gpio_state(int fd, unsigned int gpionr) { const struct gpio *gpio; int err, res; if (gpionr >= ARRAY_SIZE(gpio_table)) { fprintf(stderr, "GPIO number is too big\n"); return -1; } gpio = &(gpio_table[gpionr]); err = ioctl(fd, PPCLAIM); if (err) { fprintf(stderr, "Failed to claim parport device: %s\n", strerror(errno)); return -1; } res = gpio_get(fd, gpio); ioctl(fd, PPRELEASE); return res; } static int str2bool(const char *str) { int res = -1; if ((strcasecmp(str, "1") == 0) || (strcasecmp(str, "on") == 0) || (strcasecmp(str, "yes") == 0) || (strcasecmp(str, "true") == 0)) res = 1; else if ((strcasecmp(str, "0") == 0) || (strcasecmp(str, "off") == 0) || (strcasecmp(str, "no") == 0) || (strcasecmp(str, "false") == 0)) res = 0; return res; } static void usage(int argc, char **argv) { printf("Usage: %s /dev/parport0 GPIONR on/off\n", argv[0]); printf(" %s /dev/parport0 GPIONR get\n", argv[0]); printf(" %s /dev/parport0 --setup\n", argv[0]); } #define ERRCODE_FAIL 0xFF int main(int argc, char **argv) { const char *ppdev, *gpiostr, *statestr; unsigned int gpionr; int err, res, state = 0, fd; int setup = 0, get = 0; if ((argc != 4) && (argc != 3)) { usage(argc, argv); return ERRCODE_FAIL; } if (argc == 4) { ppdev = argv[1]; gpiostr = argv[2]; statestr = argv[3]; if (sscanf(gpiostr, "%u", &gpionr) != 1) { fprintf(stderr, "Invalid GPIO number.\n"); return ERRCODE_FAIL; } if (strcasecmp(statestr, "get") == 0) { get = 1; } else { state = str2bool(statestr); if (state == -1) { fprintf(stderr, "Invalid boolean state value.\n"); return ERRCODE_FAIL; } } } else { ppdev = argv[1]; if (strcasecmp(argv[2], "--setup") != 0) { usage(argc, argv); return ERRCODE_FAIL; } setup = 1; } fd = open(ppdev, O_RDWR); if (fd == -1) { fprintf(stderr, "Failed to open %s: %s\n", ppdev, strerror(errno)); return ERRCODE_FAIL; } res = 0; err = 0; if (setup) { err = do_setup(fd); } else { if (get) { res = do_get_gpio_state(fd, gpionr); if (res < 0) err = -1; else printf("%d\n", res); } else err = do_change_gpio_state(fd, gpionr, state); } close(fd); return err ? ERRCODE_FAIL : res; }