diff --git a/drivers/char/broadcom/Kconfig b/drivers/char/broadcom/Kconfig index 48d29e5acb34e9a6a12b17e6087ab2c966e978ab..c11b834ccdbdb8b505105a0777b92992bec3ad3c 100644 --- a/drivers/char/broadcom/Kconfig +++ b/drivers/char/broadcom/Kconfig @@ -15,6 +15,12 @@ config BCM2708_VCMEM help Helper for videocore memory access and total size allocation. +config BCM_VCIO + tristate "Mailbox userspace access" + depends on BCM2835_MBOX + help + Gives access to the mailbox property channel from userspace. + endif config BCM2835_DEVGPIOMEM diff --git a/drivers/char/broadcom/Makefile b/drivers/char/broadcom/Makefile index a5d0f33221a3cb60a5988b7fe70a6a0833f8e815..ea7495b01c2893ace5a39ab19f17e58d7ce1c2e5 100644 --- a/drivers/char/broadcom/Makefile +++ b/drivers/char/broadcom/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o +obj-$(CONFIG_BCM_VCIO) += vcio.o obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o diff --git a/drivers/char/broadcom/vcio.c b/drivers/char/broadcom/vcio.c new file mode 100644 index 0000000000000000000000000000000000000000..d2598663a2b5d5358f8e0dc3c904f02fe5d727ca --- /dev/null +++ b/drivers/char/broadcom/vcio.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2010 Broadcom + * Copyright (C) 2015 Noralf Trønnes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MBOX_CHAN_PROPERTY 8 + +#define VCIO_IOC_MAGIC 100 +#define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *) +#ifdef CONFIG_COMPAT +#define IOCTL_MBOX_PROPERTY32 _IOWR(VCIO_IOC_MAGIC, 0, compat_uptr_t) +#endif + +static struct { + dev_t devt; + struct cdev cdev; + struct class *class; + struct rpi_firmware *fw; +} vcio; + +static int vcio_user_property_list(void *user) +{ + u32 *buf, size; + int ret; + + /* The first 32-bit is the size of the buffer */ + if (copy_from_user(&size, user, sizeof(size))) + return -EFAULT; + + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user, size)) { + kfree(buf); + return -EFAULT; + } + + /* Strip off protocol encapsulation */ + ret = rpi_firmware_property_list(vcio.fw, &buf[2], size - 12); + if (ret) { + kfree(buf); + return ret; + } + + buf[1] = RPI_FIRMWARE_STATUS_SUCCESS; + if (copy_to_user(user, buf, size)) + ret = -EFAULT; + + kfree(buf); + + return ret; +} + +static int vcio_device_open(struct inode *inode, struct file *file) +{ + try_module_get(THIS_MODULE); + + return 0; +} + +static int vcio_device_release(struct inode *inode, struct file *file) +{ + module_put(THIS_MODULE); + + return 0; +} + +static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + switch (ioctl_num) { + case IOCTL_MBOX_PROPERTY: + return vcio_user_property_list((void *)ioctl_param); + default: + pr_err("unknown ioctl: %x\n", ioctl_num); + return -EINVAL; + } +} + +#ifdef CONFIG_COMPAT +static long vcio_device_compat_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + switch (ioctl_num) { + case IOCTL_MBOX_PROPERTY32: + return vcio_user_property_list(compat_ptr(ioctl_param)); + default: + pr_err("unknown ioctl: %x\n", ioctl_num); + return -EINVAL; + } +} +#endif + +const struct file_operations vcio_fops = { + .unlocked_ioctl = vcio_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vcio_device_compat_ioctl, +#endif + .open = vcio_device_open, + .release = vcio_device_release, +}; + +static int __init vcio_init(void) +{ + struct device_node *np; + static struct device *dev; + int ret; + + np = of_find_compatible_node(NULL, NULL, + "raspberrypi,bcm2835-firmware"); + if (!of_device_is_available(np)) + return -ENODEV; + + vcio.fw = rpi_firmware_get(np); + if (!vcio.fw) + return -ENODEV; + + ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio"); + if (ret) { + pr_err("failed to allocate device number\n"); + return ret; + } + + cdev_init(&vcio.cdev, &vcio_fops); + vcio.cdev.owner = THIS_MODULE; + ret = cdev_add(&vcio.cdev, vcio.devt, 1); + if (ret) { + pr_err("failed to register device\n"); + goto err_unregister_chardev; + } + + /* + * Create sysfs entries + * 'bcm2708_vcio' is used for backwards compatibility so we don't break + * userspace. Raspian has a udev rule that changes the permissions. + */ + vcio.class = class_create(THIS_MODULE, "bcm2708_vcio"); + if (IS_ERR(vcio.class)) { + ret = PTR_ERR(vcio.class); + pr_err("failed to create class\n"); + goto err_cdev_del; + } + + dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio"); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + pr_err("failed to create device\n"); + goto err_class_destroy; + } + + return 0; + +err_class_destroy: + class_destroy(vcio.class); +err_cdev_del: + cdev_del(&vcio.cdev); +err_unregister_chardev: + unregister_chrdev_region(vcio.devt, 1); + + return ret; +} +module_init(vcio_init); + +static void __exit vcio_exit(void) +{ + device_destroy(vcio.class, vcio.devt); + class_destroy(vcio.class); + cdev_del(&vcio.cdev); + unregister_chrdev_region(vcio.devt, 1); +} +module_exit(vcio_exit); + +MODULE_AUTHOR("Gray Girling"); +MODULE_AUTHOR("Noralf Trønnes"); +MODULE_DESCRIPTION("Mailbox userspace access"); +MODULE_LICENSE("GPL");