Branch data Line data Source code
1 : : /* 2 : : * Copyright (C) 2010 Broadcom 3 : : * Copyright (C) 2015 Noralf Trønnes 4 : : * 5 : : * This program is free software; you can redistribute it and/or modify 6 : : * it under the terms of the GNU General Public License version 2 as 7 : : * published by the Free Software Foundation. 8 : : * 9 : : */ 10 : : 11 : : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 : : 13 : : #include <linux/cdev.h> 14 : : #include <linux/device.h> 15 : : #include <linux/fs.h> 16 : : #include <linux/init.h> 17 : : #include <linux/ioctl.h> 18 : : #include <linux/module.h> 19 : : #include <linux/slab.h> 20 : : #include <linux/uaccess.h> 21 : : #include <soc/bcm2835/raspberrypi-firmware.h> 22 : : 23 : : #define MBOX_CHAN_PROPERTY 8 24 : : 25 : : #define VCIO_IOC_MAGIC 100 26 : : #define IOCTL_MBOX_PROPERTY _IOWR(VCIO_IOC_MAGIC, 0, char *) 27 : : #ifdef CONFIG_COMPAT 28 : : #define IOCTL_MBOX_PROPERTY32 _IOWR(VCIO_IOC_MAGIC, 0, compat_uptr_t) 29 : : #endif 30 : : 31 : : static struct { 32 : : dev_t devt; 33 : : struct cdev cdev; 34 : : struct class *class; 35 : : struct rpi_firmware *fw; 36 : : } vcio; 37 : : 38 : 0 : static int vcio_user_property_list(void *user) 39 : : { 40 : : u32 *buf, size; 41 : : int ret; 42 : : 43 : : /* The first 32-bit is the size of the buffer */ 44 : 0 : if (copy_from_user(&size, user, sizeof(size))) 45 : : return -EFAULT; 46 : : 47 : 0 : buf = kmalloc(size, GFP_KERNEL); 48 : 0 : if (!buf) 49 : : return -ENOMEM; 50 : : 51 : 0 : if (copy_from_user(buf, user, size)) { 52 : 0 : kfree(buf); 53 : 0 : return -EFAULT; 54 : : } 55 : : 56 : : /* Strip off protocol encapsulation */ 57 : 0 : ret = rpi_firmware_property_list(vcio.fw, &buf[2], size - 12); 58 : 0 : if (ret) { 59 : 0 : kfree(buf); 60 : 0 : return ret; 61 : : } 62 : : 63 : 0 : buf[1] = RPI_FIRMWARE_STATUS_SUCCESS; 64 : 0 : if (copy_to_user(user, buf, size)) 65 : : ret = -EFAULT; 66 : : 67 : 0 : kfree(buf); 68 : : 69 : 0 : return ret; 70 : : } 71 : : 72 : 0 : static int vcio_device_open(struct inode *inode, struct file *file) 73 : : { 74 : 0 : try_module_get(THIS_MODULE); 75 : : 76 : 0 : return 0; 77 : : } 78 : : 79 : 0 : static int vcio_device_release(struct inode *inode, struct file *file) 80 : : { 81 : 0 : module_put(THIS_MODULE); 82 : : 83 : 0 : return 0; 84 : : } 85 : : 86 : 0 : static long vcio_device_ioctl(struct file *file, unsigned int ioctl_num, 87 : : unsigned long ioctl_param) 88 : : { 89 : 0 : switch (ioctl_num) { 90 : : case IOCTL_MBOX_PROPERTY: 91 : 0 : return vcio_user_property_list((void *)ioctl_param); 92 : : default: 93 : 0 : pr_err("unknown ioctl: %x\n", ioctl_num); 94 : 0 : return -EINVAL; 95 : : } 96 : : } 97 : : 98 : : #ifdef CONFIG_COMPAT 99 : : static long vcio_device_compat_ioctl(struct file *file, unsigned int ioctl_num, 100 : : unsigned long ioctl_param) 101 : : { 102 : : switch (ioctl_num) { 103 : : case IOCTL_MBOX_PROPERTY32: 104 : : return vcio_user_property_list(compat_ptr(ioctl_param)); 105 : : default: 106 : : pr_err("unknown ioctl: %x\n", ioctl_num); 107 : : return -EINVAL; 108 : : } 109 : : } 110 : : #endif 111 : : 112 : : const struct file_operations vcio_fops = { 113 : : .unlocked_ioctl = vcio_device_ioctl, 114 : : #ifdef CONFIG_COMPAT 115 : : .compat_ioctl = vcio_device_compat_ioctl, 116 : : #endif 117 : : .open = vcio_device_open, 118 : : .release = vcio_device_release, 119 : : }; 120 : : 121 : 3 : static int __init vcio_init(void) 122 : : { 123 : : struct device_node *np; 124 : : static struct device *dev; 125 : : int ret; 126 : : 127 : 3 : np = of_find_compatible_node(NULL, NULL, 128 : : "raspberrypi,bcm2835-firmware"); 129 : 3 : if (!of_device_is_available(np)) 130 : : return -ENODEV; 131 : : 132 : 3 : vcio.fw = rpi_firmware_get(np); 133 : 3 : if (!vcio.fw) 134 : : return -ENODEV; 135 : : 136 : 3 : ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio"); 137 : 3 : if (ret) { 138 : 0 : pr_err("failed to allocate device number\n"); 139 : 0 : return ret; 140 : : } 141 : : 142 : 3 : cdev_init(&vcio.cdev, &vcio_fops); 143 : 3 : vcio.cdev.owner = THIS_MODULE; 144 : 3 : ret = cdev_add(&vcio.cdev, vcio.devt, 1); 145 : 3 : if (ret) { 146 : 0 : pr_err("failed to register device\n"); 147 : 0 : goto err_unregister_chardev; 148 : : } 149 : : 150 : : /* 151 : : * Create sysfs entries 152 : : * 'bcm2708_vcio' is used for backwards compatibility so we don't break 153 : : * userspace. Raspian has a udev rule that changes the permissions. 154 : : */ 155 : 3 : vcio.class = class_create(THIS_MODULE, "bcm2708_vcio"); 156 : 3 : if (IS_ERR(vcio.class)) { 157 : : ret = PTR_ERR(vcio.class); 158 : 0 : pr_err("failed to create class\n"); 159 : 0 : goto err_cdev_del; 160 : : } 161 : : 162 : 3 : dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio"); 163 : 3 : if (IS_ERR(dev)) { 164 : : ret = PTR_ERR(dev); 165 : 0 : pr_err("failed to create device\n"); 166 : : goto err_class_destroy; 167 : : } 168 : : 169 : : return 0; 170 : : 171 : : err_class_destroy: 172 : 0 : class_destroy(vcio.class); 173 : : err_cdev_del: 174 : 0 : cdev_del(&vcio.cdev); 175 : : err_unregister_chardev: 176 : 0 : unregister_chrdev_region(vcio.devt, 1); 177 : : 178 : 0 : return ret; 179 : : } 180 : : module_init(vcio_init); 181 : : 182 : 0 : static void __exit vcio_exit(void) 183 : : { 184 : 0 : device_destroy(vcio.class, vcio.devt); 185 : 0 : class_destroy(vcio.class); 186 : 0 : cdev_del(&vcio.cdev); 187 : 0 : unregister_chrdev_region(vcio.devt, 1); 188 : 0 : } 189 : : module_exit(vcio_exit); 190 : : 191 : : MODULE_AUTHOR("Gray Girling"); 192 : : MODULE_AUTHOR("Noralf Trønnes"); 193 : : MODULE_DESCRIPTION("Mailbox userspace access"); 194 : : MODULE_LICENSE("GPL");