Skip to content

Commit 38c57eb

Browse files
committed
Add /dev/gpiomem device for rootless user GPIO access
1 parent 5925037 commit 38c57eb

File tree

5 files changed

+320
-0
lines changed

5 files changed

+320
-0
lines changed

arch/arm/boot/dts/overlays/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dtb-$(RPI_DT_OVERLAYS) += bmp085_i2c-sensor-overlay.dtb
1717
dtb-$(RPI_DT_OVERLAYS) += dht11-overlay.dtb
1818
dtb-$(RPI_DT_OVERLAYS) += enc28j60-overlay.dtb
1919
dtb-$(RPI_DT_OVERLAYS) += gpio-poweroff-overlay.dtb
20+
dtb-$(RPI_DT_OVERLAYS) += gpiomem-overlay.dtb
2021
dtb-$(RPI_DT_OVERLAYS) += hifiberry-amp-overlay.dtb
2122
dtb-$(RPI_DT_OVERLAYS) += hifiberry-dac-overlay.dtb
2223
dtb-$(RPI_DT_OVERLAYS) += hifiberry-dacplus-overlay.dtb
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Description: Overlay to enable the gpiomem chardev
2+
// Allows rootless user access to the GPIO registers,
3+
// without relaxing the permissions of /dev/mem.
4+
// Author: Luke Wren
5+
6+
/dts-v1/;
7+
/plugin/;
8+
9+
/{
10+
compatible = "brcm,bcm2708";
11+
12+
fragment@0 {
13+
target = <&soc>;
14+
__overlay__ {
15+
#address-cells = <1>;
16+
#size-cells = <1>;
17+
18+
gpiomem {
19+
compatible = "brcm,bcm2835-gpiomem";
20+
reg = <0x7e200000 0x1000>;
21+
status = "okay";
22+
};
23+
};
24+
};
25+
};

drivers/char/broadcom/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,12 @@ config BCM_VC_SM
3838
help
3939
Support for the VC shared memory on the Broadcom reference
4040
design. Uses the VCHIQ stack.
41+
42+
config BCM2835_DEVGPIOMEM
43+
tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
44+
default m
45+
help
46+
Provides users with root-free access to the GPIO registers
47+
on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
48+
register page to the user's pointer.
49+

drivers/char/broadcom/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ obj-$(CONFIG_BCM_VC_CMA) += vc_cma/
22
obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
33
obj-$(CONFIG_BCM_VCIO) += vcio.o
44
obj-$(CONFIG_BCM_VC_SM) += vc_sm/
5+
6+
obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
/**
2+
* GPIO memory device driver
3+
*
4+
* Creates a chardev /dev/gpiomem which will provide user access to
5+
* the BCM2835's GPIO registers when it is mmap()'d.
6+
* No longer need root for user GPIO access, but without relaxing permissions
7+
* on /dev/mem.
8+
*
9+
* Written by Luke Wren <[email protected]>
10+
* Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
11+
*
12+
* Redistribution and use in source and binary forms, with or without
13+
* modification, are permitted provided that the following conditions
14+
* are met:
15+
* 1. Redistributions of source code must retain the above copyright
16+
* notice, this list of conditions, and the following disclaimer,
17+
* without modification.
18+
* 2. Redistributions in binary form must reproduce the above copyright
19+
* notice, this list of conditions and the following disclaimer in the
20+
* documentation and/or other materials provided with the distribution.
21+
* 3. The names of the above-listed copyright holders may not be used
22+
* to endorse or promote products derived from this software without
23+
* specific prior written permission.
24+
*
25+
* ALTERNATIVELY, this software may be distributed under the terms of the
26+
* GNU General Public License ("GPL") version 2, as published by the Free
27+
* Software Foundation.
28+
*
29+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
30+
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
31+
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
33+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
34+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40+
*/
41+
42+
#include <linux/kernel.h>
43+
#include <linux/module.h>
44+
#include <linux/of.h>
45+
#include <linux/platform_device.h>
46+
#include <linux/mm.h>
47+
#include <linux/slab.h>
48+
#include <linux/cdev.h>
49+
#include <linux/pagemap.h>
50+
#include <linux/io.h>
51+
52+
#define DEVICE_NAME "gpiomem"
53+
#define DRIVER_NAME "gpiomem-bcm2835"
54+
#define DEVICE_MINOR 0
55+
56+
#define DEVICE_LOG_PREFIX DEVICE_NAME ": "
57+
58+
#ifndef log_error
59+
#define log_error(fmt, ...) \
60+
pr_err(DEVICE_LOG_PREFIX fmt "\n", ##__VA_ARGS__)
61+
#endif
62+
63+
#ifndef log_info
64+
#define log_info(fmt, ...) \
65+
pr_info(DEVICE_LOG_PREFIX fmt "\n", ##__VA_ARGS__)
66+
#endif
67+
68+
69+
struct bcm2835_gpiomem_instance {
70+
unsigned long gpio_regs_phys;
71+
struct device *dev;
72+
};
73+
74+
static struct cdev bcm2835_gpiomem_cdev;
75+
static dev_t bcm2835_gpiomem_devid;
76+
static struct class *bcm2835_gpiomem_class;
77+
static struct device *bcm2835_gpiomem_dev;
78+
static struct bcm2835_gpiomem_instance *inst;
79+
80+
81+
/****************************************************************************
82+
*
83+
* GPIO mem chardev file ops
84+
*
85+
***************************************************************************/
86+
87+
static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
88+
{
89+
int dev = iminor(inode);
90+
int ret = 0;
91+
92+
log_info("gpiomem device opened.");
93+
94+
if (dev != DEVICE_MINOR) {
95+
log_error(" Unknown minor device: %d",
96+
dev);
97+
ret = -ENXIO;
98+
goto out;
99+
}
100+
out:
101+
return ret;
102+
}
103+
104+
static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
105+
{
106+
int dev = iminor(inode);
107+
int ret = 0;
108+
109+
if (dev != DEVICE_MINOR) {
110+
log_error("Unknown minor device %d", dev);
111+
ret = -ENXIO;
112+
goto out;
113+
}
114+
115+
out:
116+
return ret;
117+
}
118+
119+
static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
120+
#ifdef CONFIG_HAVE_IOREMAP_PROT
121+
.access = generic_access_phys
122+
#endif
123+
};
124+
125+
static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
126+
{
127+
/* Ignore what the user says - they're getting the GPIO regs
128+
whether they like it or not! */
129+
unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
130+
131+
vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
132+
PAGE_SIZE,
133+
vma->vm_page_prot);
134+
vma->vm_ops = &bcm2835_gpiomem_vm_ops;
135+
if (remap_pfn_range(vma, vma->vm_start,
136+
gpio_page,
137+
PAGE_SIZE,
138+
vma->vm_page_prot)) {
139+
return -EAGAIN;
140+
}
141+
return 0;
142+
}
143+
144+
static const struct file_operations
145+
bcm2835_gpiomem_fops = {
146+
.owner = THIS_MODULE,
147+
.open = bcm2835_gpiomem_open,
148+
.release = bcm2835_gpiomem_release,
149+
.mmap = bcm2835_gpiomem_mmap,
150+
};
151+
152+
153+
/****************************************************************************
154+
*
155+
* Probe and remove functions
156+
*
157+
***************************************************************************/
158+
159+
160+
static int bcm2835_gpiomem_probe(struct platform_device *pdev)
161+
{
162+
int err;
163+
void *ptr_err;
164+
struct device *dev = &pdev->dev;
165+
struct device_node *node = dev->of_node;
166+
struct resource *ioresource;
167+
168+
/* Allocate buffers and instance data */
169+
170+
inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
171+
172+
if (!inst) {
173+
log_error("Could not allocate inst data.");
174+
err = -ENOMEM;
175+
goto failed_inst_alloc;
176+
}
177+
178+
inst->dev = dev;
179+
180+
/* Create character device entries */
181+
182+
err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
183+
DEVICE_MINOR, 1, DEVICE_NAME);
184+
if (err != 0) {
185+
log_error("unable to allocate device number");
186+
goto failed_alloc_chrdev;
187+
}
188+
cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
189+
bcm2835_gpiomem_cdev.owner = THIS_MODULE;
190+
err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
191+
if (err != 0) {
192+
log_error("unable to register device");
193+
goto failed_cdev_add;
194+
}
195+
196+
/* Create sysfs entries */
197+
198+
bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
199+
ptr_err = bcm2835_gpiomem_class;
200+
if (IS_ERR(ptr_err))
201+
goto failed_class_create;
202+
203+
bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
204+
bcm2835_gpiomem_devid, NULL,
205+
"gpiomem");
206+
ptr_err = bcm2835_gpiomem_dev;
207+
if (IS_ERR(ptr_err))
208+
goto failed_device_create;
209+
210+
/* Get address from device tree if available (*_resource() correctly
211+
converts the bus address in device tree to a physical address -
212+
we extract that address with virt_to_phys),
213+
or use hardcoded offset + BCM2708_PERI_BASE if not.
214+
(In spite of its name 2708 actually seems to have the correct
215+
mach-dependent value on 2709 etc, as it is defined in
216+
mach-bcm270x/platform.h) */
217+
218+
if (node) {
219+
ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
220+
inst->gpio_regs_phys = ioresource->start;
221+
} else {
222+
inst->gpio_regs_phys = BCM2708_PERI_BASE + 0x00200000ul;
223+
}
224+
225+
log_info("initialised");
226+
227+
return 0;
228+
229+
failed_device_create:
230+
class_destroy(bcm2835_gpiomem_class);
231+
failed_class_create:
232+
cdev_del(&bcm2835_gpiomem_cdev);
233+
err = PTR_ERR(ptr_err);
234+
failed_cdev_add:
235+
unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
236+
failed_alloc_chrdev:
237+
kfree(inst);
238+
failed_inst_alloc:
239+
log_error("could not load bcm2835_gpiomem");
240+
return err;
241+
}
242+
243+
static int bcm2835_gpiomem_remove(struct platform_device *pdev)
244+
{
245+
kfree(inst);
246+
device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
247+
class_destroy(bcm2835_gpiomem_class);
248+
cdev_del(&bcm2835_gpiomem_cdev);
249+
unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
250+
251+
log_info("GPIO mem driver removed - OK");
252+
return 0;
253+
}
254+
255+
/****************************************************************************
256+
*
257+
* Register the driver with device tree
258+
*
259+
***************************************************************************/
260+
261+
static const struct of_device_id bcm2835_gpiomem_of_match[] = {
262+
{.compatible = "brcm,bcm2835-gpiomem",},
263+
{ /* sentinel */ },
264+
};
265+
266+
MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
267+
268+
static struct platform_driver bcm2835_gpiomem_driver = {
269+
.probe = bcm2835_gpiomem_probe,
270+
.remove = bcm2835_gpiomem_remove,
271+
.driver = {
272+
.name = DRIVER_NAME,
273+
.owner = THIS_MODULE,
274+
.of_match_table = bcm2835_gpiomem_of_match,
275+
},
276+
};
277+
278+
module_platform_driver(bcm2835_gpiomem_driver);
279+
280+
MODULE_ALIAS("platform:gpiomem-bcm2835");
281+
MODULE_LICENSE("GPL");
282+
MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
283+
MODULE_AUTHOR("Luke Wren <[email protected]>");

0 commit comments

Comments
 (0)