/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/umem.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 umem.c
4
 
 * @brief This file contains the functions handling user space memory.
5
 
 * @author Guillermo Marcus
6
 
 * @date 2009-04-05
7
 
 *
8
 
 */
9
 
#include <linux/version.h>
10
 
#include <linux/string.h>
11
 
#include <linux/types.h>
12
 
#include <linux/list.h>
13
 
#include <linux/interrupt.h>
14
 
#include <linux/pci.h>
15
 
#include <linux/cdev.h>
16
 
#include <linux/wait.h>
17
 
#include <linux/mm.h>
18
 
#include <linux/pagemap.h>
19
 
#include <linux/sched.h>
20
 
 
21
 
#include "config.h"                     /* compile-time configuration */
22
 
#include "compat.h"                     /* compatibility definitions for older linux */
23
 
#include "pciDriver.h"                  /* external interface for the driver */
24
 
#include "common.h"             /* internal definitions for all parts */
25
 
#include "umem.h"               /* prototypes for kernel memory */
26
 
#include "sysfs.h"              /* prototypes for sysfs */
27
 
 
28
 
/**
29
 
 *
30
 
 * Reserve a new scatter/gather list and map it from memory to PCI bus addresses.
31
 
 *
32
 
 */
33
 
int pcidriver_umem_sgmap(pcidriver_privdata_t *privdata, umem_handle_t *umem_handle)
34
 
{
35
 
        int i, res, nr_pages;
36
 
        struct page **pages;
37
 
        struct scatterlist *sg = NULL;
38
 
        pcidriver_umem_entry_t *umem_entry;
39
 
        unsigned int nents;
40
 
        unsigned long count,offset,length;
41
 
 
42
 
        /*
43
 
         * We do some checks first. Then, the following is necessary to create a
44
 
         * Scatter/Gather list from a user memory area:
45
 
         *  - Determine the number of pages
46
 
         *  - Get the pages for the memory area
47
 
         *      - Lock them.
48
 
         *  - Create a scatter/gather list of the pages
49
 
         *  - Map the list from memory to PCI bus addresses
50
 
         *
51
 
         * Then, we:
52
 
         *  - Create an entry on the umem list of the device, to cache the mapping.
53
 
         *  - Create a sysfs attribute that gives easy access to the SG list
54
 
         */
55
 
 
56
 
        /* zero-size?? */
57
 
        if (umem_handle->size == 0)
58
 
                return -EINVAL;
59
 
 
60
 
        /* Direction is better ignoring during mapping. */
61
 
        /* We assume bidirectional buffers always, except when sync'ing */
62
 
 
63
 
        /* calculate the number of pages */
64
 
        nr_pages = ((umem_handle->vma & ~PAGE_MASK) + umem_handle->size + ~PAGE_MASK) >> PAGE_SHIFT;
65
 
 
66
 
        mod_info_dbg("nr_pages computed: %u\n", nr_pages);
67
 
 
68
 
        /* Allocate space for the page information */
69
 
        /* This can be very big, so we use vmalloc */
70
 
        if ((pages = vmalloc(nr_pages * sizeof(*pages))) == NULL)
71
 
                return -ENOMEM;
72
 
 
73
 
        mod_info_dbg("allocated space for the pages.\n");
74
 
 
75
 
        /* Allocate space for the scatterlist */
76
 
        /* We do not know how many entries will be, but the maximum is nr_pages. */
77
 
        /* This can be very big, so we use vmalloc */
78
 
        if ((sg = vmalloc(nr_pages * sizeof(*sg))) == NULL)
79
 
                goto umem_sgmap_pages;
80
 
 
81
 
        sg_init_table(sg, nr_pages);
82
 
 
83
 
        mod_info_dbg("allocated space for the SG list.\n");
84
 
 
85
 
        /* Get the page information */
86
 
        down_read(&current->mm->mmap_sem);
87
 
        res = get_user_pages(
88
 
                                current,
89
 
                                current->mm,
90
 
                                umem_handle->vma,
91
 
                                nr_pages,
92
 
                                1,
93
 
                                0,  /* do not force, FIXME: shall I? */
94
 
                                pages,
95
 
                                NULL );
96
 
        up_read(&current->mm->mmap_sem);
97
 
 
98
 
        /* Error, not all pages mapped */
99
 
        if (res < (int)nr_pages) {
100
 
                mod_info("Could not map all user pages (%d of %d)\n", res, nr_pages);
101
 
                /* If only some pages could be mapped, we release those. If a real
102
 
                 * error occured, we set nr_pages to 0 */
103
 
                nr_pages = (res > 0 ? res : 0);
104
 
                goto umem_sgmap_unmap;
105
 
        }
106
 
 
107
 
        mod_info_dbg("Got the pages (%d).\n", res);
108
 
 
109
 
        /* Lock the pages, then populate the SG list with the pages */
110
 
        /* page0 is different */
111
 
        if ( !PageReserved(pages[0]) )
112
 
                compat_lock_page(pages[0]);
113
 
 
114
 
        offset = (umem_handle->vma & ~PAGE_MASK);
115
 
        length = (umem_handle->size > (PAGE_SIZE-offset) ? (PAGE_SIZE-offset) : umem_handle->size);
116
 
 
117
 
        sg_set_page(&sg[0], pages[0], length, offset);
118
 
 
119
 
        count = umem_handle->size - length;
120
 
        for(i=1;i<nr_pages;i++) {
121
 
                /* Lock page first */
122
 
                if ( !PageReserved(pages[i]) )
123
 
                        compat_lock_page(pages[i]);
124
 
 
125
 
                /* Populate the list */
126
 
                sg_set_page(&sg[i], pages[i], ((count > PAGE_SIZE) ? PAGE_SIZE : count), 0);
127
 
                count -= sg[i].length;
128
 
        }
129
 
 
130
 
        /* Use the page list to populate the SG list */
131
 
        /* SG entries may be merged, res is the number of used entries */
132
 
        /* We have originally nr_pages entries in the sg list */
133
 
        if ((nents = pci_map_sg(privdata->pdev, sg, nr_pages, PCI_DMA_BIDIRECTIONAL)) == 0)
134
 
                goto umem_sgmap_unmap;
135
 
 
136
 
        mod_info_dbg("Mapped SG list (%d entries).\n", nents);
137
 
 
138
 
        /* Add an entry to the umem_list of the device, and update the handle with the id */
139
 
        /* Allocate space for the new umem entry */
140
 
        if ((umem_entry = kmalloc(sizeof(*umem_entry), GFP_KERNEL)) == NULL)
141
 
                goto umem_sgmap_entry;
142
 
 
143
 
        /* Fill entry to be added to the umem list */
144
 
        umem_entry->id = atomic_inc_return(&privdata->umem_count) - 1;
145
 
        umem_entry->nr_pages = nr_pages;        /* Will be needed when unmapping */
146
 
        umem_entry->pages = pages;
147
 
        umem_entry->nents = nents;
148
 
        umem_entry->sg = sg;
149
 
 
150
 
        if (pcidriver_sysfs_initialize_umem(privdata, umem_entry->id, &(umem_entry->sysfs_attr)) != 0)
151
 
                goto umem_sgmap_name_fail;
152
 
 
153
 
        /* Add entry to the umem list */
154
 
        spin_lock( &(privdata->umemlist_lock) );
155
 
        list_add_tail( &(umem_entry->list), &(privdata->umem_list) );
156
 
        spin_unlock( &(privdata->umemlist_lock) );
157
 
 
158
 
        /* Update the Handle with the Handle ID of the entry */
159
 
        umem_handle->handle_id = umem_entry->id;
160
 
 
161
 
        return 0;
162
 
 
163
 
umem_sgmap_name_fail:
164
 
        kfree(umem_entry);
165
 
umem_sgmap_entry:
166
 
        pci_unmap_sg( privdata->pdev, sg, nr_pages, PCI_DMA_BIDIRECTIONAL );
167
 
umem_sgmap_unmap:
168
 
        /* release pages */
169
 
        if (nr_pages > 0) {
170
 
                for(i=0;i<nr_pages;i++) {
171
 
                        if (PageLocked(pages[i]))
172
 
                                compat_unlock_page(pages[i]);
173
 
                        if (!PageReserved(pages[i]))
174
 
                                set_page_dirty(pages[i]);
175
 
                        page_cache_release(pages[i]);
176
 
                }
177
 
        }
178
 
        vfree(sg);
179
 
umem_sgmap_pages:
180
 
        vfree(pages);
181
 
        return -ENOMEM;
182
 
 
183
 
}
184
 
 
185
 
/**
186
 
 *
187
 
 * Unmap a scatter/gather list
188
 
 *
189
 
 */
190
 
int pcidriver_umem_sgunmap(pcidriver_privdata_t *privdata, pcidriver_umem_entry_t *umem_entry)
191
 
{
192
 
        int i;
193
 
        pcidriver_sysfs_remove(privdata, &(umem_entry->sysfs_attr));
194
 
 
195
 
        /* Unmap user memory */
196
 
        pci_unmap_sg( privdata->pdev, umem_entry->sg, umem_entry->nr_pages, PCI_DMA_BIDIRECTIONAL );
197
 
 
198
 
        /* Release the pages */
199
 
        if (umem_entry->nr_pages > 0) {
200
 
                for(i=0;i<(umem_entry->nr_pages);i++) {
201
 
                        /* Mark pages as Dirty and unlock it */
202
 
                        if ( !PageReserved( umem_entry->pages[i] )) {
203
 
                                SetPageDirty( umem_entry->pages[i] );
204
 
                                compat_unlock_page(umem_entry->pages[i]);
205
 
                        }
206
 
                        /* and release it from the cache */
207
 
                        page_cache_release( umem_entry->pages[i] );
208
 
                }
209
 
        }
210
 
 
211
 
        /* Remove the umem list entry */
212
 
        spin_lock( &(privdata->umemlist_lock) );
213
 
        list_del( &(umem_entry->list) );
214
 
        spin_unlock( &(privdata->umemlist_lock) );
215
 
 
216
 
        /* Release SG list and page list memory */
217
 
        /* These two are in the vm area of the kernel */
218
 
        vfree(umem_entry->pages);
219
 
        vfree(umem_entry->sg);
220
 
 
221
 
        /* Release umem_entry memory */
222
 
        kfree(umem_entry);
223
 
 
224
 
        return 0;
225
 
}
226
 
 
227
 
/**
228
 
 *
229
 
 * Unmap all scatter/gather lists.
230
 
 *
231
 
 */
232
 
int pcidriver_umem_sgunmap_all(pcidriver_privdata_t *privdata)
233
 
{
234
 
        struct list_head *ptr, *next;
235
 
        pcidriver_umem_entry_t *umem_entry;
236
 
 
237
 
        /* iterate safely over the entries and delete them */
238
 
        list_for_each_safe( ptr, next, &(privdata->umem_list) ) {
239
 
                umem_entry = list_entry(ptr, pcidriver_umem_entry_t, list );
240
 
                pcidriver_umem_sgunmap( privdata, umem_entry );                 /* spin lock inside! */
241
 
        }
242
 
 
243
 
        return 0;
244
 
}
245
 
 
246
 
/**
247
 
 *
248
 
 * Copies the scatter/gather list from kernelspace to userspace.
249
 
 *
250
 
 */
251
 
int pcidriver_umem_sgget(pcidriver_privdata_t *privdata, umem_sglist_t *umem_sglist)
252
 
{
253
 
        int i;
254
 
        pcidriver_umem_entry_t *umem_entry;
255
 
        struct scatterlist *sg;
256
 
        int idx = 0;
257
 
        dma_addr_t cur_addr;
258
 
        unsigned int cur_size;
259
 
 
260
 
        /* Find the associated umem_entry for this buffer */
261
 
        umem_entry = pcidriver_umem_find_entry_id( privdata, umem_sglist->handle_id );
262
 
        if (umem_entry == NULL)
263
 
                return -EINVAL;                                 /* umem_handle is not valid */
264
 
 
265
 
        /* Check if passed SG list is enough */
266
 
        if (umem_sglist->nents < umem_entry->nents)
267
 
                return -EINVAL;                                 /* sg has not enough entries */
268
 
 
269
 
        /* Copy the SG list to the user format */
270
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
271
 
        if (umem_sglist->type == PCIDRIVER_SG_MERGED) {
272
 
                for_each_sg(umem_entry->sg, sg, umem_entry->nents, i ) {
273
 
                        if (i==0) {
274
 
                                umem_sglist->sg[0].addr = sg_dma_address( sg );
275
 
                                umem_sglist->sg[0].size = sg_dma_len( sg );
276
 
                                idx = 0;
277
 
                        }
278
 
                        else {
279
 
                                cur_addr = sg_dma_address( sg );
280
 
                                cur_size = sg_dma_len( sg );
281
 
 
282
 
                                /* Check if entry fits after current entry */
283
 
                                if (cur_addr == (umem_sglist->sg[idx].addr + umem_sglist->sg[idx].size)) {
284
 
                                        umem_sglist->sg[idx].size += cur_size;
285
 
                                        continue;
286
 
                                }
287
 
 
288
 
                                /* Skip if the entry is zero-length (yes, it can happen.... at the end of the list) */
289
 
                                if (cur_size == 0)
290
 
                                        continue;
291
 
 
292
 
                                /* None of the above, add new entry */
293
 
                                idx++;
294
 
                                umem_sglist->sg[idx].addr = cur_addr;
295
 
                                umem_sglist->sg[idx].size = cur_size;
296
 
                        }
297
 
                }
298
 
                /* Set the used size of the SG list */
299
 
                umem_sglist->nents = idx+1;
300
 
        } else {
301
 
                for_each_sg(umem_entry->sg, sg, umem_entry->nents, i ) {
302
 
                        mod_info("entry: %d\n",i);
303
 
                        umem_sglist->sg[i].addr = sg_dma_address( sg );
304
 
                        umem_sglist->sg[i].size = sg_dma_len( sg );
305
 
                }
306
 
 
307
 
                /* Set the used size of the SG list */
308
 
                /* Check if the last one is zero-length */
309
 
                if ( umem_sglist->sg[ umem_entry->nents - 1].size == 0)
310
 
                        umem_sglist->nents = umem_entry->nents -1;
311
 
                else
312
 
                        umem_sglist->nents = umem_entry->nents;
313
 
        }
314
 
#else
315
 
        if (umem_sglist->type == PCIDRIVER_SG_MERGED) {
316
 
                /* Merge entries that are contiguous into a single entry */
317
 
                /* Non-optimal but fast for most cases */
318
 
                /* First one always true */
319
 
                sg=umem_entry->sg;
320
 
                umem_sglist->sg[0].addr = sg_dma_address( sg );
321
 
                umem_sglist->sg[0].size = sg_dma_len( sg );
322
 
                sg++;
323
 
                idx = 0;
324
 
 
325
 
                /* Iterate over the SG entries */
326
 
                for(i=1; i< umem_entry->nents; i++, sg++ ) {
327
 
                        cur_addr = sg_dma_address( sg );
328
 
                        cur_size = sg_dma_len( sg );
329
 
 
330
 
                        /* Check if entry fits after current entry */
331
 
                        if (cur_addr == (umem_sglist->sg[idx].addr + umem_sglist->sg[idx].size)) {
332
 
                                umem_sglist->sg[idx].size += cur_size;
333
 
                                continue;
334
 
                        }
335
 
 
336
 
                        /* Skip if the entry is zero-length (yes, it can happen.... at the end of the list) */
337
 
                        if (cur_size == 0)
338
 
                                continue;
339
 
 
340
 
                        /* None of the above, add new entry */
341
 
                        idx++;
342
 
                        umem_sglist->sg[idx].addr = cur_addr;
343
 
                        umem_sglist->sg[idx].size = cur_size;
344
 
                }
345
 
                /* Set the used size of the SG list */
346
 
                umem_sglist->nents = idx+1;
347
 
        } else {
348
 
                /* Assume pci_map_sg made a good job (ehem..) and just copy it.
349
 
                 * actually, now I assume it just gives them plainly to me. */
350
 
                for(i=0, sg=umem_entry->sg ; i< umem_entry->nents; i++, sg++ ) {
351
 
                        umem_sglist->sg[i].addr = sg_dma_address( sg );
352
 
                        umem_sglist->sg[i].size = sg_dma_len( sg );
353
 
                }
354
 
                /* Set the used size of the SG list */
355
 
                /* Check if the last one is zero-length */
356
 
                if ( umem_sglist->sg[ umem_entry->nents - 1].size == 0)
357
 
                        umem_sglist->nents = umem_entry->nents -1;
358
 
                else
359
 
                        umem_sglist->nents = umem_entry->nents;
360
 
        }
361
 
#endif
362
 
 
363
 
        return 0;
364
 
}
365
 
 
366
 
/**
367
 
 *
368
 
 * Sync user space memory from/to device
369
 
 *
370
 
 */
371
 
int pcidriver_umem_sync( pcidriver_privdata_t *privdata, umem_handle_t *umem_handle )
372
 
{
373
 
        pcidriver_umem_entry_t *umem_entry;
374
 
 
375
 
        /* Find the associated umem_entry for this buffer */
376
 
        umem_entry = pcidriver_umem_find_entry_id( privdata, umem_handle->handle_id );
377
 
        if (umem_entry == NULL)
378
 
                return -EINVAL;                                 /* umem_handle is not valid */
379
 
 
380
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
381
 
        switch (umem_handle->dir) {
382
 
                case PCIDRIVER_DMA_TODEVICE:
383
 
                        pci_dma_sync_sg_for_device( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_TODEVICE );
384
 
                        break;
385
 
                case PCIDRIVER_DMA_FROMDEVICE:
386
 
                        pci_dma_sync_sg_for_cpu( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_FROMDEVICE );
387
 
                        break;
388
 
                case PCIDRIVER_DMA_BIDIRECTIONAL:
389
 
                        pci_dma_sync_sg_for_device( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL );
390
 
                        pci_dma_sync_sg_for_cpu( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL );
391
 
                        break;
392
 
                default:
393
 
                        return -EINVAL;                         /* wrong direction parameter */
394
 
        }
395
 
#else
396
 
        switch (umem_handle->dir) {
397
 
                case PCIDRIVER_DMA_TODEVICE:
398
 
                        pci_dma_sync_sg( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_TODEVICE );
399
 
                        break;
400
 
                case PCIDRIVER_DMA_FROMDEVICE:
401
 
                        pci_dma_sync_sg( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_FROMDEVICE );
402
 
                        break;
403
 
                case PCIDRIVER_DMA_BIDIRECTIONAL:
404
 
                        pci_dma_sync_sg( privdata->pdev, umem_entry->sg, umem_entry->nents, PCI_DMA_BIDIRECTIONAL );
405
 
                        break;
406
 
                default:
407
 
                        return -EINVAL;                         /* wrong direction parameter */
408
 
        }
409
 
#endif
410
 
 
411
 
        return 0;
412
 
}
413
 
 
414
 
/*
415
 
 *
416
 
 * Get the pcidriver_umem_entry_t structure for the given id.
417
 
 *
418
 
 * @param id ID of the umem entry to search for
419
 
 *
420
 
 */
421
 
pcidriver_umem_entry_t *pcidriver_umem_find_entry_id(pcidriver_privdata_t *privdata, int id)
422
 
{
423
 
        struct list_head *ptr;
424
 
        pcidriver_umem_entry_t *entry;
425
 
 
426
 
        spin_lock(&(privdata->umemlist_lock));
427
 
        list_for_each(ptr, &(privdata->umem_list)) {
428
 
                entry = list_entry(ptr, pcidriver_umem_entry_t, list );
429
 
 
430
 
                if (entry->id == id) {
431
 
                        spin_unlock( &(privdata->umemlist_lock) );
432
 
                        return entry;
433
 
                }
434
 
        }
435
 
 
436
 
        spin_unlock(&(privdata->umemlist_lock));
437
 
        return NULL;
438
 
}