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 : 404 : static int __init vcio_init(void)
122 : : {
123 : : struct device_node *np;
124 : : static struct device *dev;
125 : : int ret;
126 : :
127 : 404 : np = of_find_compatible_node(NULL, NULL,
128 : : "raspberrypi,bcm2835-firmware");
129 [ + - ]: 404 : if (!of_device_is_available(np))
130 : : return -ENODEV;
131 : :
132 : 404 : vcio.fw = rpi_firmware_get(np);
133 [ + - ]: 404 : if (!vcio.fw)
134 : : return -ENODEV;
135 : :
136 : 404 : ret = alloc_chrdev_region(&vcio.devt, 0, 1, "vcio");
137 [ - + ]: 404 : if (ret) {
138 : 0 : pr_err("failed to allocate device number\n");
139 : 0 : return ret;
140 : : }
141 : :
142 : 404 : cdev_init(&vcio.cdev, &vcio_fops);
143 : 404 : vcio.cdev.owner = THIS_MODULE;
144 : 404 : ret = cdev_add(&vcio.cdev, vcio.devt, 1);
145 [ - + ]: 404 : 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 : 404 : vcio.class = class_create(THIS_MODULE, "bcm2708_vcio");
156 [ - + ]: 404 : 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 : 404 : dev = device_create(vcio.class, NULL, vcio.devt, NULL, "vcio");
163 [ - + ]: 404 : 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");
|