/alps/pcitool

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

« back to all changes in this revision

Viewing changes to driver/base.c

  • Committer: Suren A. Chilingaryan
  • Date: 2011-02-13 02:07:11 UTC
  • Revision ID: csa@dside.dyndns.org-20110213020711-y9bjh3n4ke6p4t4n
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 *
 
3
 * @file base.c
 
4
 * @author Guillermo Marcus
 
5
 * @date 2009-04-05
 
6
 * @brief Contains the main code which connects all the different parts and does
 
7
 * basic driver tasks like initialization.
 
8
 *
 
9
 * This is a full rewrite of the pciDriver.
 
10
 * New default is to support kernel 2.6, using kernel 2.6 APIs.
 
11
 *
 
12
 */
 
13
 
 
14
/*
 
15
 * Change History:
 
16
 *
 
17
 * $Log: not supported by cvs2svn $
 
18
 * Revision 1.13  2008-05-30 11:38:15  marcus
 
19
 * Added patches for kernel 2.6.24
 
20
 *
 
21
 * Revision 1.12  2008-01-24 14:21:36  marcus
 
22
 * Added a CLEAR_INTERRUPT_QUEUE ioctl.
 
23
 * Added a sysfs attribute to show the outstanding IRQ queues.
 
24
 *
 
25
 * Revision 1.11  2008-01-24 12:53:11  marcus
 
26
 * Corrected wait_event condition in waiti_ioctl. Improved the loop too.
 
27
 *
 
28
 * Revision 1.10  2008-01-14 10:39:39  marcus
 
29
 * Set some messages as debug instead of normal.
 
30
 *
 
31
 * Revision 1.9  2008-01-11 10:18:28  marcus
 
32
 * Modified interrupt mechanism. Added atomic functions and queues, to address race conditions. Removed unused interrupt code.
 
33
 *
 
34
 * Revision 1.8  2007-07-17 13:15:55  marcus
 
35
 * Removed Tasklets.
 
36
 * Using newest map for the ABB interrupts.
 
37
 *
 
38
 * Revision 1.7  2007-07-06 15:56:04  marcus
 
39
 * Change default status for OLD_REGISTERS to not defined.
 
40
 *
 
41
 * Revision 1.6  2007-07-05 15:29:59  marcus
 
42
 * Corrected issue with the bar mapping for interrupt handling.
 
43
 * Added support up to kernel 2.6.20
 
44
 *
 
45
 * Revision 1.5  2007-05-29 07:50:18  marcus
 
46
 * Split code into 2 files. May get merged in the future again....
 
47
 *
 
48
 * Revision 1.4  2007/03/01 17:47:34  marcus
 
49
 * Fixed bug when the kernel memory was less than one page, it was not locked properly, recalling an old mapping issue in this case.
 
50
 *
 
51
 * Revision 1.3  2007/03/01 17:01:22  marcus
 
52
 * comment fix (again).
 
53
 *
 
54
 * Revision 1.2  2007/03/01 17:00:25  marcus
 
55
 * Changed some comment in the log.
 
56
 *
 
57
 * Revision 1.1  2007/03/01 16:57:43  marcus
 
58
 * Divided driver file to ease the interrupt hooks for the user of the driver.
 
59
 * Modified Makefile accordingly.
 
60
 *
 
61
 * From pciDriver.c:
 
62
 * Revision 1.11  2006/12/11 16:15:43  marcus
 
63
 * Fixed kernel buffer mmapping, and driver crash when application crashes.
 
64
 * Buffer memory is now marked reserved during allocation, and mmaped with
 
65
 * remap_xx_range.
 
66
 *
 
67
 * Revision 1.10  2006/11/21 09:50:49  marcus
 
68
 * Added PROGRAPE4 vendor/device IDs.
 
69
 *
 
70
 * Revision 1.9  2006/11/17 18:47:36  marcus
 
71
 * Removed MERGE_SGENTRIES flag, now it is selected at runtime with 'type'.
 
72
 * Removed noncached in non-prefetchable areas, to allow the use of MTRRs.
 
73
 *
 
74
 * Revision 1.8  2006/11/17 16:41:21  marcus
 
75
 * Added slot number to the PCI info IOctl.
 
76
 *
 
77
 * Revision 1.7  2006/11/13 12:30:34  marcus
 
78
 * Added a IOctl call, to confiure the interrupt response. (testing pending).
 
79
 * Basic interrupts are now supported, using a Tasklet and Completions.
 
80
 *
 
81
 * Revision 1.6  2006/11/08 21:30:02  marcus
 
82
 * Added changes after compile tests in kernel 2.6.16
 
83
 *
 
84
 * Revision 1.5  2006/10/31 07:57:38  marcus
 
85
 * Improved the pfn calculation in nopage(), to deal with some possible border
 
86
 * conditions. It was really no issue, because they are normally page-aligned
 
87
 * anyway, but to be on the safe side.
 
88
 *
 
89
 * Revision 1.4  2006/10/30 19:37:40  marcus
 
90
 * Solved bug on kernel memory not mapping properly.
 
91
 *
 
92
 * Revision 1.3  2006/10/18 11:19:20  marcus
 
93
 * Added kernel 2.6.8 support based on comments from Joern Adamczewski (GSI).
 
94
 *
 
95
 * Revision 1.2  2006/10/18 11:04:15  marcus
 
96
 * Bus Master is only activated when we detect a specific board.
 
97
 *
 
98
 * Revision 1.1  2006/10/10 14:46:51  marcus
 
99
 * Initial commit of the new pciDriver for kernel 2.6
 
100
 *
 
101
 * Revision 1.9  2006/10/05 11:30:46  marcus
 
102
 * Prerelease. Added bus and devfn to pciInfo for compatibility.
 
103
 *
 
104
 * Revision 1.8  2006/09/25 16:51:07  marcus
 
105
 * Added PCI config IOctls, and implemented basic mmap functions.
 
106
 *
 
107
 * Revision 1.7  2006/09/20 11:12:41  marcus
 
108
 * Added Merge SG entries
 
109
 *
 
110
 * Revision 1.6  2006/09/19 17:22:18  marcus
 
111
 * backup commit.
 
112
 *
 
113
 * Revision 1.5  2006/09/18 17:13:11  marcus
 
114
 * backup commit.
 
115
 *
 
116
 * Revision 1.4  2006/09/15 15:44:41  marcus
 
117
 * backup commit.
 
118
 *
 
119
 * Revision 1.3  2006/08/15 11:40:02  marcus
 
120
 * backup commit.
 
121
 *
 
122
 * Revision 1.2  2006/08/12 18:28:42  marcus
 
123
 * Sync with the laptop
 
124
 *
 
125
 * Revision 1.1  2006/08/11 15:30:46  marcus
 
126
 * Sync with the laptop
 
127
 *
 
128
 */
 
129
 
 
130
#include <linux/version.h>
 
131
 
 
132
/* Check macros and kernel version first */
 
133
#ifndef KERNEL_VERSION
 
134
#error "No KERNEL_VERSION macro! Stopping."
 
135
#endif
 
136
 
 
137
#ifndef LINUX_VERSION_CODE
 
138
#error "No LINUX_VERSION_CODE macro! Stopping."
 
139
#endif
 
140
 
 
141
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8)
 
142
#error "This driver has been tested only for Kernel 2.6.8 or above."
 
143
#endif
 
144
 
 
145
/* Required includes */
 
146
#include <linux/string.h>
 
147
#include <linux/slab.h>
 
148
#include <linux/types.h>
 
149
#include <linux/init.h>
 
150
#include <linux/module.h>
 
151
#include <linux/pci.h>
 
152
#include <linux/kernel.h>
 
153
#include <linux/errno.h>
 
154
#include <linux/fs.h>
 
155
#include <linux/cdev.h>
 
156
#include <linux/sysfs.h>
 
157
#include <asm/atomic.h>
 
158
#include <linux/pagemap.h>
 
159
#include <linux/spinlock.h>
 
160
#include <linux/list.h>
 
161
#include <asm/scatterlist.h>
 
162
#include <linux/vmalloc.h>
 
163
#include <linux/stat.h>
 
164
#include <linux/interrupt.h>
 
165
#include <linux/wait.h>
 
166
 
 
167
/* Configuration for the driver (what should be compiled in, module name, etc...) */
 
168
#include "config.h"
 
169
 
 
170
/* Compatibility functions/definitions (provides functions which are not available on older kernels) */
 
171
#include "compat.h"
 
172
 
 
173
/* External interface for the driver */
 
174
#include "pciDriver.h"
 
175
 
 
176
/* Internal definitions for all parts (prototypes, data, macros) */
 
177
#include "common.h"
 
178
 
 
179
/* Internal definitions for the base part */
 
180
#include "base.h"
 
181
 
 
182
/* Internal definitions of the IRQ handling part */
 
183
#include "int.h"
 
184
 
 
185
/* Internal definitions for kernel memory */
 
186
#include "kmem.h"
 
187
 
 
188
/* Internal definitions for user space memory */
 
189
#include "umem.h"
 
190
 
 
191
#include "ioctl.h"
 
192
 
 
193
/*************************************************************************/
 
194
/* Module device table associated with this driver */
 
195
MODULE_DEVICE_TABLE(pci, pcidriver_ids);
 
196
 
 
197
/* Module init and exit points */
 
198
module_init(pcidriver_init);
 
199
module_exit(pcidriver_exit);
 
200
 
 
201
/* Module info */
 
202
MODULE_AUTHOR("Guillermo Marcus");
 
203
MODULE_DESCRIPTION("Simple PCI Driver");
 
204
MODULE_LICENSE("GPL v2");
 
205
 
 
206
/* Module class */
 
207
static struct class_compat *pcidriver_class;
 
208
 
 
209
/**
 
210
 *
 
211
 * Called when loading the driver
 
212
 *
 
213
 */
 
214
static int __init pcidriver_init(void)
 
215
{
 
216
        int err;
 
217
 
 
218
        /* Initialize the device count */
 
219
        atomic_set(&pcidriver_deviceCount, 0);
 
220
 
 
221
        /* Allocate character device region dynamically */
 
222
        if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) {
 
223
                mod_info("Couldn't allocate chrdev region. Module not loaded.\n");
 
224
                goto init_alloc_fail;
 
225
        }
 
226
        mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME);
 
227
 
 
228
        /* Register driver class */
 
229
        pcidriver_class = class_create(THIS_MODULE, NODENAME);
 
230
 
 
231
        if (IS_ERR(pcidriver_class)) {
 
232
                mod_info("No sysfs support. Module not loaded.\n");
 
233
                goto init_class_fail;
 
234
        }
 
235
 
 
236
        /* Register PCI driver. This function returns the number of devices on some
 
237
         * systems, therefore check for errors as < 0. */
 
238
        if ((err = pci_register_driver(&pcidriver_driver)) < 0) {
 
239
                mod_info("Couldn't register PCI driver. Module not loaded.\n");
 
240
                goto init_pcireg_fail;
 
241
        }
 
242
 
 
243
        mod_info("Module loaded\n");
 
244
 
 
245
        return 0;
 
246
 
 
247
init_pcireg_fail:
 
248
        class_destroy(pcidriver_class);
 
249
init_class_fail:
 
250
        unregister_chrdev_region(pcidriver_devt, MAXDEVICES);
 
251
init_alloc_fail:
 
252
        return err;
 
253
}
 
254
 
 
255
/**
 
256
 *
 
257
 * Called when unloading the driver
 
258
 *
 
259
 */
 
260
static void pcidriver_exit(void)
 
261
{
 
262
        if (pcidriver_class != NULL)
 
263
                class_destroy(pcidriver_class);
 
264
 
 
265
        pci_unregister_driver(&pcidriver_driver);
 
266
        unregister_chrdev_region(pcidriver_devt, MAXDEVICES);
 
267
        mod_info("Module unloaded\n");
 
268
}
 
269
 
 
270
/*************************************************************************/
 
271
/* Driver functions */
 
272
 
 
273
/**
 
274
 *
 
275
 * This struct defines the PCI entry points.
 
276
 * Will be registered at module init.
 
277
 *
 
278
 */
 
279
static struct pci_driver pcidriver_driver = {
 
280
        .name = MODNAME,
 
281
        .id_table = pcidriver_ids,
 
282
        .probe = pcidriver_probe,
 
283
        .remove = pcidriver_remove,
 
284
};
 
285
 
 
286
/**
 
287
 *
 
288
 * This function is called when installing the driver for a device
 
289
 * @param pdev Pointer to the PCI device
 
290
 *
 
291
 */
 
292
static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
293
{
 
294
        int err;
 
295
        int devno;
 
296
        pcidriver_privdata_t *privdata;
 
297
        int devid;
 
298
 
 
299
        /* At the moment there is no difference between these boards here, other than
 
300
         * printing a different message in the log.
 
301
         *
 
302
         * However, there is some difference in the interrupt handling functions.
 
303
         */
 
304
        if ( (id->vendor == MPRACE1_VENDOR_ID) &&
 
305
                (id->device == MPRACE1_DEVICE_ID))
 
306
        {
 
307
                /* It is a mpRACE-1 */
 
308
                mod_info( "Found mpRACE-1 at %s\n", dev_name(&pdev->dev));
 
309
                /* Set bus master */
 
310
                pci_set_master(pdev);
 
311
        }
 
312
        else if ((id->vendor == PCIXTEST_VENDOR_ID) &&
 
313
                (id->device == PCIXTEST_DEVICE_ID))
 
314
        {
 
315
                /* It is a PCI-X Test board */
 
316
                mod_info( "Found PCI-X test board at %s\n", dev_name(&pdev->dev));
 
317
        }
 
318
        else if ((id->vendor == PCIEPLDA_VENDOR_ID) &&
 
319
                (id->device == PCIEPLDA_DEVICE_ID))
 
320
        {
 
321
                /* It is a PCI-X Test board */
 
322
                mod_info( "Found PCIe PLDA test board at %s\n", dev_name(&pdev->dev));
 
323
        }
 
324
        else if ((id->vendor == PCIEABB_VENDOR_ID) &&
 
325
                (id->device == PCIEABB_DEVICE_ID))
 
326
        {
 
327
                /* It is a PCI-X Test board */
 
328
                mod_info( "Found PCIe ABB test board at %s\n", dev_name(&pdev->dev));
 
329
        }
 
330
        else if ((id->vendor == PCIXPG4_VENDOR_ID) &&
 
331
                (id->device == PCIXPG4_DEVICE_ID))
 
332
        {
 
333
                /* It is a PCI-X PROGRAPE4 board */
 
334
                mod_info( "Found PCI-X PROGRAPE-4 board at %s\n", dev_name(&pdev->dev));
 
335
        }
 
336
        else if ((id->vendor == PCI64PG4_VENDOR_ID) &&
 
337
                (id->device == PCI64PG4_DEVICE_ID))
 
338
        {
 
339
                /* It is a PCI-64 PROGRAPE4 board */
 
340
                mod_info( "Found PCI-64b/66 PROGRAPE-4 board at %s\n", dev_name(&pdev->dev));
 
341
        }
 
342
        else if ((id->vendor == PCIE_XILINX_VENDOR_ID) &&
 
343
                (id->device == PCIE_ML605_DEVICE_ID))
 
344
        {
 
345
                /* It is a PCI-E Xilinx ML605 evaluation board */
 
346
                mod_info("Found ML605 board at %s\n", dev_name(&pdev->dev));
 
347
        }
 
348
        else
 
349
        {
 
350
                /* It is something else */
 
351
                mod_info( "Found unknown board (%x:%x) at %s\n", id->vendor, id->device, dev_name(&pdev->dev));
 
352
        }
 
353
 
 
354
        /* Enable the device */
 
355
        if ((err = pci_enable_device(pdev)) != 0) {
 
356
                mod_info("Couldn't enable device\n");
 
357
                goto probe_pcien_fail;
 
358
        }
 
359
 
 
360
        /* Set Memory-Write-Invalidate support */
 
361
        if ((err = pci_set_mwi(pdev)) != 0)
 
362
                mod_info("MWI not supported. Continue without enabling MWI.\n");
 
363
 
 
364
        /* Get / Increment the device id */
 
365
        devid = atomic_inc_return(&pcidriver_deviceCount) - 1;
 
366
        if (devid >= MAXDEVICES) {
 
367
                mod_info("Maximum number of devices reached! Increase MAXDEVICES.\n");
 
368
                err = -ENOMSG;
 
369
                goto probe_maxdevices_fail;
 
370
        }
 
371
 
 
372
        /* Allocate and initialize the private data for this device */
 
373
        if ((privdata = kcalloc(1, sizeof(*privdata), GFP_KERNEL)) == NULL) {
 
374
                err = -ENOMEM;
 
375
                goto probe_nomem;
 
376
        }
 
377
 
 
378
        INIT_LIST_HEAD(&(privdata->kmem_list));
 
379
        spin_lock_init(&(privdata->kmemlist_lock));
 
380
        atomic_set(&privdata->kmem_count, 0);
 
381
 
 
382
        INIT_LIST_HEAD(&(privdata->umem_list));
 
383
        spin_lock_init(&(privdata->umemlist_lock));
 
384
        atomic_set(&privdata->umem_count, 0);
 
385
 
 
386
        pci_set_drvdata( pdev, privdata );
 
387
        privdata->pdev = pdev;
 
388
 
 
389
        /* Device add to sysfs */
 
390
        devno = MKDEV(MAJOR(pcidriver_devt), MINOR(pcidriver_devt) + devid);
 
391
        privdata->devno = devno;
 
392
        if (pcidriver_class != NULL) {
 
393
                /* FIXME: some error checking missing here */
 
394
                privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, &(pdev->dev), NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata);
 
395
                class_set_devdata( privdata->class_dev, privdata );
 
396
                mod_info("Device /dev/%s%d added\n",NODENAME,MINOR(pcidriver_devt) + devid);
 
397
        }
 
398
 
 
399
        /* Setup mmaped BARs into kernel space */
 
400
        if ((err = pcidriver_probe_irq(privdata)) != 0)
 
401
                goto probe_irq_probe_fail;
 
402
 
 
403
        /* Populate sysfs attributes for the class device */
 
404
        /* TODO: correct errorhandling. ewww. must remove the files in reversed order :-( */
 
405
        #define sysfs_attr(name) do { \
 
406
                        if (class_device_create_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)) != 0) \
 
407
                                goto probe_device_create_fail; \
 
408
                        } while (0)
 
409
        #ifdef ENABLE_IRQ
 
410
        sysfs_attr(irq_count);
 
411
        sysfs_attr(irq_queues);
 
412
        #endif
 
413
 
 
414
        sysfs_attr(mmap_mode);
 
415
        sysfs_attr(mmap_area);
 
416
        sysfs_attr(kmem_count);
 
417
        sysfs_attr(kmem_alloc);
 
418
        sysfs_attr(kmem_free);
 
419
        sysfs_attr(kbuffers);
 
420
        sysfs_attr(umappings);
 
421
        sysfs_attr(umem_unmap);
 
422
        #undef sysfs_attr
 
423
 
 
424
        /* Register character device */
 
425
        cdev_init( &(privdata->cdev), &pcidriver_fops );
 
426
        privdata->cdev.owner = THIS_MODULE;
 
427
        privdata->cdev.ops = &pcidriver_fops;
 
428
        err = cdev_add( &privdata->cdev, devno, 1 );
 
429
        if (err) {
 
430
                mod_info( "Couldn't add character device.\n" );
 
431
                goto probe_cdevadd_fail;
 
432
        }
 
433
 
 
434
        return 0;
 
435
 
 
436
probe_device_create_fail:
 
437
probe_cdevadd_fail:
 
438
probe_irq_probe_fail:
 
439
        pcidriver_irq_unmap_bars(privdata);
 
440
        kfree(privdata);
 
441
probe_nomem:
 
442
        atomic_dec(&pcidriver_deviceCount);
 
443
probe_maxdevices_fail:
 
444
        pci_disable_device(pdev);
 
445
probe_pcien_fail:
 
446
        return err;
 
447
}
 
448
 
 
449
/**
 
450
 *
 
451
 * This function is called when disconnecting a device
 
452
 *
 
453
 */
 
454
static void __devexit pcidriver_remove(struct pci_dev *pdev)
 
455
{
 
456
        pcidriver_privdata_t *privdata;
 
457
 
 
458
        /* Get private data from the device */
 
459
        privdata = pci_get_drvdata(pdev);
 
460
 
 
461
        /* Removing sysfs attributes from class device */
 
462
        #define sysfs_attr(name) do { \
 
463
                        class_device_remove_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)); \
 
464
                        } while (0)
 
465
        #ifdef ENABLE_IRQ
 
466
        sysfs_attr(irq_count);
 
467
        sysfs_attr(irq_queues);
 
468
        #endif
 
469
 
 
470
        sysfs_attr(mmap_mode);
 
471
        sysfs_attr(mmap_area);
 
472
        sysfs_attr(kmem_count);
 
473
        sysfs_attr(kmem_alloc);
 
474
        sysfs_attr(kmem_free);
 
475
        sysfs_attr(kbuffers);
 
476
        sysfs_attr(umappings);
 
477
        sysfs_attr(umem_unmap);
 
478
        #undef sysfs_attr
 
479
 
 
480
        /* Free all allocated kmem buffers before leaving */
 
481
        pcidriver_kmem_free_all( privdata );
 
482
 
 
483
#ifdef ENABLE_IRQ
 
484
        pcidriver_remove_irq(privdata);
 
485
#endif
 
486
 
 
487
        /* Removing Character device */
 
488
        cdev_del(&(privdata->cdev));
 
489
 
 
490
        /* Removing the device from sysfs */
 
491
        class_device_destroy(pcidriver_class, privdata->devno);
 
492
 
 
493
        /* Releasing privdata */
 
494
        kfree(privdata);
 
495
 
 
496
        /* Disabling PCI device */
 
497
        pci_disable_device(pdev);
 
498
 
 
499
        mod_info("Device at %s removed\n", dev_name(&pdev->dev));
 
500
}
 
501
 
 
502
/*************************************************************************/
 
503
/* File operations */
 
504
/*************************************************************************/
 
505
 
 
506
/**
 
507
 * This struct defines the file operation entry points.
 
508
 *
 
509
 * @see pcidriver_ioctl
 
510
 * @see pcidriver_mmap
 
511
 * @see pcidriver_open
 
512
 * @see pcidriver_release
 
513
 *
 
514
 */
 
515
static struct file_operations pcidriver_fops = {
 
516
        .owner = THIS_MODULE,
 
517
        .ioctl = pcidriver_ioctl,
 
518
        .mmap = pcidriver_mmap,
 
519
        .open = pcidriver_open,
 
520
        .release = pcidriver_release,
 
521
};
 
522
 
 
523
/**
 
524
 *
 
525
 * Called when an application open()s a /dev/fpga*, attaches the private data
 
526
 * with the file pointer.
 
527
 *
 
528
 */
 
529
int pcidriver_open(struct inode *inode, struct file *filp)
 
530
{
 
531
        pcidriver_privdata_t *privdata;
 
532
 
 
533
        /* Set the private data area for the file */
 
534
        privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev);
 
535
        filp->private_data = privdata;
 
536
 
 
537
        return 0;
 
538
}
 
539
 
 
540
/**
 
541
 *
 
542
 * Called when the application close()s the file descriptor. Does nothing at
 
543
 * the moment.
 
544
 *
 
545
 */
 
546
int pcidriver_release(struct inode *inode, struct file *filp)
 
547
{
 
548
        pcidriver_privdata_t *privdata;
 
549
 
 
550
        /* Get the private data area */
 
551
        privdata = filp->private_data;
 
552
 
 
553
        return 0;
 
554
}
 
555
 
 
556
/**
 
557
 *
 
558
 * This function is the entry point for mmap() and calls either pcidriver_mmap_pci
 
559
 * or pcidriver_mmap_kmem
 
560
 *
 
561
 * @see pcidriver_mmap_pci
 
562
 * @see pcidriver_mmap_kmem
 
563
 *
 
564
 */
 
565
int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma)
 
566
{
 
567
        pcidriver_privdata_t *privdata;
 
568
        int ret = 0, bar;
 
569
 
 
570
        mod_info_dbg("Entering mmap\n");
 
571
 
 
572
        /* Get the private data area */
 
573
        privdata = filp->private_data;
 
574
 
 
575
        /* Check the current mmap mode */
 
576
        switch (privdata->mmap_mode) {
 
577
                case PCIDRIVER_MMAP_PCI:
 
578
                        /* Mmap a PCI region */
 
579
                        switch (privdata->mmap_area) {
 
580
                                case PCIDRIVER_BAR0:    bar = 0; break;
 
581
                                case PCIDRIVER_BAR1:    bar = 1; break;
 
582
                                case PCIDRIVER_BAR2:    bar = 2; break;
 
583
                                case PCIDRIVER_BAR3:    bar = 3; break;
 
584
                                case PCIDRIVER_BAR4:    bar = 4; break;
 
585
                                case PCIDRIVER_BAR5:    bar = 5; break;
 
586
                                default:
 
587
                                        mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area);
 
588
                                        return -EINVAL;                 /* invalid parameter */
 
589
                                        break;
 
590
                        }
 
591
                        ret = pcidriver_mmap_pci(privdata, vma, bar);
 
592
                        break;
 
593
                case PCIDRIVER_MMAP_KMEM:
 
594
                        /* mmap a Kernel buffer */
 
595
                        ret = pcidriver_mmap_kmem(privdata, vma);
 
596
                        break;
 
597
                default:
 
598
                        mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode );
 
599
                        return -EINVAL;                 /* Invalid parameter (mode) */
 
600
        }
 
601
 
 
602
        return ret;
 
603
}
 
604
 
 
605
/*************************************************************************/
 
606
/* Internal driver functions */
 
607
int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar)
 
608
{
 
609
        int ret = 0;
 
610
        unsigned long bar_addr;
 
611
        unsigned long bar_length, vma_size;
 
612
        unsigned long bar_flags;
 
613
 
 
614
        mod_info_dbg("Entering mmap_pci\n");
 
615
 
 
616
        /* Get info of the BAR to be mapped */
 
617
        bar_addr = pci_resource_start(privdata->pdev, bar);
 
618
        bar_length = pci_resource_len(privdata->pdev, bar);
 
619
        bar_flags = pci_resource_flags(privdata->pdev, bar);
 
620
 
 
621
        /* Check sizes */
 
622
        vma_size = (vmap->vm_end - vmap->vm_start);
 
623
        if ((vma_size != bar_length) &&
 
624
           ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) {
 
625
                mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size );
 
626
                return -EINVAL;
 
627
        }
 
628
 
 
629
        if (bar_flags & IORESOURCE_IO) {
 
630
                /* Unlikely case, we will mmap a IO region */
 
631
 
 
632
                /* IO regions are never cacheable */
 
633
#ifdef pgprot_noncached
 
634
                vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot);
 
635
#endif
 
636
 
 
637
                /* Map the BAR */
 
638
                ret = io_remap_pfn_range_compat(
 
639
                                        vmap,
 
640
                                        vmap->vm_start,
 
641
                                        bar_addr,
 
642
                                        bar_length,
 
643
                                        vmap->vm_page_prot);
 
644
        } else {
 
645
                /* Normal case, mmap a memory region */
 
646
 
 
647
                /* Ensure this VMA is non-cached, if it is not flaged as prefetchable.
 
648
                 * If it is prefetchable, caching is allowed and will give better performance.
 
649
                 * This should be set properly by the BIOS, but we want to be sure. */
 
650
                /* adapted from drivers/char/mem.c, mmap function. */
 
651
#ifdef pgprot_noncached
 
652
/* Setting noncached disables MTRR registers, and we want to use them.
 
653
 * So we take this code out. This can lead to caching problems if and only if
 
654
 * the System BIOS set something wrong. Check LDDv3, page 425.
 
655
 */
 
656
//              if (!(bar_flags & IORESOURCE_PREFETCH))
 
657
//                      vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot);
 
658
#endif
 
659
 
 
660
                /* Map the BAR */
 
661
                ret = remap_pfn_range_compat(
 
662
                                        vmap,
 
663
                                        vmap->vm_start,
 
664
                                        bar_addr,
 
665
                                        bar_length,
 
666
                                        vmap->vm_page_prot);
 
667
        }
 
668
 
 
669
        if (ret) {
 
670
                mod_info("remap_pfn_range failed\n");
 
671
                return -EAGAIN;
 
672
        }
 
673
 
 
674
        return 0;       /* success */
 
675
}