| From 665ddd6b1e4bad3473ef5f4834c04db3e8968867 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org> |
| Date: Fri, 26 Jun 2015 14:27:06 +0200 |
| Subject: [PATCH] char: broadcom: Add vcio module |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| Add module for accessing the mailbox property channel through |
| /dev/vcio. Was previously in bcm2708-vcio. |
| |
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> |
| |
| char: vcio: Add compat ioctl handling |
| |
| There was no compat ioctl handler, so 32 bit userspace on a |
| 64 bit kernel failed as IOCTL_MBOX_PROPERTY used the size |
| of char*. |
| |
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> |
| |
| char: vcio: Fail probe if rpi_firmware is not found. |
| |
| Device Tree is now the only supported config mechanism, therefore |
| uncomment the block of code that fails the probe if the |
| firmware node can't be found. |
| |
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org> |
| --- |
| drivers/char/broadcom/Kconfig | 6 + |
| drivers/char/broadcom/Makefile | 1 + |
| drivers/char/broadcom/vcio.c | 194 +++++++++++++++++++++++++++++++++ |
| 3 files changed, 201 insertions(+) |
| create mode 100644 drivers/char/broadcom/vcio.c |
| |
| --- 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 BCM_VC_SM |
| --- a/drivers/char/broadcom/Makefile |
| +++ b/drivers/char/broadcom/Makefile |
| @@ -1,4 +1,5 @@ |
| obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o |
| +obj-$(CONFIG_BCM_VCIO) += vcio.o |
| obj-$(CONFIG_BCM_VC_SM) += vc_sm/ |
| |
| obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o |
| --- /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 <linux/cdev.h> |
| +#include <linux/device.h> |
| +#include <linux/fs.h> |
| +#include <linux/init.h> |
| +#include <linux/ioctl.h> |
| +#include <linux/module.h> |
| +#include <linux/slab.h> |
| +#include <linux/uaccess.h> |
| +#include <soc/bcm2835/raspberrypi-firmware.h> |
| + |
| +#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"); |