The simple GPIO driver allows users to manipulate any GPIO from userspace (also known as programmable flags). It also serves as simple sample driver for people who wish to see a good example for working with GPIOs using the GPIO framework. And for the people who are just getting started, a very simple character device driver.
Userspace interacts with any GPIO via a character device node. The act of opening and closing it corresponds to the act of claiming and releasing the GPIO. When you read from it, you get back the GPIO data (which usually corresponds to a 0 or 1 which represents the signal level). To control the GPIO, you write individual characters which correspond to a simple “command set”. This way you can control the direction and value with single bytes.
Notice that the creation of the device node merely exposes the resource. Claiming of the resource does not actually occur until you open the device node. In this way you can dynamically share the GPIO resource with other drivers by only claiming it when you need it.
Each GPIO corresponds directly to a character device node. So /dev/gpio5 represents GPIO 5.
You declare the range of GPIOs that you want exposed via the platform resources. So for the BF537 STAMP board, we expose all GPIOs by default.
file: arch/blackfin/mach-bf537/boards/stamp.c
static struct resource bfin_gpios_resources = { .start = 0, .end = MAX_BLACKFIN_GPIOS - 1, .flags = IORESOURCE_IRQ, };
file: arch/blackfin/mach-bf537/boards/stamp.c
static struct platform_device bfin_gpios_device = { .name = "simple-gpio", .id = -1, .num_resources = 1, .resource = &bfin_gpios_resources, };
The device driver can be found in the kernel configuration menu:
Device Drivers --->
Character devices --->
<*> Simple GPIO char interface
The init function does the following:
file: drivers/char/simple-gpio.c
static int __init simple_gpio_init(void) { simple_gpio_class = class_create(THIS_MODULE, DRIVER_NAME); if (IS_ERR(simple_gpio_class)) { pr_init(KERN_ERR PFX "unable to create gpio class\n"); return PTR_ERR(simple_gpio_class); } return platform_driver_register(&simple_gpio_device_driver); }
The real meat of the setup process occurs here:
You can see here that we use the minor numbers to communicate the GPIO number to kernel space.
file: drivers/char/simple-gpio.c
static int __devinit simple_gpio_probe(struct platform_device *pdev) { int ret; struct group_data *group_data; struct resource *gpio_range = platform_get_resource(pdev, IORESOURCE_IRQ, 0); int gpio, gpio_max = gpio_range->end - gpio_range->start + 1; stampit(); group_data = kzalloc(sizeof(*group_data) + sizeof(struct gpio_data) * gpio_max, GFP_KERNEL); if (!group_data) return -ENOMEM; group_data->gpio_range = gpio_range; group_data->gpios = (void *)group_data + sizeof(*group_data); platform_set_drvdata(pdev, group_data); ret = alloc_chrdev_region(&group_data->dev_node, gpio_range->start, gpio_max, "gpio"); if (ret) { pr_devinit(KERN_ERR PFX "unable to get a char device\n"); kfree(group_data); return ret; } cdev_init(&group_data->cdev, &simple_gpio_fops); group_data->cdev.owner = THIS_MODULE; ret = cdev_add(&group_data->cdev, group_data->dev_node, gpio_max); if (ret) { pr_devinit(KERN_ERR PFX "unable to register char device\n"); kfree(group_data); unregister_chrdev_region(group_data->dev_node, gpio_max); return ret; } for (gpio = gpio_range->start; gpio <= gpio_range->end; ++gpio) device_create(simple_gpio_class, &pdev->dev, group_data->dev_node + gpio, NULL, "gpio%i", gpio); device_init_wakeup(&pdev->dev, 1); pr_devinit(KERN_INFO PFX "now handling %i GPIOs: %i - %i\n", gpio_max, gpio_range->start, gpio_range->end); return 0; }
This has the normal open, release, read, and write functions.
file: drivers/char/simple-gpio.c
static struct file_operations simple_gpio_fops = { .owner = THIS_MODULE, .read = simple_gpio_read, .write = simple_gpio_write, .open = simple_gpio_open, .release = simple_gpio_release, };
The open function makes sure the GPIO number is within range, and then it tries to request it using gpio_request. You can see we allow any number of people to open the device at the same time. Resource management is done by the GPIO framework.
file: drivers/char/simple-gpio.c
static int simple_gpio_open(struct inode *ino, struct file *file) { struct group_data *group_data = container_of(ino->i_cdev, struct group_data, cdev); unsigned int gpio = iminor(ino); struct gpio_data *gpio_data = &group_data->gpios[gpio - group_data->gpio_range->start]; int ret; stampit(); if (gpio < group_data->gpio_range->start || gpio > group_data->gpio_range->end) return -ENXIO; ret = gpio_request(gpio, DRIVER_NAME); if (ret) return ret; atomic_inc(&gpio_data->open_count); return 0; }
The release function also makes sure the GPIO number is valid, and if this is the last consumer to close the device, we release the GPIO with gpio_free so that some other driver may use it.
file: drivers/char/simple-gpio.c
static int simple_gpio_release(struct inode *ino, struct file *file) { struct group_data *group_data = container_of(ino->i_cdev, struct group_data, cdev); unsigned int gpio = iminor(ino); struct gpio_data *gpio_data = &group_data->gpios[gpio - group_data->gpio_range->start]; stampit(); /* do not free until last consumer has closed */ if (atomic_dec_and_test(&gpio_data->open_count)) gpio_free(gpio); else stamp("gpio still in use -- not freeing"); return 0; }
The read function just returns the state of the GPIO with the gpio_get_value function.
file: drivers/char/simple-gpio.c
static ssize_t simple_gpio_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { unsigned int gpio = iminor(file->f_path.dentry->d_inode); ssize_t ret; stampit(); for (ret = 0; ret < count; ++ret) { char byte = '0' + gpio_get_value(gpio); if (put_user(byte, buf + ret)) break; } return ret; }
The write function allows the user to control the GPIO. The simple command set is:
| Command | Description |
|---|---|
| I | Set GPIO direction to input via gpio_direction_input |
| O | Set GPIO direction to output via gpio_direction_output |
| T | Toggle the GPIO value |
| 0 | Set GPIO value to 0 via gpio_set_value |
| 1 | Set GPIO value to 1 via gpio_set_value |
file: drivers/char/simple-gpio.c
static ssize_t simple_gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { unsigned int gpio = iminor(file->f_path.dentry->d_inode); ssize_t ret; char dir = '?', uvalue = '?'; int value; stampit(); ret = 0; while (ret < count) { char byte; int user_ret = get_user(byte, buf + ret++); if (user_ret) return user_ret; switch (byte) { case '\r': case '\n': continue; case 'I': case 'O': dir = byte; break; case 'T': case '0': case '1': uvalue = byte; break; default: return -EINVAL; } stamp("processed byte '%c'", byte); } switch (uvalue) { case '?': value = gpio_get_value(gpio); break; case 'T': value = !gpio_get_value(gpio); break; default: value = uvalue - '0'; break; } switch (dir) { case 'I': gpio_direction_input(gpio); break; case 'O': gpio_direction_output(gpio, value); break; } if (uvalue != '?') gpio_set_value(gpio, value); return ret; }
Here we reverse the operations performed by the probe function:
file: drivers/char/simple-gpio.c
static int __devexit simple_gpio_remove(struct platform_device *pdev) { struct group_data *group_data = platform_get_drvdata(pdev); struct resource *gpio_range = group_data->gpio_range; int gpio, gpio_max = gpio_range->end - gpio_range->start + 1; stampit(); for (gpio = gpio_range->start; gpio <= gpio_range->end; ++gpio) device_destroy(simple_gpio_class, group_data->dev_node + gpio); cdev_del(&group_data->cdev); unregister_chrdev_region(group_data->dev_node, gpio_max); kfree(group_data); return 0; }
Here we reverse the operations performed by the init function:
file: drivers/char/simple-gpio.c
static void __exit simple_gpio_exit(void) { class_destroy(simple_gpio_class); platform_driver_unregister(&simple_gpio_device_driver); }
Simple output operations can be executed from the shell per echo command, e.g.
root:/> echo 'O0' > /dev/gpio17This sets a port bit to output with value 0. See table above under section “write”. Note though that open close() of a GPIO device, the GPIO is freed which puts it back to its reset state. So the above command will only toggle the state.
Further, a few simple test programs to illustrate the userspace side of working with the device driver are available. These are integrated into the uClinux distribution that we distribute.
In the uClinux dist vendor/user configuration menu, select:
Blackfin test programs ---> [*] Simple GPIO test
This test will open the specified GPIO and wait for it to go high. In other words, this tests a Push Button. So on the BF537-STAMP, you should choose a GPIO that is tied to one of the PB's on the board (and make sure you have not disconnected the push buttons with the board switches).
root:~> simple-gpio-test /dev/gpio2 Going to sleep until the gpio goes high. (if this is a button, you should push it :p) The gpio went high! Time to bail!
While it's hard to show it in a wiki, I just pushed PB1 on the stamp board (which is hooked up to GPIO 2). The userspace application constantly read from the GPIO until it detected the push.
This is a little more complicated. You specify a list of GPIOs to be treated as buttons and a list of GPIOs to be treated as LEDs. First the application will run “blinky” on all of the LEDs for a few seconds, then it will synchronize the LEDs with the respective push buttons. So whenever you push a button, the corresponding LED will light up. Whenever a button is released, the corresponding LED will go dark.