123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- From e52d7ce66319f6687da3531b00cfec3001aec7a8 Mon Sep 17 00:00:00 2001
- From: Luke Wren <luke@raspberrypi.org>
- Date: Fri, 21 Aug 2015 23:14:48 +0100
- Subject: [PATCH] Add /dev/gpiomem device for rootless user GPIO access
- Signed-off-by: Luke Wren <luke@raspberrypi.org>
- bcm2835-gpiomem: Fix for ARCH_BCM2835 builds
- Build on ARCH_BCM2835, and fail to probe if no IO resource.
- See: https://github.com/raspberrypi/linux/issues/1154
- ---
- drivers/char/broadcom/Kconfig | 9 ++
- drivers/char/broadcom/Makefile | 3 +
- drivers/char/broadcom/bcm2835-gpiomem.c | 260 ++++++++++++++++++++++++++++++++
- 3 files changed, 272 insertions(+)
- create mode 100644 drivers/char/broadcom/bcm2835-gpiomem.c
- --- a/drivers/char/broadcom/Kconfig
- +++ b/drivers/char/broadcom/Kconfig
- @@ -32,3 +32,12 @@ config BCM_VC_SM
- help
- Support for the VC shared memory on the Broadcom reference
- design. Uses the VCHIQ stack.
- +
- +config BCM2835_DEVGPIOMEM
- + tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
- + default m
- + help
- + Provides users with root-free access to the GPIO registers
- + on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
- + register page to the user's pointer.
- +
- --- a/drivers/char/broadcom/Makefile
- +++ b/drivers/char/broadcom/Makefile
- @@ -1,3 +1,6 @@
- obj-$(CONFIG_BCM_VC_CMA) += vc_cma/
- obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
- obj-$(CONFIG_BCM_VC_SM) += vc_sm/
- +
- +obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
- +
- --- /dev/null
- +++ b/drivers/char/broadcom/bcm2835-gpiomem.c
- @@ -0,0 +1,260 @@
- +/**
- + * GPIO memory device driver
- + *
- + * Creates a chardev /dev/gpiomem which will provide user access to
- + * the BCM2835's GPIO registers when it is mmap()'d.
- + * No longer need root for user GPIO access, but without relaxing permissions
- + * on /dev/mem.
- + *
- + * Written by Luke Wren <luke@raspberrypi.org>
- + * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
- + *
- + * 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,
- + * without modification.
- + * 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 names of the above-listed copyright holders may not be used
- + * to endorse or promote products derived from this software without
- + * specific prior written permission.
- + *
- + * ALTERNATIVELY, this software may be distributed under the terms of the
- + * GNU General Public License ("GPL") version 2, as published by the Free
- + * Software Foundation.
- + *
- + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/of.h>
- +#include <linux/platform_device.h>
- +#include <linux/mm.h>
- +#include <linux/slab.h>
- +#include <linux/cdev.h>
- +#include <linux/pagemap.h>
- +#include <linux/io.h>
- +
- +#define DEVICE_NAME "bcm2835-gpiomem"
- +#define DRIVER_NAME "gpiomem-bcm2835"
- +#define DEVICE_MINOR 0
- +
- +struct bcm2835_gpiomem_instance {
- + unsigned long gpio_regs_phys;
- + struct device *dev;
- +};
- +
- +static struct cdev bcm2835_gpiomem_cdev;
- +static dev_t bcm2835_gpiomem_devid;
- +static struct class *bcm2835_gpiomem_class;
- +static struct device *bcm2835_gpiomem_dev;
- +static struct bcm2835_gpiomem_instance *inst;
- +
- +
- +/****************************************************************************
- +*
- +* GPIO mem chardev file ops
- +*
- +***************************************************************************/
- +
- +static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
- +{
- + int dev = iminor(inode);
- + int ret = 0;
- +
- + dev_info(inst->dev, "gpiomem device opened.");
- +
- + if (dev != DEVICE_MINOR) {
- + dev_err(inst->dev, "Unknown minor device: %d", dev);
- + ret = -ENXIO;
- + }
- + return ret;
- +}
- +
- +static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
- +{
- + int dev = iminor(inode);
- + int ret = 0;
- +
- + if (dev != DEVICE_MINOR) {
- + dev_err(inst->dev, "Unknown minor device %d", dev);
- + ret = -ENXIO;
- + }
- + return ret;
- +}
- +
- +static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
- +#ifdef CONFIG_HAVE_IOREMAP_PROT
- + .access = generic_access_phys
- +#endif
- +};
- +
- +static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
- +{
- + /* Ignore what the user says - they're getting the GPIO regs
- + whether they like it or not! */
- + unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
- +
- + vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
- + PAGE_SIZE,
- + vma->vm_page_prot);
- + vma->vm_ops = &bcm2835_gpiomem_vm_ops;
- + if (remap_pfn_range(vma, vma->vm_start,
- + gpio_page,
- + PAGE_SIZE,
- + vma->vm_page_prot)) {
- + return -EAGAIN;
- + }
- + return 0;
- +}
- +
- +static const struct file_operations
- +bcm2835_gpiomem_fops = {
- + .owner = THIS_MODULE,
- + .open = bcm2835_gpiomem_open,
- + .release = bcm2835_gpiomem_release,
- + .mmap = bcm2835_gpiomem_mmap,
- +};
- +
- +
- + /****************************************************************************
- +*
- +* Probe and remove functions
- +*
- +***************************************************************************/
- +
- +
- +static int bcm2835_gpiomem_probe(struct platform_device *pdev)
- +{
- + int err;
- + void *ptr_err;
- + struct device *dev = &pdev->dev;
- + struct resource *ioresource;
- +
- + /* Allocate buffers and instance data */
- +
- + inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
- +
- + if (!inst) {
- + err = -ENOMEM;
- + goto failed_inst_alloc;
- + }
- +
- + inst->dev = dev;
- +
- + ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- + if (ioresource) {
- + inst->gpio_regs_phys = ioresource->start;
- + } else {
- + dev_err(inst->dev, "failed to get IO resource");
- + err = -ENOENT;
- + goto failed_get_resource;
- + }
- +
- + /* Create character device entries */
- +
- + err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
- + DEVICE_MINOR, 1, DEVICE_NAME);
- + if (err != 0) {
- + dev_err(inst->dev, "unable to allocate device number");
- + goto failed_alloc_chrdev;
- + }
- + cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
- + bcm2835_gpiomem_cdev.owner = THIS_MODULE;
- + err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
- + if (err != 0) {
- + dev_err(inst->dev, "unable to register device");
- + goto failed_cdev_add;
- + }
- +
- + /* Create sysfs entries */
- +
- + bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
- + ptr_err = bcm2835_gpiomem_class;
- + if (IS_ERR(ptr_err))
- + goto failed_class_create;
- +
- + bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
- + bcm2835_gpiomem_devid, NULL,
- + "gpiomem");
- + ptr_err = bcm2835_gpiomem_dev;
- + if (IS_ERR(ptr_err))
- + goto failed_device_create;
- +
- + dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
- + inst->gpio_regs_phys);
- +
- + return 0;
- +
- +failed_device_create:
- + class_destroy(bcm2835_gpiomem_class);
- +failed_class_create:
- + cdev_del(&bcm2835_gpiomem_cdev);
- + err = PTR_ERR(ptr_err);
- +failed_cdev_add:
- + unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
- +failed_alloc_chrdev:
- +failed_get_resource:
- + kfree(inst);
- +failed_inst_alloc:
- + dev_err(inst->dev, "could not load bcm2835_gpiomem");
- + return err;
- +}
- +
- +static int bcm2835_gpiomem_remove(struct platform_device *pdev)
- +{
- + struct device *dev = inst->dev;
- +
- + kfree(inst);
- + device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
- + class_destroy(bcm2835_gpiomem_class);
- + cdev_del(&bcm2835_gpiomem_cdev);
- + unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
- +
- + dev_info(dev, "GPIO mem driver removed - OK");
- + return 0;
- +}
- +
- + /****************************************************************************
- +*
- +* Register the driver with device tree
- +*
- +***************************************************************************/
- +
- +static const struct of_device_id bcm2835_gpiomem_of_match[] = {
- + {.compatible = "brcm,bcm2835-gpiomem",},
- + { /* sentinel */ },
- +};
- +
- +MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
- +
- +static struct platform_driver bcm2835_gpiomem_driver = {
- + .probe = bcm2835_gpiomem_probe,
- + .remove = bcm2835_gpiomem_remove,
- + .driver = {
- + .name = DRIVER_NAME,
- + .owner = THIS_MODULE,
- + .of_match_table = bcm2835_gpiomem_of_match,
- + },
- +};
- +
- +module_platform_driver(bcm2835_gpiomem_driver);
- +
- +MODULE_ALIAS("platform:gpiomem-bcm2835");
- +MODULE_LICENSE("GPL");
- +MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
- +MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
|