Right now I am working with writing an interrupt routine for my PCI card. The interrupt receives the interrupt signal and schedules the signal handler (the signal handler is running in user space) to run later. So far, so good. But the problem is that the interrupt I'm using is shared with another device. Is there any proper way to make my interrupt routine find out which device caused the interrupt? I want the interrupt routine to only schedule the signal handler when the correct device is interrupting. Olav T Cut and paste from the irqsignal_mod.c module: Requesting IRQ: struct pci_dev *pdev; pdev = pci_find_device(4466,4, NULL); result = request_irq(pdev->irq, irqsignal_interrupt, SA_INTERRUPT | SA_SHIRQ, THIS_MODULE->name, pdev); if (result != 0) goto handle_error; }; The interrupt routine: void irqsignal_interrupt(int irq, void *dev_id, struct pt_regs *regs) { # ifdef DEBUG printk(KERN_INFO "%s: INTERRUPT START, irq %d\n", THIS_MODULE->name, irq); # endif struct pci_dev *pdevkvakk=dev_id; irqsignal_dispatcher_task.data = (void*)irq; // Send the irq number as input data to the dispatcher. schedule_task(&irqsignal_dispatcher_task); # ifdef DEBUG printk(KERN_INFO "%s: INTERRUPT END, irq %d\n", THIS_MODULE->name, irq); # endif }; The complete source code for irqsignal_mod.c: /********************************************************************************* Author: Artur Szostak Email: artur@xxxxxxxxxxxxxxxxxxx A kernel module implementing a signaling mechanism to notify user space programs of IRQ events. Parts of this code come from the sample codes of "Linux Device Drivers, 2nd Edition", By Alessandro Rubini & Jonathan Corbet ISDN 0-59600-008-1 **********************************************************************************/ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include <linux/config.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/kernel.h> /* printk() */ #include <linux/fs.h> /* everything... */ #include <linux/errno.h> /* error codes */ #include <linux/delay.h> /* udelay */ #include <linux/mm.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/interrupt.h> #include <linux/tqueue.h> #include <linux/spinlock.h> #include <linux/pci.h> #include <asm/io.h> #include <asm/uaccess.h> #include "irqsignal_mod.h" #ifndef KERNEL_VERSION /* pre-2.1.90 didn't have it */ # define KERNEL_VERSION(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) ) #endif /* Only support kernels newer than 2.4.x */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) # error "This kernel is too old: not supported by this file" #endif // Some module descriptions... MODULE_AUTHOR("Artur Szostak <artur@xxxxxxxxxxxxxxxxxxx>"); MODULE_DESCRIPTION("Signaling mechanism of IRQ events for user space programs."); MODULE_LICENSE("GPL"); /* The following parameters can be passed in during module load time. eg. insmod irqsignal major=36 */ static int major = 0; // Major version of character device. MODULE_PARM(major, "i"); rwlock_t irqdata_lock = RW_LOCK_UNLOCKED; // The lock for the irqsignal_irqdata structure. /* The structure of the irqsignal_irqdata[i] array elements. */ typedef struct irqsignal_tasklist_entry { struct list_head list; // Required for kernel lists. int signal; // The signal to send to the process. struct task_struct* task; // The task structure of the process receiving the signal. } irqsignal_tasklist_entry_t; typedef struct list_head list_head_t; struct pci_dev *pdev; // A array of kernel lists. Each list contains elements of type irqsignal_tasklist_entry. list_head_t irqsignal_irqdata[MAX_NUMBER_OF_IRQS]; /* Prints the task list for the specified IRQ. */ void irqsignal_printlist(unsigned int irq) { #ifdef DEBUG list_head_t* list; list_head_t* ptr; irqsignal_tasklist_entry_t* entry; printk(KERN_INFO "%s: ======== DUMPING TASK LIST FOR IRQ %d ===========\n", THIS_MODULE->name, irq); // Go through the list of tasks in the task list for the specified IRQ and // print each one to the kernel message buffer. list = &irqsignal_irqdata[irq]; for (ptr = list->next; ptr != list; ptr = ptr->next) { entry = list_entry(ptr, irqsignal_tasklist_entry_t, list); printk(KERN_INFO "%s: ptr [0x%lX]\tptr->next [0x%lX]\tptr->prev [0x%lX]\ttask pid %d\tsignal %d\n", THIS_MODULE->name, (unsigned long)ptr, (unsigned long)ptr->next, (unsigned long)ptr->prev, entry->task->pid, entry->signal); }; printk(KERN_INFO "%s: =================================================\n", THIS_MODULE->name); #endif }; /* We must initialise the data used by this kernel module, especially list of type list_head. */ void irqsignal_initialise_irqdata(void) { int i; for (i = 0; i < MAX_NUMBER_OF_IRQS; i++) { INIT_LIST_HEAD( &irqsignal_irqdata[i] ); irqsignal_printlist(i); }; }; /* Adds the current task to the task list for the specified IRQ to receive the specified signal. A task can only add one entry for any specific signal number for any IRQ. But more than one signal number can be added if it is not already in the IRQ's task list. i.e. signal numbers must be unique per task per IRQ. Note: "irq" must be a valid number in the range 0..(MAX_NUMBER_OF_IRQS-1) No range checking is performed in this routine. Possible return values: IRQSIG_OK - Added successfully. IRQSIG_ALREADY_REGISTERED - Entry already exists in the task list for the specified IRQ. IRQSIG_OUTOFMEM - Could not allocate memory with kmalloc. */ irqsignal_status_t irqsignal_addtolist(unsigned int irq, int signal) { list_head_t* list = &irqsignal_irqdata[irq]; list_head_t* ptr; irqsignal_tasklist_entry_t* entry, *newentry; // Check that the current task and corresponding signal is not in the task list // for the specified irq. We only want unique entries. for (ptr = list->next; ptr != list; ptr = ptr->next) { entry = list_entry(ptr, irqsignal_tasklist_entry_t, list); if (entry->task == current && entry->signal == signal) return IRQSIG_ALREADY_REGISTERED; }; // Allocate memory and store the new entry. newentry = kmalloc( sizeof(irqsignal_tasklist_entry_t), GFP_KERNEL ); if (newentry == NULL) return IRQSIG_OUTOFMEM; INIT_LIST_HEAD( &newentry->list ); newentry->task = current; // add the current task to the list. newentry->signal = signal; // signal to send back to task on IRQ event. list_add_tail(&newentry->list, list); return IRQSIG_OK; }; /* Removes the current task from the specified IRQ task list so that the current process does not get notification of the specified signal. If other signal number notifications were registered for the same IRQ then they will still be in effect. Note: "irq" must be a valid number in the range 0..(MAX_NUMBER_OF_IRQS-1) No range checking is performed in this routine. Possible return values: IRQSIG_OK - Removed successfully. IRQSIG_NOT_REGISTERED - The entry for the current task and specified signal does not exist for the IRQ. */ irqsignal_status_t irqsignal_removefromlist(unsigned int irq, int signal) { list_head_t* list = &irqsignal_irqdata[irq]; list_head_t* ptr; irqsignal_tasklist_entry_t* entry; if (list_empty(list)) return IRQSIG_NOT_REGISTERED; // Find the task and corresponding signal. for (ptr = list->next; ptr != list; ptr = ptr->next) { entry = list_entry(ptr, irqsignal_tasklist_entry_t, list); if (entry->task == current && entry->signal == signal) { // Remove from the list and free the memory. list_del(ptr); kfree(entry); return IRQSIG_OK; }; }; return IRQSIG_NOT_REGISTERED; // If we got here then we are not registered. }; /* Removes all the signal number notifications from the specified IRQ's task list only for the current task. Note: "irq" must be a valid number in the range 0..(MAX_NUMBER_OF_IRQS-1) No range checking is performed in this routine. Possible return values: IRQSIG_OK - Removed successfully. IRQSIG_NOT_REGISTERED - No entries were found for the current task and specified IRQ. */ irqsignal_status_t irqsignal_removetaskfromlist(unsigned int irq) { irqsignal_status_t status = IRQSIG_NOT_REGISTERED; list_head_t* list = &irqsignal_irqdata[irq]; list_head_t* ptr = list->next; irqsignal_tasklist_entry_t* entry; // Find all the signals for the current task. while (ptr != list) { entry = list_entry(ptr, irqsignal_tasklist_entry_t, list); ptr = ptr->next; if (entry->task == current) { // Remove the entry we found from the list and free the memory. list_del( &entry->list ); kfree(entry); status = IRQSIG_OK; }; }; return status; }; /* Removes all entries form the irqsignal_irqdata task lists. Thus no tasks will recieve IRQ notification. Note: "irq" must be a valid number in the range 0..(MAX_NUMBER_OF_IRQS-1) No range checking is performed in this routine. */ void irqsignal_clearlist(unsigned int irq) { list_head_t* list = &irqsignal_irqdata[irq]; list_head_t* ptr = list->next; irqsignal_tasklist_entry_t* entry; if (list_empty(list)) return; // Find all the signals for the current task. while (ptr != list) { entry = list_entry(ptr, irqsignal_tasklist_entry_t, list); ptr = ptr->next; // Remove the current entry from the list and free the memory. list_del( &entry->list ); kfree(entry); }; }; /* This routine actually performs the dispatching of the signals to the user space processes. We have to have this done outside of the interrupt handler because a interrupt handler can not call schedule so it is impossible for it to send a signal directly. Note: send_sig_info does just that, it reschedules tasks. */ void irqsignal_dispatcher(void* _irq /* read as: unsigned int irq */) { unsigned int irq = (unsigned int)_irq; // Expect the interrupt handler to send the // IRQ number as the data parameter. list_head_t* list; list_head_t* ptr; irqsignal_tasklist_entry_t* entry; siginfo_t info; // Initialise the siginfo structure. memset(&info, 0, sizeof(info)); info.si_code = SI_KERNEL; // This means the signal came form the kernel. info.si_pid = current->pid; info.si_uid = current->uid; # ifdef DEBUG printk(KERN_DEBUG "%s: Interrupted on IRQ %d, dispatching signals.\n", THIS_MODULE->name, irq); # endif // Sanity check: if (irq >= MAX_NUMBER_OF_IRQS) { printk(KERN_ERR "%s: Signal dispatcher got a invalid IRQ (%d), will not send signals.\n", THIS_MODULE->name, irq); return; }; read_lock(&irqdata_lock); // Must lock the data structures since these are modified // during the register and unregister routines. // Go through the list of tasks in the task list for the fired IRQ and // send the corresponding signals to the tasks. list = &irqsignal_irqdata[irq]; for (ptr = list->next; ptr != list; ptr = ptr->next) { entry = list_entry(ptr, irqsignal_tasklist_entry_t, list); info.si_signo = entry->signal; # ifdef DEBUG printk(KERN_DEBUG "%s: Sending signal number %d to pid %d.\n", THIS_MODULE->name, entry->signal, entry->task->pid); # endif send_sig_info(entry->signal, &info, entry->task); }; read_unlock(&irqdata_lock); }; /* Setup the task structure for the irqsignal_dispatcher routine. This task will be scheduled by the interrupt handler: irqsignal_interrupt */ struct tq_struct irqsignal_dispatcher_task = { LIST_HEAD_INIT(irqsignal_dispatcher_task.list), 0, &irqsignal_dispatcher, NULL }; /* The interrupt handler schedules the dispatcher to dispatch signals to all listed tasks. */ void irqsignal_interrupt(int irq, void *dev_id, struct pt_regs *regs) { # ifdef DEBUG printk(KERN_INFO "%s: INTERRUPT START, irq %d\n", THIS_MODULE->name, irq); # endif struct pci_dev *pdevkvakk=dev_id; printk(KERN_INFO "%s: INTERRUPT START, irq %d\n", pdevkvakk->name, pdevkvakk->irq); if (pdevkvakk->vendor==4466) // Dersom me hev med ein Altera-device aa gjera { irqsignal_dispatcher_task.data = (void*)irq; // Send the irq number as input data to the dispatcher. schedule_task(&irqsignal_dispatcher_task); } # ifdef DEBUG printk(KERN_INFO "%s: INTERRUPT END, irq %d\n", THIS_MODULE->name, irq); # endif }; /* Registers the current task for IRQ signal notification, by adding it to the specific task list in irqsignal_irqdata[i] that corresponds to the IRQ the current task wants to be notified about. Possible return values: IRQSIG_OK - Registered successfully. IRQSIG_IRQ_IN_USE - The IRQ we are trying to register for is already in use by another driver that does not share its IRQ. IRQSIG_ALREADY_REGISTERED - Task entry already exists in the task list for the specified IRQ and signal. IRQSIG_OUTOFMEM - Could not allocate memory for new task entry. IRQSIG_BADIRQ - The IRQ number was invalid. i.e. irq number >= MAX_NUMBER_OF_IRQS IRQSIG_UNKNOWN_ERROR - "request_irq" returned a unexpected result. -EFAULT - Pointer (_info) to data in user space memory is invalid. */ irqsignal_status_t irqsignal_register(irqsignal_register_info_t* _info) { irqsignal_register_info_t info; irqsignal_status_t status; int result; // We need to copy data from user space into kernel space. if ( copy_from_user(&info, _info, sizeof(irqsignal_register_info_t)) ) return -EFAULT; if (info.irq >= MAX_NUMBER_OF_IRQS) return IRQSIG_BADIRQ; write_lock(&irqdata_lock); // Make sure we do not modify the irqsignal_irqdata data structures // while the dispatcher routine is using them. /* If the task list is empty, that means there is no IRQ handler assigned to the specified irq. So we need to register a handler. */ if ( list_empty( &irqsignal_irqdata[info.irq]) ) { // pdev = pci_find_device(0x8086,0x24C3, NULL); pdev = pci_find_device(4466,4, NULL); printk(KERN_INFO "%s: Vendor, irq %d\n", THIS_MODULE->name, pdev->vendor); printk(KERN_INFO "%s: Device, irq %d\n", THIS_MODULE->name, pdev->device); printk(KERN_INFO "%s: Irq, irq %d\n", THIS_MODULE->name, pdev->irq); printk(KERN_INFO "%s: Namn, irq %s\n", THIS_MODULE->name, pdev->name); result = request_irq(pdev->irq, irqsignal_interrupt, SA_INTERRUPT | SA_SHIRQ, THIS_MODULE->name, pdev); if (result != 0) goto handle_error; }; status = irqsignal_addtolist(info.irq, info.signal); irqsignal_printlist(info.irq); if (status != IRQSIG_OK) { // Cleanup on error. free_irq(pdev->irq, pdev); }; write_unlock(&irqdata_lock); return status; handle_error: // Handle any errors from the routine request_irq by making sure we unlock the // data structures we locked (dont want to hang the machine in a dead-lock), and // returning an appropriate error code. write_unlock(&irqdata_lock); switch (result) { case -EBUSY: return IRQSIG_IRQ_IN_USE; case -EINVAL: return IRQSIG_BADIRQ; case -ENOMEM: return IRQSIG_OUTOFMEM; default: return IRQSIG_UNKNOWN_ERROR; }; }; /* Unregisters the current process from receiving the specified signal (_info->signal) for the specified IRQ (_info->irq). If no entry can be found in the irqsignal_irqdata structure for the current task, specified IRQ and signal then an error code is returned and the data structures are left untouched. Possible return values: IRQSIG_OK - Unregistered successfully. IRQSIG_NOT_REGISTERED - No task entry foind in irqsignal_irqdata for the current task. IRQSIG_BADIRQ - The IRQ number was invalid. -EFAULT - Pointer (_info) to data in user space memory is invalid. */ irqsignal_status_t irqsignal_unregister(irqsignal_register_info_t* _info) { irqsignal_register_info_t info; irqsignal_status_t status; // We need to copy data from user space into kernel space. if ( copy_from_user(&info, _info, sizeof(irqsignal_register_info_t)) ) return -EFAULT; if (info.irq >= MAX_NUMBER_OF_IRQS) return IRQSIG_BADIRQ; write_lock(&irqdata_lock); // Make sure we do not modify the irqsignal_irqdata data structures // while the dispatcher routine is using them. // If the task list is empty then we do not own the IRQ and there is nothing to do. if (! list_empty( &irqsignal_irqdata[info.irq]) ) { status = irqsignal_removefromlist(info.irq, info.signal); irqsignal_printlist(info.irq); /* If the task list is empty, we must remove the IRQ handler assigned to the specified irq since no one wants any notifications. */ if ( list_empty( &irqsignal_irqdata[info.irq]) ) free_irq(pdev->irq, pdev); } else status = IRQSIG_NOT_REGISTERED; write_unlock(&irqdata_lock); return status; }; /* Unregisters the current process from receiving any signals for the specified IRQ. If no entries can be found in irqsignal_irqdata for the current task then an error code is returned and the data structures are left untouched. Possible return values: IRQSIG_OK - Unregistered successfully. IRQSIG_NOT_REGISTERED - No task entries found in irqsignal_irqdata for the current task. IRQSIG_BADIRQ - The IRQ number was invalid. */ irqsignal_status_t irqsignal_unregisterall(unsigned int irq) { irqsignal_status_t status; if (irq >= MAX_NUMBER_OF_IRQS) return IRQSIG_BADIRQ; write_lock(&irqdata_lock); // Make sure we do not modify the irqsignal_irqdata data structures // while the dispatcher routine is using them. // If the task list is empty then we do not own the IRQ and there is nothing to do. if (! list_empty( &irqsignal_irqdata[irq]) ) { status = irqsignal_removetaskfromlist(irq); irqsignal_printlist(irq); /* If the task list is empty, we must remove the IRQ handler assigned to the specified irq since no one wants any notifications. */ if ( list_empty( &irqsignal_irqdata[irq]) ) free_irq(pdev->irq, pdev); } else status = IRQSIG_NOT_REGISTERED; write_unlock(&irqdata_lock); return status; }; /* Unregisters all processes for the specified IRQ. This is a very forcefull way of doing things but sometimes must be done for debugging. Possible return values: IRQSIG_OK - Reset successfully. IRQSIG_BADIRQ - The IRQ number was invalid. */ irqsignal_status_t irqsignal_reset(unsigned int irq) { if (irq >= MAX_NUMBER_OF_IRQS) return IRQSIG_BADIRQ; write_lock(&irqdata_lock); // Make sure we do not modify the irqsignal_irqdata data structures // while the dispatcher routine is using them. // If the task list is empty then we do not own the IRQ and there is nothing to do. if (! list_empty( &irqsignal_irqdata[irq]) ) { irqsignal_clearlist(irq); irqsignal_printlist(irq); /* If the task list is empty, we must remove the IRQ handler assigned to the specified irq since no one wants any notifications. */ if ( list_empty( &irqsignal_irqdata[irq]) ) free_irq(pdev->irq, pdev); }; write_unlock(&irqdata_lock); return IRQSIG_OK; }; /* Unregisters all processes for all IRQ's. This is a very forcefull way of doing things but sometimes must be done for debugging. Only possible return value: IRQSIG_OK - Reset successfully. */ irqsignal_status_t irqsignal_resetall(void) { unsigned int irq; write_lock(&irqdata_lock); // Make sure we do not modify the irqsignal_irqdata data structures // while the dispatcher routine is using them. for (irq = 0; irq < MAX_NUMBER_OF_IRQS; irq++) { // If the task list is empty then we do not own the IRQ and there is nothing to do. if (! list_empty( &irqsignal_irqdata[irq]) ) { irqsignal_clearlist(irq); irqsignal_printlist(irq); /* If the task list is empty, we must remove the IRQ handler assigned to the specified irq since no one wants any notifications. */ if ( list_empty( &irqsignal_irqdata[irq]) ) free_irq(pdev->irq, pdev); }; }; write_unlock(&irqdata_lock); return IRQSIG_OK; }; /* The test routine simulates the interrupt handler. Note any IRQ number can be passed to this routine and it will simply pass it on to the dispatcher routine. So the dispatcher better handle this condition properly. Possible return values: IRQSIG_OK - Routine exited successfully. Does not indicate a succesfull test though. The dispatcher still needs to run properly. IRQSIG_BADIRQ - The IRQ number was invalid. IRQSIG_CANT_SCHEDULE_TASK - Something went wrong when trying to schedule the dispatcher task with "schedule_task" */ irqsignal_status_t irqsignal_test(unsigned int irq) { int result; printk(KERN_DEBUG "%s: Simulating IRQ %d.\n", THIS_MODULE->name, irq); irqsignal_dispatcher_task.data = (void*)irq; // Send the irq number as input data to the dispatcher. result = schedule_task(&irqsignal_dispatcher_task); printk(KERN_DEBUG "%s: Scheduled signal dispatcher, got return value of %d.\n", THIS_MODULE->name, result); if (result != 0) return IRQSIG_OK; else return IRQSIG_CANT_SCHEDULE_TASK; }; /* Used to recover from serious problems during debugging. */ void irqsignal_hardreset(void) { irqsignal_resetall(); while (MOD_IN_USE) MOD_DEC_USE_COUNT; MOD_INC_USE_COUNT; // leave the use count at 1 since at least the calling process // still has a file handle. }; /* This routine handles opening of the device file. */ int irqsignal_open(struct inode *inode, struct file *filp) { unsigned int minor = MINOR(inode->i_rdev); printk(KERN_INFO "%s: Open for minor device %d.\n", THIS_MODULE->name, minor); return 0; }; /* This routine handles closing of the device file. We cleanup after the current process if it didn't already do so with ioctl calls to unregister itself. */ int irqsignal_release(struct inode *inode, struct file *filp) { unsigned int irq; unsigned int minor = MINOR(inode->i_rdev); printk(KERN_INFO "%s: Release for minor device %d.\n", THIS_MODULE->name, minor); // We need to make sure we clean up after a process by removing it // from all the IRQ task lists and releaseing any IRQ handlers if necessary. for (irq = 0; irq < MAX_NUMBER_OF_IRQS; irq++) { // If the task list is empty then we do not own the IRQ and there is nothing to do. if (! list_empty( &irqsignal_irqdata[irq]) ) { irqsignal_removetaskfromlist(irq); irqsignal_printlist(irq); /* If the task list is empty, we must remove the IRQ handler assigned to the specified irq since no one wants any notifications. */ if ( list_empty( &irqsignal_irqdata[irq]) ) free_irq(pdev->irq, pdev); }; }; return 0; }; /* ioctl routine maps the ioctl numbers for this kernel module to the appropriate handling routines. */ int irqsignal_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { irqsignal_status_t status; unsigned int minor = MINOR(inode->i_rdev); switch (cmd) { case IRQSIGNAL_REGISTER: printk (KERN_INFO "%s: ioctl IRQSIGNAL_REGISTER for minor device %d.\n", THIS_MODULE->name, minor); status = irqsignal_register( (irqsignal_register_info_t*)arg ); break; case IRQSIGNAL_UNREGISTER: printk (KERN_INFO "%s: ioctl IRQSIGNAL_UNREGISTER for minor device %d.\n", THIS_MODULE->name, minor); status = irqsignal_unregister( (irqsignal_register_info_t*)arg ); break; case IRQSIGNAL_UNREGISTER_ALL: printk (KERN_INFO "%s: ioctl IRQSIGNAL_UNREGISTER_ALL for minor device %d.\n", THIS_MODULE->name, minor); status = irqsignal_unregisterall( (unsigned int)arg ); break; case IRQSIGNAL_RESETIRQ: printk (KERN_INFO "%s: ioctl IRQSIGNAL_RESETIRQ for minor device %d.\n", THIS_MODULE->name, minor); status = irqsignal_reset( (unsigned int)arg ); break; case IRQSIGNAL_RESETIRQALL: printk (KERN_INFO "%s: ioctl IRQSIGNAL_RESETIRQALL for minor device %d.\n", THIS_MODULE->name, minor); status = irqsignal_resetall(); break; case IRQSIGNAL_HARDRESET: printk (KERN_INFO "%s: ioctl IRQSIGNAL_HARDRESET for minor device %d.\n", THIS_MODULE->name, minor); irqsignal_hardreset(); status = IRQSIG_OK; break; case IRQSIGNAL_TEST: printk (KERN_INFO "%s: ioctl IRQSIGNAL_TEST for minor device %d.\n", THIS_MODULE->name, minor); status = irqsignal_test( (unsigned int)arg ); break; default: printk (KERN_ERR "%s: Bad ioctl for device major number %d, minor number %d.\n", THIS_MODULE->name, major, minor); status = IRQSIG_NO_SUCH_IOCTRL; }; return status; }; /* Map the file operations to our routines. */ struct file_operations irqsignal_fileops = { ioctl: irqsignal_ioctl, open: irqsignal_open, release: irqsignal_release, owner: THIS_MODULE, }; /* Module initialisation: Allocate resources and register as a character device driver. */ int irqsignal_init(void) { int result; printk (KERN_INFO "%s: Initialise.\n", THIS_MODULE->name); irqsignal_initialise_irqdata(); // Registering as acharacter device, just like PSI. // If the major number is zero then we will auto assign a major number. result = register_chrdev(major, "irqsignal", &irqsignal_fileops); if (result < 0) { printk(KERN_ERR "%s: Can't get major number.\n", THIS_MODULE->name); return result; } if (major == 0) major = result; /* dynamic */ return 0; } /* Module cleanup: Unregister and release all resources. */ void irqsignal_cleanup(void) { printk (KERN_INFO "%s: Cleanup.\n", THIS_MODULE->name); irqsignal_resetall(); // Hope everyone released their resources. unregister_chrdev(major, "irqsignal"); } module_init(irqsignal_init); module_exit(irqsignal_cleanup); -- Kernelnewbies: Help each other learn about the Linux kernel. Archive: http://mail.nl.linux.org/kernelnewbies/ FAQ: http://kernelnewbies.org/faq/