/alps/ipecamera

To get this branch, use:
bzr branch http://darksoft.org/webbzr/alps/ipecamera

« back to all changes in this revision

Viewing changes to driver/ioctl.c

  • Committer: Suren A. Chilingaryan
  • Date: 2015-04-27 00:28:57 UTC
  • Revision ID: csa@suren.me-20150427002857-82fk6r3e8rfgy4wr
First stand-alone ipecamera implementation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 *
3
 
 * @file ioctl.c
4
 
 * @author Guillermo Marcus
5
 
 * @date 2009-04-05
6
 
 * @brief Contains the functions handling the different ioctl calls.
7
 
 *
8
 
 */
9
 
#include <linux/version.h>
10
 
#include <linux/string.h>
11
 
#include <linux/slab.h>
12
 
#include <linux/types.h>
13
 
#include <linux/init.h>
14
 
#include <linux/module.h>
15
 
#include <linux/pci.h>
16
 
#include <linux/kernel.h>
17
 
#include <linux/errno.h>
18
 
#include <linux/fs.h>
19
 
#include <linux/cdev.h>
20
 
#include <linux/sysfs.h>
21
 
#include <asm/atomic.h>
22
 
#include <linux/pagemap.h>
23
 
#include <linux/spinlock.h>
24
 
#include <linux/list.h>
25
 
#include <asm/scatterlist.h>
26
 
#include <linux/vmalloc.h>
27
 
#include <linux/stat.h>
28
 
#include <linux/interrupt.h>
29
 
#include <linux/wait.h>
30
 
#include <linux/sched.h>
31
 
 
32
 
#include "config.h"                     /* Configuration for the driver */
33
 
#include "compat.h"                     /* Compatibility functions/definitions */
34
 
#include "pciDriver.h"                  /* External interface for the driver */
35
 
#include "common.h"                     /* Internal definitions for all parts */
36
 
#include "kmem.h"                       /* Internal definitions for kernel memory */
37
 
#include "umem.h"                       /* Internal definitions for user space memory */
38
 
#include "ioctl.h"                      /* Internal definitions for the ioctl part */
39
 
 
40
 
/** Declares a variable of the given type with the given name and copies it from userspace */
41
 
#define READ_FROM_USER(type, name) \
42
 
        type name; \
43
 
        if ((ret = copy_from_user(&name, (type*)arg, sizeof(name))) != 0) \
44
 
                return -EFAULT;
45
 
 
46
 
/** Writes back the given variable with the given type to userspace */
47
 
#define WRITE_TO_USER(type, name) \
48
 
        if ((ret = copy_to_user((type*)arg, &name, sizeof(name))) != 0) \
49
 
                return -EFAULT;
50
 
 
51
 
/**
52
 
 *
53
 
 * Sets the mmap mode for following mmap() calls.
54
 
 *
55
 
 * @param arg Not a pointer, but either PCIDRIVER_MMAP_PCI or PCIDRIVER_MMAP_KMEM
56
 
 *
57
 
 */
58
 
static int ioctl_mmap_mode(pcidriver_privdata_t *privdata, unsigned long arg)
59
 
{
60
 
        if ((arg != PCIDRIVER_MMAP_PCI) && (arg != PCIDRIVER_MMAP_KMEM))
61
 
                return -EINVAL;
62
 
 
63
 
        /* change the mode */
64
 
        privdata->mmap_mode = arg;
65
 
 
66
 
        return 0;
67
 
}
68
 
 
69
 
/**
70
 
 *
71
 
 * Sets the mmap area (BAR) for following mmap() calls.
72
 
 *
73
 
 */
74
 
static int ioctl_mmap_area(pcidriver_privdata_t *privdata, unsigned long arg)
75
 
{
76
 
        /* validate input */
77
 
        if ((arg < PCIDRIVER_BAR0) || (arg > PCIDRIVER_BAR5))
78
 
                return -EINVAL;
79
 
 
80
 
        /* change the PCI area to mmap */
81
 
        privdata->mmap_area = arg;
82
 
 
83
 
        return 0;
84
 
}
85
 
 
86
 
/**
87
 
 *
88
 
 * Reads/writes a byte/word/dword of the device's PCI config.
89
 
 *
90
 
 * @see pcidriver_pci_read
91
 
 * @see pcidriver_pci_write
92
 
 *
93
 
 */
94
 
static int ioctl_pci_config_read_write(pcidriver_privdata_t *privdata, unsigned int cmd, unsigned long arg)
95
 
{
96
 
        int ret;
97
 
        READ_FROM_USER(pci_cfg_cmd, pci_cmd);
98
 
 
99
 
        if (cmd == PCIDRIVER_IOC_PCI_CFG_RD) {
100
 
                switch (pci_cmd.size) {
101
 
                        case PCIDRIVER_PCI_CFG_SZ_BYTE:
102
 
                                ret = pci_read_config_byte( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.byte) );
103
 
                                break;
104
 
                        case PCIDRIVER_PCI_CFG_SZ_WORD:
105
 
                                ret = pci_read_config_word( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.word) );
106
 
                                break;
107
 
                        case PCIDRIVER_PCI_CFG_SZ_DWORD:
108
 
                                ret = pci_read_config_dword( privdata->pdev, pci_cmd.addr, &(pci_cmd.val.dword) );
109
 
                                break;
110
 
                        default:
111
 
                                return -EINVAL;         /* Wrong size setting */
112
 
                }
113
 
        } else {
114
 
                switch (pci_cmd.size) {
115
 
                        case PCIDRIVER_PCI_CFG_SZ_BYTE:
116
 
                                ret = pci_write_config_byte( privdata->pdev, pci_cmd.addr, pci_cmd.val.byte );
117
 
                                break;
118
 
                        case PCIDRIVER_PCI_CFG_SZ_WORD:
119
 
                                ret = pci_write_config_word( privdata->pdev, pci_cmd.addr, pci_cmd.val.word );
120
 
                                break;
121
 
                        case PCIDRIVER_PCI_CFG_SZ_DWORD:
122
 
                                ret = pci_write_config_dword( privdata->pdev, pci_cmd.addr, pci_cmd.val.dword );
123
 
                                break;
124
 
                        default:
125
 
                                return -EINVAL;         /* Wrong size setting */
126
 
                                break;
127
 
                }
128
 
        }
129
 
 
130
 
        WRITE_TO_USER(pci_cfg_cmd, pci_cmd);
131
 
 
132
 
        return 0;
133
 
}
134
 
 
135
 
/**
136
 
 *
137
 
 * Gets the PCI information for the device.
138
 
 *
139
 
 * @see pcidriver_pci_info
140
 
 *
141
 
 */
142
 
static int ioctl_pci_info(pcidriver_privdata_t *privdata, unsigned long arg)
143
 
{
144
 
        int ret;
145
 
        int bar;
146
 
        READ_FROM_USER(pcilib_board_info_t, pci_info);
147
 
 
148
 
        pci_info.vendor_id = privdata->pdev->vendor;
149
 
        pci_info.device_id = privdata->pdev->device;
150
 
        pci_info.bus = privdata->pdev->bus->number;
151
 
        pci_info.slot = PCI_SLOT(privdata->pdev->devfn);
152
 
        pci_info.devfn = privdata->pdev->devfn;
153
 
        pci_info.func = PCI_FUNC(privdata->pdev->devfn);
154
 
 
155
 
        if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_PIN, &(pci_info.interrupt_pin))) != 0)
156
 
                return ret;
157
 
 
158
 
        if ((ret = pci_read_config_byte(privdata->pdev, PCI_INTERRUPT_LINE, &(pci_info.interrupt_line))) != 0)
159
 
                return ret;
160
 
 
161
 
        for (bar = 0; bar < 6; bar++) {
162
 
                pci_info.bar_start[bar] = pci_resource_start(privdata->pdev, bar);
163
 
                pci_info.bar_length[bar] = pci_resource_len(privdata->pdev, bar);
164
 
                pci_info.bar_flags[bar] = pci_resource_flags(privdata->pdev, bar);
165
 
        }
166
 
 
167
 
        WRITE_TO_USER(pcilib_board_info_t, pci_info);
168
 
 
169
 
        return 0;
170
 
}
171
 
 
172
 
/**
173
 
 *
174
 
 * Allocates kernel memory.
175
 
 *
176
 
 * @see pcidriver_kmem_alloc
177
 
 *
178
 
 */
179
 
static int ioctl_kmem_alloc(pcidriver_privdata_t *privdata, unsigned long arg)
180
 
{
181
 
        int ret;
182
 
        READ_FROM_USER(kmem_handle_t, khandle);
183
 
 
184
 
        if ((ret = pcidriver_kmem_alloc(privdata, &khandle)) != 0)
185
 
                return ret;
186
 
 
187
 
        WRITE_TO_USER(kmem_handle_t, khandle);
188
 
 
189
 
        return 0;
190
 
}
191
 
 
192
 
/**
193
 
 *
194
 
 * Frees kernel memory.
195
 
 *
196
 
 * @see pcidriver_kmem_free
197
 
 *
198
 
 */
199
 
static int ioctl_kmem_free(pcidriver_privdata_t *privdata, unsigned long arg)
200
 
{
201
 
        int ret;
202
 
        READ_FROM_USER(kmem_handle_t, khandle);
203
 
 
204
 
        if ((ret = pcidriver_kmem_free(privdata, &khandle)) != 0)
205
 
                return ret;
206
 
 
207
 
        return 0;
208
 
}
209
 
 
210
 
/**
211
 
 *
212
 
 * Syncs kernel memory.
213
 
 *
214
 
 * @see pcidriver_kmem_sync
215
 
 *
216
 
 */
217
 
static int ioctl_kmem_sync(pcidriver_privdata_t *privdata, unsigned long arg)
218
 
{
219
 
        int ret;
220
 
        READ_FROM_USER(kmem_sync_t, ksync);
221
 
 
222
 
        if ((ret =  pcidriver_kmem_sync(privdata, &ksync)) != 0)
223
 
            return ret;
224
 
        
225
 
        WRITE_TO_USER(kmem_sync_t, ksync);
226
 
        
227
 
        return 0;
228
 
}
229
 
 
230
 
/*
231
 
 *
232
 
 * Maps the given scatter/gather list from memory to PCI bus addresses.
233
 
 *
234
 
 * @see pcidriver_umem_sgmap
235
 
 *
236
 
 */
237
 
static int ioctl_umem_sgmap(pcidriver_privdata_t *privdata, unsigned long arg)
238
 
{
239
 
        int ret;
240
 
        READ_FROM_USER(umem_handle_t, uhandle);
241
 
 
242
 
        if ((ret = pcidriver_umem_sgmap(privdata, &uhandle)) != 0)
243
 
                return ret;
244
 
 
245
 
        WRITE_TO_USER(umem_handle_t, uhandle);
246
 
 
247
 
        return 0;
248
 
}
249
 
 
250
 
/**
251
 
 *
252
 
 * Unmaps the given scatter/gather list.
253
 
 *
254
 
 * @see pcidriver_umem_sgunmap
255
 
 *
256
 
 */
257
 
static int ioctl_umem_sgunmap(pcidriver_privdata_t *privdata, unsigned long arg)
258
 
{
259
 
        int ret;
260
 
        pcidriver_umem_entry_t *umem_entry;
261
 
        READ_FROM_USER(umem_handle_t, uhandle);
262
 
 
263
 
        /* Find the associated umem_entry for this buffer,
264
 
         * return -EINVAL if the specified handle id is invalid */
265
 
        if ((umem_entry = pcidriver_umem_find_entry_id(privdata, uhandle.handle_id)) == NULL)
266
 
                return -EINVAL;
267
 
 
268
 
        if ((ret = pcidriver_umem_sgunmap(privdata, umem_entry)) != 0)
269
 
                return ret;
270
 
 
271
 
        return 0;
272
 
}
273
 
 
274
 
/**
275
 
 *
276
 
 * Copies the scatter/gather list from kernelspace to userspace.
277
 
 *
278
 
 * @see pcidriver_umem_sgget
279
 
 *
280
 
 */
281
 
static int ioctl_umem_sgget(pcidriver_privdata_t *privdata, unsigned long arg)
282
 
{
283
 
        int ret;
284
 
        READ_FROM_USER(umem_sglist_t, usglist);
285
 
 
286
 
        /* The umem_sglist_t has a pointer to the scatter/gather list itself which
287
 
         * needs to be copied separately. The number of elements is stored in ->nents.
288
 
         * As the list can get very big, we need to use vmalloc. */
289
 
        if ((usglist.sg = vmalloc(usglist.nents * sizeof(umem_sgentry_t))) == NULL)
290
 
                return -ENOMEM;
291
 
 
292
 
        /* copy array to kernel structure */
293
 
        ret = copy_from_user(usglist.sg, ((umem_sglist_t *)arg)->sg, (usglist.nents)*sizeof(umem_sgentry_t));
294
 
        if (ret) return -EFAULT;
295
 
 
296
 
        if ((ret = pcidriver_umem_sgget(privdata, &usglist)) != 0)
297
 
                return ret;
298
 
 
299
 
        /* write data to user space */
300
 
        ret = copy_to_user(((umem_sglist_t *)arg)->sg, usglist.sg, (usglist.nents)*sizeof(umem_sgentry_t));
301
 
        if (ret) return -EFAULT;
302
 
 
303
 
        /* free array memory */
304
 
        vfree(usglist.sg);
305
 
 
306
 
        /* restore sg pointer to vma address in user space before copying */
307
 
        usglist.sg = ((umem_sglist_t *)arg)->sg;
308
 
 
309
 
        WRITE_TO_USER(umem_sglist_t, usglist);
310
 
 
311
 
        return 0;
312
 
}
313
 
 
314
 
/**
315
 
 *
316
 
 * Syncs user memory.
317
 
 *
318
 
 * @see pcidriver_umem_sync
319
 
 *
320
 
 */
321
 
static int ioctl_umem_sync(pcidriver_privdata_t *privdata, unsigned long arg)
322
 
{
323
 
        int ret;
324
 
        READ_FROM_USER(umem_handle_t, uhandle);
325
 
 
326
 
        return pcidriver_umem_sync( privdata, &uhandle );
327
 
}
328
 
 
329
 
/**
330
 
 *
331
 
 * Waits for an interrupt
332
 
 *
333
 
 * @param arg Not a pointer, but the irq source to wait for (unsigned int)
334
 
 *
335
 
 */
336
 
static int ioctl_wait_interrupt(pcidriver_privdata_t *privdata, unsigned long arg)
337
 
{
338
 
#ifdef ENABLE_IRQ
339
 
        int ret;
340
 
        unsigned long timeout;
341
 
        unsigned int irq_source;
342
 
        unsigned long temp = 0;
343
 
 
344
 
        READ_FROM_USER(interrupt_wait_t, irq_handle);
345
 
 
346
 
        irq_source = irq_handle.source;
347
 
 
348
 
        if (irq_source >= PCIDRIVER_INT_MAXSOURCES)
349
 
                return -EFAULT;                                         /* User tried to overrun the IRQ_SOURCES array */
350
 
 
351
 
        timeout = jiffies + (irq_handle.timeout * HZ / 1000000);
352
 
 
353
 
        /* Thanks to Joern for the correction and tips! */
354
 
        /* done this way to avoid wrong behaviour (endless loop) of the compiler in AMD platforms */
355
 
        do {
356
 
                /* We wait here with an interruptible timeout. This will be interrupted
357
 
                 * by int.c:check_acknowledge_channel() as soon as in interrupt for
358
 
                 * the specified source arrives. */
359
 
                wait_event_interruptible_timeout( (privdata->irq_queues[irq_source]), (atomic_read(&(privdata->irq_outstanding[irq_source])) > 0), (10*HZ/1000) );
360
 
 
361
 
                if (atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source])) )
362
 
                        atomic_inc( &(privdata->irq_outstanding[irq_source]) );
363
 
                else
364
 
                        temp = 1;
365
 
        } while ((!temp)&&(jiffies < timeout));
366
 
        
367
 
        if ((temp)&&(irq_handle.count)) {
368
 
            while (!atomic_add_negative( -1, &(privdata->irq_outstanding[irq_source]))) temp++;
369
 
            atomic_inc( &(privdata->irq_outstanding[irq_source]) );
370
 
        }
371
 
        
372
 
        irq_handle.count = temp;
373
 
 
374
 
        WRITE_TO_USER(interrupt_wait_t, irq_handle);
375
 
 
376
 
        return 0;
377
 
#else
378
 
        mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n");
379
 
        return -EFAULT;
380
 
#endif
381
 
}
382
 
 
383
 
/**
384
 
 *
385
 
 * Clears the interrupt wait queue.
386
 
 *
387
 
 * @param arg Not a pointer, but the irq source (unsigned int)
388
 
 * @returns -EFAULT if the user specified an irq source out of range
389
 
 *
390
 
 */
391
 
static int ioctl_clear_ioq(pcidriver_privdata_t *privdata, unsigned long arg)
392
 
{
393
 
#ifdef ENABLE_IRQ
394
 
        unsigned int irq_source;
395
 
 
396
 
        if (arg >= PCIDRIVER_INT_MAXSOURCES)
397
 
                return -EFAULT;
398
 
 
399
 
        irq_source = arg;
400
 
        atomic_set(&(privdata->irq_outstanding[irq_source]), 0);
401
 
 
402
 
        return 0;
403
 
#else
404
 
        mod_info("Asked to wait for interrupt but interrupts are not enabled in the driver\n");
405
 
        return -EFAULT;
406
 
#endif
407
 
}
408
 
 
409
 
/**
410
 
 *
411
 
 * This function handles all ioctl file operations.
412
 
 * Generally, the data of the ioctl is copied from userspace to kernelspace, a separate
413
 
 * function is called to handle the ioctl itself, then the data is copied back to userspace.
414
 
 *
415
 
 * @returns -EFAULT when an invalid memory pointer is passed
416
 
 *
417
 
 */
418
 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11)
419
 
int pcidriver_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
420
 
#else
421
 
long pcidriver_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
422
 
#endif
423
 
{
424
 
        pcidriver_privdata_t *privdata = filp->private_data;
425
 
 
426
 
        /* Select the appropiate command */
427
 
        switch (cmd) {
428
 
                case PCIDRIVER_IOC_MMAP_MODE:
429
 
                        return ioctl_mmap_mode(privdata, arg);
430
 
 
431
 
                case PCIDRIVER_IOC_MMAP_AREA:
432
 
                        return ioctl_mmap_area(privdata, arg);
433
 
 
434
 
                case PCIDRIVER_IOC_PCI_CFG_RD:
435
 
                case PCIDRIVER_IOC_PCI_CFG_WR:
436
 
                        return ioctl_pci_config_read_write(privdata, cmd, arg);
437
 
 
438
 
                case PCIDRIVER_IOC_PCI_INFO:
439
 
                        return ioctl_pci_info(privdata, arg);
440
 
 
441
 
                case PCIDRIVER_IOC_KMEM_ALLOC:
442
 
                        return ioctl_kmem_alloc(privdata, arg);
443
 
 
444
 
                case PCIDRIVER_IOC_KMEM_FREE:
445
 
                        return ioctl_kmem_free(privdata, arg);
446
 
 
447
 
                case PCIDRIVER_IOC_KMEM_SYNC:
448
 
                        return ioctl_kmem_sync(privdata, arg);
449
 
 
450
 
                case PCIDRIVER_IOC_UMEM_SGMAP:
451
 
                        return ioctl_umem_sgmap(privdata, arg);
452
 
 
453
 
                case PCIDRIVER_IOC_UMEM_SGUNMAP:
454
 
                        return ioctl_umem_sgunmap(privdata, arg);
455
 
 
456
 
                case PCIDRIVER_IOC_UMEM_SGGET:
457
 
                        return ioctl_umem_sgget(privdata, arg);
458
 
 
459
 
                case PCIDRIVER_IOC_UMEM_SYNC:
460
 
                        return ioctl_umem_sync(privdata, arg);
461
 
 
462
 
                case PCIDRIVER_IOC_WAITI:
463
 
                        return ioctl_wait_interrupt(privdata, arg);
464
 
 
465
 
                case PCIDRIVER_IOC_CLEAR_IOQ:
466
 
                        return ioctl_clear_ioq(privdata, arg);
467
 
 
468
 
                default:
469
 
                        return -EINVAL;
470
 
        }
471
 
}