/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/kmem.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 kmem.c
4
 
 * @brief This file contains all functions dealing with kernel 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
 
 
20
 
#include "config.h"                     /* compile-time configuration */
21
 
#include "compat.h"                     /* compatibility definitions for older linux */
22
 
#include "pciDriver.h"                  /* external interface for the driver */
23
 
#include "common.h"                     /* internal definitions for all parts */
24
 
#include "kmem.h"                       /* prototypes for kernel memory */
25
 
#include "sysfs.h"                      /* prototypes for sysfs */
26
 
 
27
 
/* VM_RESERVED is removed in 3.7-rc1 */
28
 
#ifndef VM_RESERVED
29
 
# define  VM_RESERVED   (VM_DONTEXPAND | VM_DONTDUMP)
30
 
#endif
31
 
 
32
 
/**
33
 
 *
34
 
 * Allocates new kernel memory including the corresponding management structure, makes
35
 
 * it available via sysfs if possible.
36
 
 *
37
 
 */
38
 
int pcidriver_kmem_alloc(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
39
 
{
40
 
        int flags;
41
 
        pcidriver_kmem_entry_t *kmem_entry;
42
 
        void *retptr;
43
 
 
44
 
        if (kmem_handle->flags&KMEM_FLAG_REUSE) {
45
 
            kmem_entry = pcidriver_kmem_find_entry_use(privdata, kmem_handle->use, kmem_handle->item);
46
 
            if (kmem_entry) {
47
 
                unsigned long flags = kmem_handle->flags;
48
 
                
49
 
                if (flags&KMEM_FLAG_TRY) {
50
 
                    kmem_handle->type = kmem_entry->type;
51
 
                    kmem_handle->size = kmem_entry->size;
52
 
                    kmem_handle->align = kmem_entry->align;
53
 
                } else {
54
 
                    if (kmem_handle->type != kmem_entry->type) {
55
 
                        mod_info("Invalid type of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->type, kmem_handle->type);
56
 
                        return -EINVAL;
57
 
                    }
58
 
 
59
 
                    if ((kmem_handle->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_PAGE) {
60
 
                            kmem_handle->size = kmem_entry->size;
61
 
                    } else if (kmem_handle->size != kmem_entry->size) {
62
 
                        mod_info("Invalid size of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->size, kmem_handle->size);
63
 
                        return -EINVAL;
64
 
                    }
65
 
                    
66
 
                    if (kmem_handle->align != kmem_entry->align) {
67
 
                        mod_info("Invalid alignment of reusable kmem_entry, currently: %lu, but requested: %lu\n", kmem_entry->align, kmem_handle->align);
68
 
                        return -EINVAL;
69
 
                    }
70
 
 
71
 
                    if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)?1:0) != ((flags&KMEM_FLAG_EXCLUSIVE)?1:0)) {
72
 
                        mod_info("Invalid mode of reusable kmem_entry\n");
73
 
                        return -EINVAL;
74
 
                    }
75
 
                }
76
 
                
77
 
 
78
 
                if ((kmem_entry->mode&KMEM_MODE_COUNT)==KMEM_MODE_COUNT) {
79
 
                        mod_info("Reuse counter of kmem_entry is overflown");
80
 
                        return -EBUSY;
81
 
                }
82
 
                
83
 
                
84
 
                kmem_handle->handle_id = kmem_entry->id;
85
 
                kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
86
 
 
87
 
                kmem_handle->flags = KMEM_FLAG_REUSED;
88
 
                if (kmem_entry->refs&KMEM_REF_HW) kmem_handle->flags |= KMEM_FLAG_REUSED_HW;
89
 
                if (kmem_entry->mode&KMEM_MODE_PERSISTENT) kmem_handle->flags |= KMEM_FLAG_REUSED_PERSISTENT;
90
 
 
91
 
                kmem_entry->mode += 1;
92
 
                if (flags&KMEM_FLAG_HW) {
93
 
                    if ((kmem_entry->refs&KMEM_REF_HW)==0)
94
 
                        pcidriver_module_get(privdata);
95
 
                        
96
 
                    kmem_entry->refs |= KMEM_REF_HW;
97
 
                }
98
 
                if (flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT;
99
 
 
100
 
                privdata->kmem_cur_id = kmem_entry->id;
101
 
                
102
 
                return 0;
103
 
            }
104
 
            
105
 
            if (kmem_handle->flags&KMEM_FLAG_TRY) return -ENOENT;
106
 
        }
107
 
 
108
 
        /* First, allocate zeroed memory for the kmem_entry */
109
 
        if ((kmem_entry = kcalloc(1, sizeof(pcidriver_kmem_entry_t), GFP_KERNEL)) == NULL)
110
 
                goto kmem_alloc_entry_fail;
111
 
 
112
 
        /* Initialize the kmem_entry */
113
 
        kmem_entry->id = atomic_inc_return(&privdata->kmem_count) - 1;
114
 
        privdata->kmem_cur_id = kmem_entry->id;
115
 
        kmem_handle->handle_id = kmem_entry->id;
116
 
 
117
 
        kmem_entry->use = kmem_handle->use;
118
 
        kmem_entry->item = kmem_handle->item;
119
 
        kmem_entry->type = kmem_handle->type;
120
 
        kmem_entry->align = kmem_handle->align;
121
 
        kmem_entry->direction = PCI_DMA_NONE;
122
 
 
123
 
        /* Initialize sysfs if possible */
124
 
        if (pcidriver_sysfs_initialize_kmem(privdata, kmem_entry->id, &(kmem_entry->sysfs_attr)) != 0)
125
 
                goto kmem_alloc_mem_fail;
126
 
 
127
 
        /* ...and allocate the DMA memory */
128
 
        /* note this is a memory pair, referencing the same area: the cpu address (cpua)
129
 
         * and the PCI bus address (pa). The CPU and PCI addresses may not be the same.
130
 
         * The CPU sees only CPU addresses, while the device sees only PCI addresses.
131
 
         * CPU address is used for the mmap (internal to the driver), and
132
 
         * PCI address is the address passed to the DMA Controller in the device.
133
 
         */
134
 
        switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) {
135
 
         case PCILIB_KMEM_TYPE_CONSISTENT:
136
 
            retptr = pci_alloc_consistent( privdata->pdev, kmem_handle->size, &(kmem_entry->dma_handle) );
137
 
            break;
138
 
         case PCILIB_KMEM_TYPE_REGION:
139
 
            retptr = ioremap(kmem_handle->pa,  kmem_handle->size);
140
 
            kmem_entry->dma_handle = kmem_handle->pa;
141
 
            if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_S2C) {
142
 
                kmem_entry->direction = PCI_DMA_TODEVICE;
143
 
            } else if (kmem_entry->type == PCILIB_KMEM_TYPE_REGION_C2S) {
144
 
                kmem_entry->direction = PCI_DMA_FROMDEVICE;
145
 
            }
146
 
            break;
147
 
         case PCILIB_KMEM_TYPE_PAGE:
148
 
            flags = GFP_KERNEL;
149
 
 
150
 
            if (kmem_handle->size == 0)
151
 
                kmem_handle->size = PAGE_SIZE;
152
 
            else if (kmem_handle->size%PAGE_SIZE)
153
 
                goto kmem_alloc_mem_fail;
154
 
        
155
 
            retptr = (void*)__get_free_pages(flags, get_order(kmem_handle->size));
156
 
            kmem_entry->dma_handle = 0;
157
 
            
158
 
            if (retptr) {
159
 
                if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) {
160
 
                    kmem_entry->direction = PCI_DMA_TODEVICE;
161
 
                    kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_TODEVICE);
162
 
                    if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) {
163
 
                        free_pages((unsigned long)retptr, get_order(kmem_handle->size));
164
 
                        goto kmem_alloc_mem_fail;
165
 
                    }
166
 
                } else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) {
167
 
                    kmem_entry->direction = PCI_DMA_FROMDEVICE;
168
 
                    kmem_entry->dma_handle = pci_map_single(privdata->pdev, retptr, kmem_handle->size, PCI_DMA_FROMDEVICE);
169
 
                    if (pci_dma_mapping_error(privdata->pdev, kmem_entry->dma_handle)) {
170
 
                        free_pages((unsigned long)retptr, get_order(kmem_handle->size));
171
 
                        goto kmem_alloc_mem_fail;
172
 
                    
173
 
                    }
174
 
                }
175
 
            }
176
 
            
177
 
            break;
178
 
         default:
179
 
            goto kmem_alloc_mem_fail;
180
 
        }
181
 
        
182
 
        
183
 
        if (retptr == NULL)
184
 
                goto kmem_alloc_mem_fail;
185
 
 
186
 
        kmem_entry->size = kmem_handle->size;
187
 
        kmem_entry->cpua = (unsigned long)retptr;
188
 
        kmem_handle->pa = (unsigned long)(kmem_entry->dma_handle);
189
 
 
190
 
        kmem_entry->mode = 1;
191
 
        if (kmem_handle->flags&KMEM_FLAG_REUSE) {
192
 
            kmem_entry->mode |= KMEM_MODE_REUSABLE;
193
 
            if (kmem_handle->flags&KMEM_FLAG_EXCLUSIVE) kmem_entry->mode |= KMEM_MODE_EXCLUSIVE;
194
 
            if (kmem_handle->flags&KMEM_FLAG_PERSISTENT) kmem_entry->mode |= KMEM_MODE_PERSISTENT;
195
 
        }
196
 
        
197
 
        kmem_entry->refs = 0;
198
 
        if (kmem_handle->flags&KMEM_FLAG_HW) {
199
 
            pcidriver_module_get(privdata);
200
 
 
201
 
            kmem_entry->refs |= KMEM_REF_HW;
202
 
        }
203
 
 
204
 
        kmem_handle->flags = 0;
205
 
        
206
 
        set_pages_reserved_compat(kmem_entry->cpua, kmem_entry->size);
207
 
 
208
 
        /* Add the kmem_entry to the list of the device */
209
 
        spin_lock( &(privdata->kmemlist_lock) );
210
 
        list_add_tail( &(kmem_entry->list), &(privdata->kmem_list) );
211
 
        spin_unlock( &(privdata->kmemlist_lock) );
212
 
 
213
 
        return 0;
214
 
 
215
 
kmem_alloc_mem_fail:
216
 
                kfree(kmem_entry);
217
 
kmem_alloc_entry_fail:
218
 
                return -ENOMEM;
219
 
}
220
 
 
221
 
static int pcidriver_kmem_free_check(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle, pcidriver_kmem_entry_t *kmem_entry) {
222
 
        if ((kmem_handle->flags & KMEM_FLAG_FORCE) == 0) {
223
 
            if (kmem_entry->mode&KMEM_MODE_COUNT)
224
 
                kmem_entry->mode -= 1;
225
 
 
226
 
            if (kmem_handle->flags&KMEM_FLAG_HW) {
227
 
                if (kmem_entry->refs&KMEM_REF_HW) 
228
 
                    pcidriver_module_put(privdata);
229
 
 
230
 
                kmem_entry->refs &= ~KMEM_REF_HW;
231
 
            }
232
 
        
233
 
            if (kmem_handle->flags&KMEM_FLAG_PERSISTENT)
234
 
                kmem_entry->mode &= ~KMEM_MODE_PERSISTENT;
235
 
        
236
 
            if (kmem_handle->flags&KMEM_FLAG_REUSE) 
237
 
                return 0;
238
 
 
239
 
            if (kmem_entry->refs) {
240
 
                kmem_entry->mode += 1;
241
 
                mod_info("can't free referenced kmem_entry\n");
242
 
                return -EBUSY;
243
 
            }
244
 
        
245
 
            if (kmem_entry->mode & KMEM_MODE_PERSISTENT) {
246
 
                kmem_entry->mode += 1;
247
 
                mod_info("can't free persistent kmem_entry\n");
248
 
                return -EBUSY;
249
 
            }
250
 
 
251
 
            if (((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)==0)&&(kmem_entry->mode&KMEM_MODE_COUNT)&&((kmem_handle->flags&KMEM_FLAG_EXCLUSIVE)==0)) 
252
 
                return 0;
253
 
        } else {
254
 
            if (kmem_entry->refs&KMEM_REF_HW)
255
 
                    pcidriver_module_put(privdata);
256
 
                
257
 
            while (!atomic_add_negative(-1, &(privdata->refs))) pcidriver_module_put(privdata);
258
 
            atomic_inc(&(privdata->refs));
259
 
                
260
 
        }
261
 
        
262
 
        return 1;
263
 
}
264
 
 
265
 
static int pcidriver_kmem_free_use(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
266
 
{
267
 
        int err;
268
 
        int failed = 0;
269
 
        struct list_head *ptr, *next;
270
 
        pcidriver_kmem_entry_t *kmem_entry;
271
 
 
272
 
        /* iterate safely over the entries and delete them */
273
 
        list_for_each_safe(ptr, next, &(privdata->kmem_list)) {
274
 
                kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
275
 
                if (kmem_entry->use == kmem_handle->use) {
276
 
                    err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry);
277
 
                    if (err > 0)
278
 
                        pcidriver_kmem_free_entry(privdata, kmem_entry);                /* spin lock inside! */
279
 
                    else
280
 
                        failed = 1;
281
 
                }
282
 
        }
283
 
        
284
 
        if (failed) {
285
 
                mod_info("Some kmem_entries for use %lx are still referenced\n", kmem_handle->use);
286
 
                return -EBUSY;
287
 
        }       
288
 
 
289
 
        return 0;
290
 
}
291
 
 
292
 
/**
293
 
 *
294
 
 * Called via sysfs, frees kernel memory and the corresponding management structure
295
 
 *
296
 
 */
297
 
int pcidriver_kmem_free( pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle )
298
 
{
299
 
        int err;
300
 
        pcidriver_kmem_entry_t *kmem_entry;
301
 
 
302
 
        if (kmem_handle->flags&KMEM_FLAG_MASS) {
303
 
            kmem_handle->flags &= ~KMEM_FLAG_MASS;
304
 
            return pcidriver_kmem_free_use(privdata, kmem_handle);
305
 
        }
306
 
        
307
 
        /* Find the associated kmem_entry for this buffer */
308
 
        if ((kmem_entry = pcidriver_kmem_find_entry(privdata, kmem_handle)) == NULL)
309
 
                return -EINVAL;                                 /* kmem_handle is not valid */
310
 
 
311
 
        err = pcidriver_kmem_free_check(privdata, kmem_handle, kmem_entry);
312
 
        
313
 
        if (err > 0)
314
 
                return pcidriver_kmem_free_entry(privdata, kmem_entry);
315
 
        
316
 
        return err;
317
 
}
318
 
 
319
 
/**
320
 
 *
321
 
 * Called when cleaning up, frees all kernel memory and their corresponding management structure
322
 
 *
323
 
 */
324
 
int pcidriver_kmem_free_all(pcidriver_privdata_t *privdata)
325
 
{
326
 
//      int failed = 0;
327
 
        struct list_head *ptr, *next;
328
 
        pcidriver_kmem_entry_t *kmem_entry;
329
 
 
330
 
        /* iterate safely over the entries and delete them */
331
 
        list_for_each_safe(ptr, next, &(privdata->kmem_list)) {
332
 
                kmem_entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
333
 
                /*if (kmem_entry->refs)
334
 
                        failed = 1;
335
 
                else*/
336
 
                        pcidriver_kmem_free_entry(privdata, kmem_entry);                /* spin lock inside! */
337
 
        }
338
 
/*      
339
 
        if (failed) {
340
 
                mod_info("Some kmem_entries are still referenced\n");
341
 
                return -EBUSY;
342
 
        }       
343
 
*/
344
 
        return 0;
345
 
}
346
 
 
347
 
 
348
 
/**
349
 
 *
350
 
 * Synchronize memory to/from the device (or in both directions).
351
 
 *
352
 
 */
353
 
int pcidriver_kmem_sync_entry( pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry, int direction)
354
 
{
355
 
        if (kmem_entry->direction == PCI_DMA_NONE)
356
 
                return -EINVAL;
357
 
 
358
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11)
359
 
        switch (direction) {
360
 
                case PCILIB_KMEM_SYNC_TODEVICE:
361
 
                        pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
362
 
                        break;
363
 
                case PCILIB_KMEM_SYNC_FROMDEVICE:
364
 
                        pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
365
 
                        break;
366
 
                case PCILIB_KMEM_SYNC_BIDIRECTIONAL:
367
 
                        pci_dma_sync_single_for_device( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
368
 
                        pci_dma_sync_single_for_cpu( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
369
 
                        break;
370
 
                default:
371
 
                        return -EINVAL;                         /* wrong direction parameter */
372
 
        }
373
 
#else
374
 
        switch (direction) {
375
 
                case PCILIB_KMEM_SYNC_TODEVICE:
376
 
                        pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
377
 
                        break;
378
 
                case PCILIB_KMEM_SYNC_FROMDEVICE:
379
 
                        pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
380
 
                        break;
381
 
                case PCILIB_KMEM_SYNC_BIDIRECTIONAL:
382
 
                        pci_dma_sync_single( privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, kmem_entry->direction );
383
 
                        break;
384
 
                default:
385
 
                        return -EINVAL;                         /* wrong direction parameter */
386
 
        }
387
 
#endif
388
 
 
389
 
        return 0;       /* success */
390
 
}
391
 
 
392
 
/**
393
 
 *
394
 
 * Synchronize memory to/from the device (or in both directions).
395
 
 *
396
 
 */
397
 
int pcidriver_kmem_sync( pcidriver_privdata_t *privdata, kmem_sync_t *kmem_sync )
398
 
{
399
 
        pcidriver_kmem_entry_t *kmem_entry;
400
 
 
401
 
        /* Find the associated kmem_entry for this buffer */
402
 
        if ((kmem_entry = pcidriver_kmem_find_entry(privdata, &(kmem_sync->handle))) == NULL)
403
 
                return -EINVAL;                                 /* kmem_handle is not valid */
404
 
 
405
 
        return pcidriver_kmem_sync_entry(privdata, kmem_entry, kmem_sync->dir);
406
 
}
407
 
 
408
 
/**
409
 
 *
410
 
 * Free the given kmem_entry and its memory.
411
 
 *
412
 
 */
413
 
int pcidriver_kmem_free_entry(pcidriver_privdata_t *privdata, pcidriver_kmem_entry_t *kmem_entry)
414
 
{
415
 
        pcidriver_sysfs_remove(privdata, &(kmem_entry->sysfs_attr));
416
 
 
417
 
        /* Go over the pages of the kmem buffer, and mark them as not reserved */
418
 
#if 0
419
 
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
420
 
        /*
421
 
         * This code is DISABLED.
422
 
         * Apparently, it is not needed to unreserve them. Doing so here
423
 
         * hangs the machine. Why?
424
 
         *
425
 
         * Uhm.. see links:
426
 
         *
427
 
         * http://lwn.net/Articles/161204/
428
 
         * http://lists.openfabrics.org/pipermail/general/2007-March/034101.html
429
 
         *
430
 
         * I insist, this should be enabled, but doing so hangs the machine.
431
 
         * Literature supports the point, and there is even a similar problem (see link)
432
 
         * But this is not the case. It seems right to me. but obviously is not.
433
 
         *
434
 
         * Anyway, this goes away in kernel >=2.6.15.
435
 
         */
436
 
        unsigned long start = __pa(kmem_entry->cpua) >> PAGE_SHIFT;
437
 
        unsigned long end = __pa(kmem_entry->cpua + kmem_entry->size) >> PAGE_SHIFT;
438
 
        unsigned long i;
439
 
        for(i=start;i<end;i++) {
440
 
                struct page *kpage = pfn_to_page(i);
441
 
                ClearPageReserved(kpage);
442
 
        }
443
 
#endif
444
 
#endif
445
 
 
446
 
        /* Release DMA memory */
447
 
        switch (kmem_entry->type&PCILIB_KMEM_TYPE_MASK) {
448
 
         case PCILIB_KMEM_TYPE_CONSISTENT:
449
 
            pci_free_consistent( privdata->pdev, kmem_entry->size, (void *)(kmem_entry->cpua), kmem_entry->dma_handle );
450
 
            break;
451
 
         case PCILIB_KMEM_TYPE_REGION:
452
 
            iounmap((void *)(kmem_entry->cpua));
453
 
            break;
454
 
         case PCILIB_KMEM_TYPE_PAGE:
455
 
            if (kmem_entry->dma_handle) {
456
 
                if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_S2C_PAGE) {
457
 
                    pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_TODEVICE);
458
 
                } else if (kmem_entry->type == PCILIB_KMEM_TYPE_DMA_C2S_PAGE) {
459
 
                    pci_unmap_single(privdata->pdev, kmem_entry->dma_handle, kmem_entry->size, PCI_DMA_FROMDEVICE);
460
 
                }
461
 
            }
462
 
            free_pages((unsigned long)kmem_entry->cpua, get_order(kmem_entry->size));
463
 
            break;
464
 
        }
465
 
 
466
 
 
467
 
        /* Remove the kmem list entry */
468
 
        spin_lock( &(privdata->kmemlist_lock) );
469
 
        list_del( &(kmem_entry->list) );
470
 
        spin_unlock( &(privdata->kmemlist_lock) );
471
 
 
472
 
        /* Release kmem_entry memory */
473
 
        kfree(kmem_entry);
474
 
 
475
 
        return 0;
476
 
}
477
 
 
478
 
/**
479
 
 *
480
 
 * Find the corresponding kmem_entry for the given kmem_handle.
481
 
 *
482
 
 */
483
 
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry(pcidriver_privdata_t *privdata, kmem_handle_t *kmem_handle)
484
 
{
485
 
        struct list_head *ptr;
486
 
        pcidriver_kmem_entry_t *entry, *result = NULL;
487
 
 
488
 
        /* should I implement it better using the handle_id? */
489
 
 
490
 
        spin_lock(&(privdata->kmemlist_lock));
491
 
        list_for_each(ptr, &(privdata->kmem_list)) {
492
 
                entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
493
 
 
494
 
                if (entry->id == kmem_handle->handle_id) {
495
 
                        result = entry;
496
 
                        break;
497
 
                }
498
 
        }
499
 
 
500
 
        spin_unlock(&(privdata->kmemlist_lock));
501
 
        return result;
502
 
}
503
 
 
504
 
/**
505
 
 *
506
 
 * find the corresponding kmem_entry for the given id.
507
 
 *
508
 
 */
509
 
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_id(pcidriver_privdata_t *privdata, int id)
510
 
{
511
 
        struct list_head *ptr;
512
 
        pcidriver_kmem_entry_t *entry, *result = NULL;
513
 
 
514
 
        spin_lock(&(privdata->kmemlist_lock));
515
 
        list_for_each(ptr, &(privdata->kmem_list)) {
516
 
                entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
517
 
 
518
 
                if (entry->id == id) {
519
 
                        result = entry;
520
 
                        break;
521
 
                }
522
 
        }
523
 
 
524
 
        spin_unlock(&(privdata->kmemlist_lock));
525
 
        return result;
526
 
}
527
 
 
528
 
/**
529
 
 *
530
 
 * find the corresponding kmem_entry for the given use and item.
531
 
 *
532
 
 */
533
 
pcidriver_kmem_entry_t *pcidriver_kmem_find_entry_use(pcidriver_privdata_t *privdata, unsigned long use, unsigned long item)
534
 
{
535
 
        struct list_head *ptr;
536
 
        pcidriver_kmem_entry_t *entry, *result = NULL;
537
 
 
538
 
        spin_lock(&(privdata->kmemlist_lock));
539
 
        list_for_each(ptr, &(privdata->kmem_list)) {
540
 
                entry = list_entry(ptr, pcidriver_kmem_entry_t, list);
541
 
 
542
 
                if ((entry->use == use)&&(entry->item == item)&&(entry->mode&KMEM_MODE_REUSABLE)) {
543
 
                        result = entry;
544
 
                        break;
545
 
                }
546
 
        }
547
 
 
548
 
        spin_unlock(&(privdata->kmemlist_lock));
549
 
        return result;
550
 
}
551
 
 
552
 
 
553
 
void pcidriver_kmem_mmap_close(struct vm_area_struct *vma) {
554
 
    unsigned long vma_size;
555
 
    pcidriver_kmem_entry_t *kmem_entry = (pcidriver_kmem_entry_t*)vma->vm_private_data;
556
 
    if (kmem_entry) {
557
 
/*
558
 
        if (kmem_entry->id == 0) {
559
 
            mod_info("refs: %p %p %lx\n", vma, vma->vm_private_data, kmem_entry->refs);
560
 
            mod_info("kmem_size: %lu vma_size: %lu, s: %lx, e: %lx\n", kmem_entry->size, (vma->vm_end - vma->vm_start), vma->vm_start, vma->vm_end);
561
 
        }
562
 
*/
563
 
 
564
 
        vma_size = (vma->vm_end - vma->vm_start);
565
 
        
566
 
        if (kmem_entry->refs&KMEM_REF_COUNT) {
567
 
            kmem_entry->refs -= vma_size / PAGE_SIZE;
568
 
        }
569
 
    }
570
 
}
571
 
 
572
 
static struct vm_operations_struct pcidriver_kmem_mmap_ops = {
573
 
    .close = pcidriver_kmem_mmap_close
574
 
};
575
 
 
576
 
/**
577
 
 *
578
 
 * mmap() kernel memory to userspace.
579
 
 *
580
 
 */
581
 
int pcidriver_mmap_kmem(pcidriver_privdata_t *privdata, struct vm_area_struct *vma)
582
 
{
583
 
        unsigned long vma_size;
584
 
        pcidriver_kmem_entry_t *kmem_entry;
585
 
        int ret;
586
 
 
587
 
        mod_info_dbg("Entering mmap_kmem\n");
588
 
 
589
 
        /* FIXME: Is this really right? Always just the latest one? Can't we identify one? */
590
 
        /* Get latest entry on the kmem_list */
591
 
        kmem_entry = pcidriver_kmem_find_entry_id(privdata, privdata->kmem_cur_id);
592
 
        if (!kmem_entry) {
593
 
                mod_info("Trying to mmap a kernel memory buffer without creating it first!\n");
594
 
                return -EFAULT;
595
 
        }
596
 
 
597
 
        mod_info_dbg("Got kmem_entry with id: %d\n", kmem_entry->id);
598
 
 
599
 
        /* Check sizes */
600
 
        vma_size = (vma->vm_end - vma->vm_start);
601
 
        
602
 
        if ((vma_size > kmem_entry->size) &&
603
 
                ((kmem_entry->size < PAGE_SIZE) && (vma_size != PAGE_SIZE))) {
604
 
                mod_info("kem_entry size(%lu) and vma size do not match(%lu)\n", kmem_entry->size, vma_size);
605
 
                return -EINVAL;
606
 
        }
607
 
 
608
 
        /* reference counting */
609
 
        if ((kmem_entry->mode&KMEM_MODE_EXCLUSIVE)&&(kmem_entry->refs&KMEM_REF_COUNT)) {
610
 
                mod_info("can't make second mmaping for exclusive kmem_entry\n");
611
 
                return -EBUSY;
612
 
        }
613
 
        if (((kmem_entry->refs&KMEM_REF_COUNT) + (vma_size / PAGE_SIZE)) > KMEM_REF_COUNT) {
614
 
                mod_info("maximal amount of references is reached by kmem_entry\n");
615
 
                return -EBUSY;
616
 
        }
617
 
        
618
 
        kmem_entry->refs += vma_size / PAGE_SIZE;
619
 
 
620
 
        vma->vm_flags |= (VM_RESERVED);
621
 
 
622
 
#ifdef pgprot_noncached
623
 
        // This is coherent memory, so it must not be cached.
624
 
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
625
 
#endif
626
 
 
627
 
        mod_info_dbg("Mapping address %08lx / PFN %08lx\n",
628
 
                        virt_to_phys((void*)kmem_entry->cpua),
629
 
                        page_to_pfn(virt_to_page((void*)kmem_entry->cpua)));
630
 
 
631
 
         if ((kmem_entry->type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) {
632
 
                ret = remap_pfn_range_compat(
633
 
                                        vma,
634
 
                                        vma->vm_start,
635
 
                                        kmem_entry->dma_handle,
636
 
                                        (vma_size < kmem_entry->size)?vma_size:kmem_entry->size,
637
 
                                        vma->vm_page_prot);
638
 
         } else {
639
 
                ret = remap_pfn_range_cpua_compat(
640
 
                                        vma,
641
 
                                        vma->vm_start,
642
 
                                        kmem_entry->cpua,
643
 
                                        (vma_size < kmem_entry->size)?vma_size:kmem_entry->size,
644
 
                                        vma->vm_page_prot );
645
 
        }
646
 
 
647
 
        if (ret) {
648
 
                mod_info("kmem remap failed: %d (%lx)\n", ret,kmem_entry->cpua);
649
 
                kmem_entry->refs -= 1;
650
 
                return -EAGAIN;
651
 
        }
652
 
 
653
 
        vma->vm_ops = &pcidriver_kmem_mmap_ops;
654
 
        vma->vm_private_data = (void*)kmem_entry;
655
 
        
656
 
        return ret;
657
 
}