Need help with writing PCI driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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/


[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux