/
dmabufr.c
353 lines (306 loc) · 9.91 KB
/
dmabufr.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/********************************************************************************
* *
* *
* Dummy kernel module for dmabuf offscreen renderer (dmabufr)
* *
* *
/********************************************************************************/
#include <linux/dma-buf.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/mutex.h>
#include "dmabufr.h"
#define DMABUFR_NUM_DEVICES 1
#define DMABUFR_NAME "dmabufr"
static int dmabufr_open(struct inode *i, struct file *f);
static int dmabufr_release(struct inode *i, struct file *f);
static int dmabufr_mmap(struct file *filp, struct vm_area_struct *vma);
static long dmabufr_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg);
static long dmabufr_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static const struct file_operations dmabufr_fops = {
.owner = THIS_MODULE,
.open = dmabufr_open,
.unlocked_ioctl = dmabufr_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = dmabufr_compat_ioctl,
#endif
.release = dmabufr_release
};
static int major;
static int dma_size = 0;
static struct class *dmabufr_class;
static struct device *dmabufr_device;
static DEFINE_MUTEX(dmabufr_io_mutex);
/*
* Initialise dmabufr
*/
static int __init dmabufr_init(void)
{
printk(KERN_INFO "dmabufr offscreen device for SGX\n");
major = register_chrdev(0, DMABUFR_NAME, &dmabufr_fops);
if (major <= 0)
{
printk(KERN_WARNING "dmabufr: unable to get major number\n");
return -1;
}
dmabufr_class = class_create(THIS_MODULE, DMABUFR_NAME);
if (IS_ERR(dmabufr_class))
{
printk(KERN_WARNING "dmabufr_dev: class_register failed\n");
return -EIO;
}
dmabufr_device = device_create(dmabufr_class, NULL, MKDEV(major, 0), NULL,
DMABUFR_NAME);
if (IS_ERR(dmabufr_device))
{
printk(KERN_ERR DMABUFR_NAME ": unable to create device\n");
class_destroy(dmabufr_class);
unregister_chrdev(major, DMABUFR_NAME);
return -1;
}
return 0;
}
static void __exit dmabufr_exit(void)
{
dev_t dev = MKDEV(major, 0);
class_unregister(dmabufr_class);
unregister_chrdev_region(dev, DMABUFR_NUM_DEVICES);
}
static int dmabufr_open(struct inode *i, struct file *f)
{
/* Nothing to do now */
}
static int dmabufr_release(struct inode *i, struct file *f)
{
/* Nothing to do */
}
/********************************************************************************
* *
* *
* IOCTL implementation for the device
* *
* *
/********************************************************************************/
static void dmabufr_dummy_dma_buf_op_handler()
{
}
static struct dma_buf *curr_dma_buf;
static struct dma_buf_attachment *curr_dma_buf_attachment;
static struct sg_table *curr_sg_table;
static struct dma_buf_ops dma_ops =
{
.map_dma_buf = dmabufr_dummy_dma_buf_op_handler,
.unmap_dma_buf = dmabufr_dummy_dma_buf_op_handler,
.release = dmabufr_dummy_dma_buf_op_handler,
.begin_cpu_access = dmabufr_dummy_dma_buf_op_handler,
.end_cpu_access = dmabufr_dummy_dma_buf_op_handler,
.kmap_atomic = dmabufr_dummy_dma_buf_op_handler,
.kunmap_atomic = dmabufr_dummy_dma_buf_op_handler,
.kmap = dmabufr_dummy_dma_buf_op_handler,
.kunmap = dmabufr_dummy_dma_buf_op_handler,
.mmap = dmabufr_dummy_dma_buf_op_handler
};
typedef struct _FD_INFO
{
int fd;
void* pBuf;
}FD_INFO;
static FD_INFO fdInfo = {0};
int ioctl_request_fd()
{
/* The buffer exporter announces its wish to export a buffer. In this, it
connects its own private buffer data, provides implementation for operations
that can be performed on the exported dma_buf, and flags for the file
associated with this buffer.
Interface:
struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops,
size_t size, int flags)
*/
/*
Userspace entity requests for a file-descriptor (fd) which is a handle to the
anonymous file associated with the buffer. It can then share the fd with other
drivers and/or processes.
Interface:
int dma_buf_fd(struct dma_buf *dmabuf)
*/
int fd;
curr_dma_buf = dma_buf_export(NULL, &dma_ops, dma_size, 0);
fd = dma_buf_fd(curr_dma_buf);
return fd;
}
int ioctl_connect(int fd)
{
/*
Each buffer-user 'connects' itself to the buffer
Each buffer-user now gets a reference to the buffer, using the fd passed to
it.
Interface:
struct dma_buf *dma_buf_get(int fd)
After this, the buffer-user needs to attach its device with the buffer, which
helps the exporter to know of device buffer constraints.
Interface:
struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
struct device *dev)
*/
struct dma_buf* dma_buf_stored = dma_buf_get(fd); //todo - store this in some context
curr_dma_buf_attachment = dma_buf_attach(dma_buf_stored, dmabufr_device);
return 0;
}
int ioctl_use_buffer(int fd)
{
/*
buffer-user requests access to the buffer
Whenever a buffer-user wants to use the buffer for any DMA, it asks for
access to the buffer using dma_buf_map_attachment API. At least one attach to
the buffer must have happened before map_dma_buf can be called.
Interface:
struct sg_table * dma_buf_map_attachment(struct dma_buf_attachment *,
enum dma_data_direction);
*/
curr_sg_table = dma_buf_map_attachment(curr_dma_buf_attachment, DMA_BIDIRECTIONAL);
/* todo - use these sg buffers to do some operation on the device */
return 0;
}
int ioctl_end_of_operation(int fd)
{
/*
When finished, the buffer-user notifies end-of-DMA to exporter
Once the DMA for the current buffer-user is over, it signals 'end-of-DMA' to
the exporter using the dma_buf_unmap_attachment API.
Interface:
void dma_buf_unmap_attachment(struct dma_buf_attachment *,
struct sg_table *);
*/
/* todo - use fd from userland to get back the contexts */
dma_buf_unmap_attachment(curr_dma_buf_attachment, curr_sg_table);
return 0;
}
int ioctl_detach(int fd)
{
/*
when buffer-user is done using this buffer, it 'disconnects' itself from the
buffer.
After the buffer-user has no more interest in using this buffer, it should
disconnect itself from the buffer:
- it first detaches itself from the buffer.
Interface:
void dma_buf_detach(struct dma_buf *dmabuf,
struct dma_buf_attachment *dmabuf_attach);
Then, the buffer-user returns the buffer reference to exporter.
Interface:
void dma_buf_put(struct dma_buf *dmabuf);
*/
/* todo - use fd from userland to get back the contexts */
dma_buf_detach(curr_dma_buf, curr_dma_buf_attachment);
dma_buf_put(curr_dma_buf);
return 0;
}
/********************************************************************************
* *
* *
* IOCTL implementation for the device
* *
* *
/********************************************************************************/
static long dmabufr_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg)
{
int res;
mutex_lock(&dmabufr_io_mutex);
res = dmabufr_ioctl(file, cmd, arg);
mutex_unlock(&dmabufr_io_mutex);
return res;
}
static long dmabufr_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
DMABUFR_IO* dmabufr_io_pointer = (DMABUFR_IO*)arg;
long ret = 0;
switch(cmd)
{
case DMABUFR_IOCTL_REQUEST_FD:
{
int fd;
DMABUFR_ALLOCATION_INFO info;
if (!access_ok(VERIFY_WRITE, arg, sizeof(DMABUFR_IO)))
return -EFAULT;
if(copy_from_user(&info, dmabufr_io_pointer->input, sizeof(DMABUFR_ALLOCATION_INFO)))
return -EFAULT;
if(info.format != FMT_ARGB) return -EFAULT;
if(fdInfo.fd != 0) return -EFAULT; //only 1
dma_size = 4*info.width*info.height;
fdInfo.pBuf = kmalloc(4*info.width*info.height, GFP_KERNEL);
fdInfo.fd = ioctl_request_fd();
dmabufr_io_pointer->output = (void*)fd;
break;
}
case DMABUFR_IOCTL_CONNECT_FD:
{
int fd;
if (!access_ok(VERIFY_READ, arg, sizeof(DMABUFR_IO)))
return -EFAULT;
fd = dmabufr_io_pointer->input;
ret = ioctl_connect(fd);
break;
}
case DMABUFR_IOCTL_USE_BUFFER_FD:
{
int fd;
if (!access_ok(VERIFY_READ, arg, sizeof(DMABUFR_IO)))
return -EFAULT;
fd = (int)dmabufr_io_pointer->input;
ret = ioctl_use_buffer(fd);
break;
}
case DMABUFR_IOCTL_END_OF_OPERATION_FD:
{
int fd;
if (!access_ok(VERIFY_READ, arg, sizeof(DMABUFR_IO)))
return -EFAULT;
fd = (int)dmabufr_io_pointer->input;
ret = ioctl_end_of_operation(fd);
break;
}
case DMABUFR_IOCTL_DETACH_FD:
{
int fd;
if (!access_ok(VERIFY_READ, arg, sizeof(DMABUFR_IO)))
return -EFAULT;
fd = (int)dmabufr_io_pointer->input;
ret = ioctl_detach(fd);
break;
}
default:
{
printk("ERR: Undefined IOCTL cmd %x\n", cmd);
ret = -EFAULT;
break;
}
}
return ret;
}
/********************************************************************************
* *
* *
* End of file
* *
* *
/********************************************************************************/
late_initcall(dmabufr_init);
module_exit(dmabufr_exit)
MODULE_AUTHOR("Prabindh Sundareson <prabu@ti.com>");
MODULE_DESCRIPTION("Wrapper driver for offscreen dma-buf memory device for SGX");
MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(DMABUFR_MAJOR);