/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: 2016-03-02 18:37:30 UTC
  • Revision ID: csa@suren.me-20160302183730-nlrgi7h3yuizcizc
Restructure driver headers

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
 
 
10
 
 
11
1
#include <linux/string.h>
12
2
#include <linux/slab.h>
13
3
#include <linux/types.h>
30
20
#include <linux/wait.h>
31
21
 
32
22
#include "../pcilib/version.h"
33
 
 
34
 
#include "config.h"
35
 
#include "compat.h"
36
 
#include "pciDriver.h"
37
 
#include "common.h"
 
23
#include "build.h"
38
24
#include "base.h"
39
 
#include "int.h"
40
 
#include "kmem.h"
41
 
#include "umem.h"
42
 
#include "ioctl.h"
43
 
#include "build.h"
44
 
 
45
 
/*************************************************************************/
46
 
/* Module device table associated with this driver */
47
 
MODULE_DEVICE_TABLE(pci, pcidriver_ids);
48
 
 
49
 
/* Module init and exit points */
50
 
module_init(pcidriver_init);
51
 
module_exit(pcidriver_exit);
 
25
 
 
26
 
52
27
 
53
28
/* Module info */
54
29
MODULE_AUTHOR("Guillermo Marcus");
55
30
MODULE_DESCRIPTION("Simple PCI Driver");
56
31
MODULE_LICENSE("GPL v2");
57
32
 
 
33
/*
 
34
 * This is the table of PCI devices handled by this driver by default
 
35
 * If you want to add devices dynamically to this list, do:
 
36
 *
 
37
 *   echo "vendor device" > /sys/bus/pci/drivers/pciDriver/new_id
 
38
 * where vendor and device are in hex, without leading '0x'.
 
39
 */
 
40
 
 
41
static const __devinitdata struct pci_device_id pcidriver_ids[] = {
 
42
    { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_ML605_DEVICE_ID ) },          // PCI-E Xilinx ML605
 
43
    { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_IPECAMERA_DEVICE_ID ) },      // PCI-E IPE Camera
 
44
    { PCI_DEVICE( PCIE_XILINX_VENDOR_ID, PCIE_KAPTURE_DEVICE_ID ) },        // PCI-E KAPTURE board for HEB
 
45
    {0,0,0,0},
 
46
};
 
47
 
 
48
MODULE_DEVICE_TABLE(pci, pcidriver_ids);
 
49
 
58
50
/* Module class */
59
 
static struct class_compat *pcidriver_class;
 
51
static struct class *pcidriver_class;
60
52
 
61
53
#ifdef PCIDRIVER_DUMMY_DEVICE
62
54
pcidriver_privdata_t *pcidriver_dummydata = NULL;
63
 
#endif /* PCIDRIVER_DUMMY_DEVICE */
64
 
 
65
 
/**
66
 
 *
67
 
 * Called when loading the driver
68
 
 *
69
 
 */
70
 
static int __init pcidriver_init(void)
71
 
{
72
 
    int err = 0;
73
 
 
74
 
    /* Initialize the device count */
75
 
    atomic_set(&pcidriver_deviceCount, 0);
76
 
 
77
 
    memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata));
78
 
 
79
 
    /* Allocate character device region dynamically */
80
 
    if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) {
81
 
        mod_info("Couldn't allocate chrdev region. Module not loaded.\n");
82
 
        goto init_alloc_fail;
83
 
    }
84
 
    mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME);
85
 
 
86
 
    /* Register driver class */
87
 
    pcidriver_class = class_create(THIS_MODULE, NODENAME);
88
 
 
89
 
    if (IS_ERR(pcidriver_class)) {
90
 
        mod_info("No sysfs support. Module not loaded.\n");
91
 
        goto init_class_fail;
92
 
    }
93
 
 
94
 
    /* Register PCI driver. This function returns the number of devices on some
95
 
     * systems, therefore check for errors as < 0. */
96
 
#ifdef PCIDRIVER_DUMMY_DEVICE
97
 
    if ((err = pcidriver_probe(NULL, NULL)) < 0) {
98
55
#else /* PCIDRIVER_DUMMY_DEVICE */
99
 
    if ((err = pci_register_driver(&pcidriver_driver)) < 0) {
100
 
#endif /* PCIDRIVER_DUMMY_DEVICE */
101
 
        mod_info("Couldn't register PCI driver. Module not loaded.\n");
102
 
        goto init_pcireg_fail;
103
 
    }
104
 
 
105
 
    mod_info("pcidriver %u.%u.%u loaded\n", PCILIB_VERSION_GET_MAJOR(PCILIB_VERSION), PCILIB_VERSION_GET_MINOR(PCILIB_VERSION), PCILIB_VERSION_GET_MICRO(PCILIB_VERSION));
106
 
    mod_info("%s\n", PCIDRIVER_BUILD);
107
 
    mod_info("%s\n", PCIDRIVER_REVISION);
108
 
    if (strlen(PCIDRIVER_CHANGES)) {
109
 
        mod_info("Extra changes - %s\n", PCIDRIVER_CHANGES);
110
 
    }
111
 
 
112
 
    return 0;
113
 
 
114
 
init_pcireg_fail:
115
 
    class_destroy(pcidriver_class);
116
 
init_class_fail:
117
 
    unregister_chrdev_region(pcidriver_devt, MAXDEVICES);
118
 
init_alloc_fail:
119
 
    return err;
120
 
}
121
 
 
122
 
/**
123
 
 *
124
 
 * Called when unloading the driver
125
 
 *
126
 
 */
127
 
static void pcidriver_exit(void)
128
 
{
129
 
#ifdef PCIDRIVER_DUMMY_DEVICE
130
 
    pcidriver_remove(NULL);
131
 
#else
132
 
    pci_unregister_driver(&pcidriver_driver);
133
 
#endif /* PCIDRIVER_DUMMY_DEVICE */
134
 
 
135
 
    unregister_chrdev_region(pcidriver_devt, MAXDEVICES);
136
 
 
137
 
    if (pcidriver_class != NULL)
138
 
        class_destroy(pcidriver_class);
139
 
 
140
 
    mod_info("Module unloaded\n");
141
 
}
142
 
 
143
 
/*************************************************************************/
144
 
/* Driver functions */
145
 
 
146
 
/**
147
 
 *
148
 
 * This struct defines the PCI entry points.
149
 
 * Will be registered at module init.
150
 
 *
151
 
 */
152
 
#ifndef PCIDRIVER_DUMMY_DEVICE
153
 
static struct pci_driver pcidriver_driver = {
154
 
    .name = MODNAME,
155
 
    .id_table = pcidriver_ids,
156
 
    .probe = pcidriver_probe,
157
 
    .remove = pcidriver_remove,
158
 
};
159
 
#endif /* ! PCIDRIVER_DUMMY_DEVICE */
160
 
 
161
 
/**
162
 
 *
 
56
static struct pci_driver pcidriver_driver;
 
57
#endif /* PCIDRIVER_DUMMY_DEVICE */
 
58
 
 
59
/* Hold the allocated major & minor numbers */
 
60
static dev_t pcidriver_devt;
 
61
 
 
62
/* Number of devices allocated */
 
63
static atomic_t pcidriver_deviceCount;
 
64
 
 
65
/* Private data for probed devices */
 
66
static pcidriver_privdata_t* pcidriver_privdata[MAXDEVICES];
 
67
 
 
68
 
 
69
pcidriver_privdata_t *pcidriver_get_privdata(int devid) {
 
70
    if (devid >= MAXDEVICES)
 
71
        return NULL;
 
72
 
 
73
    return pcidriver_privdata[devid];
 
74
}
 
75
 
 
76
void pcidriver_put_privdata(pcidriver_privdata_t *privdata) {
 
77
 
 
78
}
 
79
 
 
80
/**
163
81
 * This function is called when installing the driver for a device
164
82
 * @param pdev Pointer to the PCI device
165
 
 *
166
83
 */
167
84
static int __devinit pcidriver_probe(struct pci_dev *pdev, const struct pci_device_id *id)
168
85
{
250
167
    privdata->devno = devno;
251
168
 
252
169
    /* FIXME: some error checking missing here */
253
 
#ifdef PCIDRIVER_DUMMY_DEVICE
254
 
    privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, NULL, NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata);
255
 
#else /* PCIDRIVER_DUMMY_DEVICE */
256
 
    privdata->class_dev = class_device_create(pcidriver_class, NULL, devno, &(pdev->dev), NODENAMEFMT, MINOR(pcidriver_devt) + devid, privdata);
257
 
#endif /* PCIDRIVER_DUMMY_DEVICE */
258
 
    class_set_devdata( privdata->class_dev, privdata );
 
170
    privdata->class_dev = device_create(pcidriver_class, NULL, devno, privdata, NODENAMEFMT, MINOR(pcidriver_devt) + devid);
 
171
    dev_set_drvdata(privdata->class_dev, privdata);
259
172
    mod_info("Device /dev/%s%d added\n",NODENAME,MINOR(pcidriver_devt) + devid);
260
173
 
261
174
#ifndef PCIDRIVER_DUMMY_DEVICE
264
177
        goto probe_irq_probe_fail;
265
178
#endif /* ! PCIDRIVER_DUMMY_DEVICE */
266
179
 
267
 
    /* Populate sysfs attributes for the class device */
268
180
    /* TODO: correct errorhandling. ewww. must remove the files in reversed order :-( */
269
 
#define sysfs_attr(name) do { \
270
 
                        if (class_device_create_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)) != 0) \
271
 
                                goto probe_device_create_fail; \
272
 
                        } while (0)
273
 
#ifdef ENABLE_IRQ
274
 
    sysfs_attr(irq_count);
275
 
    sysfs_attr(irq_queues);
276
 
#endif
277
 
 
278
 
    sysfs_attr(mmap_mode);
279
 
    sysfs_attr(mmap_area);
280
 
    sysfs_attr(kmem_count);
281
 
    sysfs_attr(kmem_alloc);
282
 
    sysfs_attr(kmem_free);
283
 
    sysfs_attr(kbuffers);
284
 
    sysfs_attr(umappings);
285
 
    sysfs_attr(umem_unmap);
286
 
#undef sysfs_attr
 
181
    if (pcidriver_create_sysfs_attributes(privdata) != 0)
 
182
        goto probe_device_create_fail;
287
183
 
288
184
    /* Register character device */
289
 
    cdev_init( &(privdata->cdev), &pcidriver_fops );
 
185
    cdev_init(&(privdata->cdev), pcidriver_get_fops());
290
186
    privdata->cdev.owner = THIS_MODULE;
291
 
    privdata->cdev.ops = &pcidriver_fops;
 
187
    privdata->cdev.ops = pcidriver_get_fops();
292
188
    err = cdev_add( &privdata->cdev, devno, 1 );
293
189
    if (err) {
294
190
        mod_info( "Couldn't add character device.\n" );
338
234
    pcidriver_privdata[privdata->devid] = NULL;
339
235
 
340
236
    /* Removing sysfs attributes from class device */
341
 
#define sysfs_attr(name) do { \
342
 
                        class_device_remove_file(sysfs_attr_def_pointer, &sysfs_attr_def_name(name)); \
343
 
                        } while (0)
344
 
#ifdef ENABLE_IRQ
345
 
    sysfs_attr(irq_count);
346
 
    sysfs_attr(irq_queues);
347
 
#endif
348
 
 
349
 
    sysfs_attr(mmap_mode);
350
 
    sysfs_attr(mmap_area);
351
 
    sysfs_attr(kmem_count);
352
 
    sysfs_attr(kmem_alloc);
353
 
    sysfs_attr(kmem_free);
354
 
    sysfs_attr(kbuffers);
355
 
    sysfs_attr(umappings);
356
 
    sysfs_attr(umem_unmap);
357
 
#undef sysfs_attr
 
237
    pcidriver_remove_sysfs_attributes(privdata);
358
238
 
359
239
    /* Free all allocated kmem buffers before leaving */
360
240
    pcidriver_kmem_free_all( privdata );
369
249
    cdev_del(&(privdata->cdev));
370
250
 
371
251
    /* Removing the device from sysfs */
372
 
    class_device_destroy(pcidriver_class, privdata->devno);
 
252
    device_destroy(pcidriver_class, privdata->devno);
373
253
 
374
254
    /* Releasing privdata */
375
255
    kfree(privdata);
384
264
 
385
265
}
386
266
 
387
 
/*************************************************************************/
388
 
/* File operations */
389
 
/*************************************************************************/
390
 
 
391
 
/**
392
 
 * This struct defines the file operation entry points.
393
 
 *
394
 
 * @see pcidriver_ioctl
395
 
 * @see pcidriver_mmap
396
 
 * @see pcidriver_open
397
 
 * @see pcidriver_release
398
 
 *
399
 
 */
400
 
static struct file_operations pcidriver_fops = {
401
 
    .owner = THIS_MODULE,
402
 
    .unlocked_ioctl = pcidriver_ioctl,
403
 
    .mmap = pcidriver_mmap,
404
 
    .open = pcidriver_open,
405
 
    .release = pcidriver_release,
 
267
#ifndef PCIDRIVER_DUMMY_DEVICE
 
268
static struct pci_driver pcidriver_driver = {
 
269
    .name = MODNAME,
 
270
    .id_table = pcidriver_ids,
 
271
    .probe = pcidriver_probe,
 
272
    .remove = pcidriver_remove,
406
273
};
407
 
 
408
 
void pcidriver_module_get(pcidriver_privdata_t *privdata) {
409
 
    atomic_inc(&(privdata->refs));
410
 
//    mod_info("Ref: %i\n", atomic_read(&(privdata->refs)));
411
 
}
412
 
 
413
 
void pcidriver_module_put(pcidriver_privdata_t *privdata) {
414
 
    if (atomic_add_negative(-1, &(privdata->refs))) {
415
 
        atomic_inc(&(privdata->refs));
416
 
        mod_info("Reference counting error...");
417
 
    } else {
418
 
//      mod_info("Unref: %i\n", atomic_read(&(privdata->refs)));
419
 
    }
420
 
}
421
 
 
422
 
/**
423
 
 *
424
 
 * Called when an application open()s a /dev/fpga*, attaches the private data
425
 
 * with the file pointer.
426
 
 *
427
 
 */
428
 
int pcidriver_open(struct inode *inode, struct file *filp)
429
 
{
430
 
    pcidriver_privdata_t *privdata;
431
 
 
432
 
    /* Set the private data area for the file */
433
 
    privdata = container_of( inode->i_cdev, pcidriver_privdata_t, cdev);
434
 
    filp->private_data = privdata;
435
 
 
436
 
    pcidriver_module_get(privdata);
437
 
 
438
 
    return 0;
439
 
}
440
 
 
441
 
/**
442
 
 *
443
 
 * Called when the application close()s the file descriptor. Does nothing at
444
 
 * the moment.
445
 
 *
446
 
 */
447
 
int pcidriver_release(struct inode *inode, struct file *filp)
448
 
{
449
 
    pcidriver_privdata_t *privdata;
450
 
 
451
 
    /* Get the private data area */
452
 
    privdata = filp->private_data;
453
 
 
454
 
    pcidriver_module_put(privdata);
455
 
 
456
 
    return 0;
457
 
}
458
 
 
459
 
/**
460
 
 *
461
 
 * This function is the entry point for mmap() and calls either pcidriver_mmap_pci
462
 
 * or pcidriver_mmap_kmem
463
 
 *
464
 
 * @see pcidriver_mmap_pci
465
 
 * @see pcidriver_mmap_kmem
466
 
 *
467
 
 */
468
 
int pcidriver_mmap(struct file *filp, struct vm_area_struct *vma)
469
 
{
470
 
    pcidriver_privdata_t *privdata;
471
 
    int ret = 0, bar;
472
 
 
473
 
    mod_info_dbg("Entering mmap\n");
474
 
 
475
 
    /* Get the private data area */
476
 
    privdata = filp->private_data;
477
 
 
478
 
    /* Check the current mmap mode */
479
 
    switch (privdata->mmap_mode) {
480
 
    case PCIDRIVER_MMAP_PCI:
481
 
        /* Mmap a PCI region */
482
 
        switch (privdata->mmap_area) {
483
 
        case PCIDRIVER_BAR0:
484
 
            bar = 0;
485
 
            break;
486
 
        case PCIDRIVER_BAR1:
487
 
            bar = 1;
488
 
            break;
489
 
        case PCIDRIVER_BAR2:
490
 
            bar = 2;
491
 
            break;
492
 
        case PCIDRIVER_BAR3:
493
 
            bar = 3;
494
 
            break;
495
 
        case PCIDRIVER_BAR4:
496
 
            bar = 4;
497
 
            break;
498
 
        case PCIDRIVER_BAR5:
499
 
            bar = 5;
500
 
            break;
501
 
        default:
502
 
            mod_info("Attempted to mmap a PCI area with the wrong mmap_area value: %d\n",privdata->mmap_area);
503
 
            return -EINVAL;                     /* invalid parameter */
504
 
            break;
505
 
        }
506
 
        ret = pcidriver_mmap_pci(privdata, vma, bar);
507
 
        break;
508
 
    case PCIDRIVER_MMAP_KMEM:
509
 
        /* mmap a Kernel buffer */
510
 
        ret = pcidriver_mmap_kmem(privdata, vma);
511
 
        break;
512
 
    default:
513
 
        mod_info( "Invalid mmap_mode value (%d)\n",privdata->mmap_mode );
514
 
        return -EINVAL;                 /* Invalid parameter (mode) */
515
 
    }
516
 
 
517
 
    return ret;
518
 
}
519
 
 
520
 
/*************************************************************************/
521
 
/* Internal driver functions */
522
 
int pcidriver_mmap_pci(pcidriver_privdata_t *privdata, struct vm_area_struct *vmap, int bar)
523
 
{
 
274
#endif /* ! PCIDRIVER_DUMMY_DEVICE */
 
275
 
 
276
static int __init pcidriver_init(void)
 
277
{
 
278
    int err = 0;
 
279
 
 
280
    /* Initialize the device count */
 
281
    atomic_set(&pcidriver_deviceCount, 0);
 
282
 
 
283
    memset(pcidriver_privdata, 0, sizeof(pcidriver_privdata));
 
284
 
 
285
    /* Allocate character device region dynamically */
 
286
    if ((err = alloc_chrdev_region(&pcidriver_devt, MINORNR, MAXDEVICES, NODENAME)) != 0) {
 
287
        mod_info("Couldn't allocate chrdev region. Module not loaded.\n");
 
288
        goto init_alloc_fail;
 
289
    }
 
290
    mod_info("Major %d allocated to nodename '%s'\n", MAJOR(pcidriver_devt), NODENAME);
 
291
 
 
292
    /* Register driver class */
 
293
    pcidriver_class = class_create(THIS_MODULE, NODENAME);
 
294
 
 
295
    if (IS_ERR(pcidriver_class)) {
 
296
        mod_info("No sysfs support. Module not loaded.\n");
 
297
        goto init_class_fail;
 
298
    }
 
299
 
 
300
    /* Register PCI driver. This function returns the number of devices on some
 
301
     * systems, therefore check for errors as < 0. */
524
302
#ifdef PCIDRIVER_DUMMY_DEVICE
525
 
    return -ENXIO;
 
303
    if ((err = pcidriver_probe(NULL, NULL)) < 0) {
526
304
#else /* PCIDRIVER_DUMMY_DEVICE */
527
 
    int ret = 0;
528
 
    unsigned long bar_addr;
529
 
    unsigned long bar_length, vma_size;
530
 
    unsigned long bar_flags;
531
 
 
532
 
    mod_info_dbg("Entering mmap_pci\n");
533
 
 
534
 
 
535
 
    /* Get info of the BAR to be mapped */
536
 
    bar_addr = pci_resource_start(privdata->pdev, bar);
537
 
    bar_length = pci_resource_len(privdata->pdev, bar);
538
 
    bar_flags = pci_resource_flags(privdata->pdev, bar);
539
 
 
540
 
    /* Check sizes */
541
 
    vma_size = (vmap->vm_end - vmap->vm_start);
542
 
 
543
 
    if ((vma_size != bar_length) &&
544
 
            ((bar_length < PAGE_SIZE) && (vma_size != PAGE_SIZE))) {
545
 
        mod_info( "mmap size is not correct! bar: %lu - vma: %lu\n", bar_length, vma_size );
546
 
        return -EINVAL;
547
 
    }
548
 
 
549
 
    if (bar_flags & IORESOURCE_IO) {
550
 
        /* Unlikely case, we will mmap a IO region */
551
 
 
552
 
        /* IO regions are never cacheable */
553
 
        vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot);
554
 
 
555
 
        /* Map the BAR */
556
 
        ret = io_remap_pfn_range_compat(vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot);
557
 
    } else {
558
 
        /* Normal case, mmap a memory region */
559
 
 
560
 
        /* Ensure this VMA is non-cached, if it is not flaged as prefetchable.
561
 
         * If it is prefetchable, caching is allowed and will give better performance.
562
 
         * This should be set properly by the BIOS, but we want to be sure. */
563
 
        /* adapted from drivers/char/mem.c, mmap function. */
564
 
 
565
 
        /* Setting noncached disables MTRR registers, and we want to use them.
566
 
         * So we take this code out. This can lead to caching problems if and only if
567
 
         * the System BIOS set something wrong. Check LDDv3, page 425.
568
 
         */
569
 
 
570
 
//                if (!(bar_flags & IORESOURCE_PREFETCH))
571
 
//                      vmap->vm_page_prot = pgprot_noncached(vmap->vm_page_prot);
572
 
 
573
 
 
574
 
        /* Map the BAR */
575
 
        ret = remap_pfn_range_compat(vmap, vmap->vm_start, bar_addr, bar_length, vmap->vm_page_prot);
576
 
    }
577
 
 
578
 
    if (ret) {
579
 
        mod_info("remap_pfn_range failed\n");
580
 
        return -EAGAIN;
581
 
    }
582
 
 
583
 
    return 0;   /* success */
584
 
#endif /* PCIDRIVER_DUMMY_DEVICE */
585
 
}
586
 
 
587
 
pcidriver_privdata_t *pcidriver_get_privdata(int devid) {
588
 
    if (devid >= MAXDEVICES)
589
 
        return NULL;
590
 
 
591
 
    return pcidriver_privdata[devid];
592
 
}
593
 
 
594
 
void pcidriver_put_privdata(pcidriver_privdata_t *privdata) {
595
 
 
596
 
}
 
305
    if ((err = pci_register_driver(&pcidriver_driver)) < 0) {
 
306
#endif /* PCIDRIVER_DUMMY_DEVICE */
 
307
        mod_info("Couldn't register PCI driver. Module not loaded.\n");
 
308
        goto init_pcireg_fail;
 
309
    }
 
310
 
 
311
    mod_info("pcidriver %u.%u.%u loaded\n", PCILIB_VERSION_GET_MAJOR(PCILIB_VERSION), PCILIB_VERSION_GET_MINOR(PCILIB_VERSION), PCILIB_VERSION_GET_MICRO(PCILIB_VERSION));
 
312
    mod_info("%s\n", PCIDRIVER_BUILD);
 
313
    mod_info("%s\n", PCIDRIVER_REVISION);
 
314
    if (strlen(PCIDRIVER_CHANGES)) {
 
315
        mod_info("Extra changes - %s\n", PCIDRIVER_CHANGES);
 
316
    }
 
317
 
 
318
    return 0;
 
319
 
 
320
init_pcireg_fail:
 
321
    class_destroy(pcidriver_class);
 
322
init_class_fail:
 
323
    unregister_chrdev_region(pcidriver_devt, MAXDEVICES);
 
324
init_alloc_fail:
 
325
    return err;
 
326
}
 
327
 
 
328
static void pcidriver_exit(void)
 
329
{
 
330
#ifdef PCIDRIVER_DUMMY_DEVICE
 
331
    pcidriver_remove(NULL);
 
332
#else
 
333
    pci_unregister_driver(&pcidriver_driver);
 
334
#endif /* PCIDRIVER_DUMMY_DEVICE */
 
335
 
 
336
    unregister_chrdev_region(pcidriver_devt, MAXDEVICES);
 
337
 
 
338
    if (pcidriver_class != NULL)
 
339
        class_destroy(pcidriver_class);
 
340
 
 
341
    mod_info("Module unloaded\n");
 
342
}
 
343
 
 
344
module_init(pcidriver_init);
 
345
module_exit(pcidriver_exit);