如何在没有内核崩溃的情况下优雅地禁用中断线?

时间:2014-04-17 13:06:04

标签: c kernel linux-device-driver kernel-module

我已经实现了一个从keyboad读取的程序,并扫描代码并将其放入tasklet中。 tasklet取消阻塞read()。因此,我的QT应用程序可以读取数据,如果它找到l的扫描代码,它会触发对Qt-webkit的回调。但是,当我在做我的角色驱动程序的rmmod时。整个内核崩溃了。我的角色驱动程序有什么问题。

#include <linux/init.h>
#include <linux/module.h> /** needed by all modules **/
#include <linux/kernel.h>  /** This is for KERN_ALERT **/
#include <linux/fs.h> /** for file operations **/
#include <linux/cdev.h>  /** character device **/
#include <linux/device.h>  /** for sys device registration in /dev/ and /sys/class **/
/** for copy_to_user **/
#include <asm/uaccess.h>
/** kernel thread */
#include <linux/kthread.h>
/** spin lock essential here **/
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/io.h>

/** For class registration to work, you need GPL license **/
MODULE_LICENSE("GPL");

#define NUMBER_OF_MINOR_DEVICE (0)

/** to avoid namespace pollution, make everything static **/

static struct cdev basicCdev;
static struct class *basicDriverClass;
/** Created thread **/
static struct task_struct *basicCharThread = NULL;
static wait_queue_head_t chrDriverQueue;
static spinlock_t mLock;
static int read_unblock_flag = 0;
static int  basicMajorNumber = 0;

/** Just for debugging, whether bottom half (tasklet) is executed or not **/
static char my_tasklet_data[]="my_tasklet_function was called";

/** scan code that will be return by read **/
static unsigned char scancode;
static unsigned char status;

static void my_tasklet_function( unsigned long data );

DECLARE_TASKLET( my_tasklet, my_tasklet_function,(unsigned long) &my_tasklet_data );

/** Prototype for read, this will be invoked when the read function is done on to the driver **/
/** The declaration type is file operations based function pointer - read **/

static ssize_t basicRead(struct file *filp, char *buffer, size_t length,loff_t *offset);

static int basicOspen(struct inode *inode, struct file *file);

/** File Operations function pointer table **/
/** There are plenty of file operations  **/

static struct file_operations fops =
{
    .read = basicRead,
    .write = NULL,
    .open = basicOspen,
    .release = NULL
};

static ssize_t basicRead(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
    unsigned long flags;
    char msg[] = "Hello SJ_read\0";

    /** lock it **/
    spin_lock_irqsave( &mLock, flags );

    printk(KERN_ALERT "The Read operation called\r\n");

    spin_unlock_irqrestore( &mLock, flags );

    wait_event_interruptible(chrDriverQueue, read_unblock_flag != 0);

    /** set it back to original for sleep **/
    read_unblock_flag = 0;

    printk(KERN_ALERT "Read out of sleep\r\n");

    if( signal_pending( current ) )
    {
        printk(KERN_ALERT "Signal pending error\r\n");
        return -1;
    }

    spin_lock_irqsave( &mLock, flags );

    if ( 0 != copy_to_user( buffer, &scancode, sizeof(scancode) ))
    {
        printk(KERN_ALERT "Copy_to_user failed !!\r\n");
    }

    spin_unlock_irqrestore( &mLock, flags );

    return sizeof(msg);
}

static int basicOspen(struct inode *inode, struct file *file)
{

    printk("Kernel.Basic Driver Opened now!!\r\n");

    return 0;
}

static void setup_cdev(struct cdev *dev, int minor, struct file_operations *fops)
{
    int err = -1;
    /** MKDEV call creates a device number i.e. combination of major and minor number **/
    int devno = MKDEV(basicMajorNumber, minor);
    /** Initiliaze character dev with fops **/
    cdev_init(dev, fops);
    /**owner and operations initialized **/
    dev->owner = THIS_MODULE;
    dev->ops = fops;
    /** add the character device to the system**/
    /** Here 1 means only 1 minor number, you can give 2 for 2 minor device, the last param is the count of minor number enrolled **/
    err = cdev_add (dev, devno, 1);

    if (err)
    {
        printk (KERN_NOTICE "Couldn't add cdev");
    }
}


/** Un-used Kernel Thread here **/
static int basicCharThread_fn (void *data)
{
    unsigned long j0,j1;
    int delay = 30*HZ;
    unsigned long flags;

    j0 = jiffies;
    j1 = j0 + delay;

    /** this loop will run till the jiffies is equal to the (old jiffies + 60Hz delay) , this is a 1 minute delay in total **/
    while (time_before(jiffies, j1))
    {
        //printk("The thread is started\r\n");
        schedule();
    }

    spin_lock_irqsave( &mLock, flags );
    read_unblock_flag = 1;
    spin_unlock_irqrestore( &mLock, flags );
    wake_up_interruptible( &chrDriverQueue );
    printk("The process is moved out of wait queue now\r\n");
    return 0;
}


/* Bottom Half Function - Tasklet */
void my_tasklet_function( unsigned long data )
{
    unsigned long flags;
    printk( "%s\n", (char *)data );

    /** Scan Code 1c Pressed **/
    printk(KERN_INFO "Scan Code %x %s.\n",
           scancode& 0x7F,
           scancode & 0x80 ? "Released" : "Pressed");

    spin_lock_irqsave( &mLock, flags );
    read_unblock_flag = 1;
    spin_unlock_irqrestore( &mLock, flags );
    wake_up_interruptible( &chrDriverQueue );
    return;
}


/*** Interrupt Handler ***/
irqreturn_t irq_handler(int irq, void *dev_id, struct pt_regs *regs)
{
    /*
    * Read keyboard status
    */
    status = inb(0x64);
    scancode = inb(0x60);
    /** BH scheduled **/
    tasklet_schedule( &my_tasklet );

    return IRQ_HANDLED;
}

/** character Driver load - Main Entry Function - Module_Init() - called on insmod **/
static int chrDriverInit(void)
{

    int result = -1;
    dev_t dev;

    /** Initialize the kernel thread **/
    char  kthreadBasic[]="kthreadBasic";
    printk("Welcome!! Device Init now..");

    /** int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);  **/
    /** dev -> The dev_t variable type,which will get the major number that the kernel allocates.  **/
    /**The same name will appear in /proc/devices.  **/

    /** it is registering the character device **/
    /** a major number will be dynamically allocated here **/
    /**  alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); **/
    result = alloc_chrdev_region(&dev, 0, NUMBER_OF_MINOR_DEVICE, "pSeudoDrv");

    if( result < 0 )
    {
        printk("Error in allocating device");
        return -1;
    }

    /** From these two if's we are avoiding the manual mknod command to create the /dev/<driver> **/
    /**  creating class, and then device created removes the dependency of calling mknod  **/
    /** A good method - the mknod way is depreciated **/
    /** mknod way is -  mknod /dev/<driver_name> c <majorNumber> <minorNumber> */


    /** add the driver to /-sys/class/chardrv */
    if ((basicDriverClass = class_create(THIS_MODULE, "chardrv")) == NULL)    //$ls /sys/class
    {
        unregister_chrdev_region(dev, 1);
        return -1;
    }

    /** add the driver to /dev/pSeudoDrv -- here **/
    if (device_create(basicDriverClass, NULL, dev, NULL, "pSeudoDrv") == NULL) //$ls /dev/
    {
        class_destroy(basicDriverClass);
        unregister_chrdev_region(dev, 1);
        return -1;
    }

    /** let's see what major number was assigned by the Kernel **/
    basicMajorNumber = MAJOR(dev);
    printk("Kernel assigned major number is %d ..\r\n",basicMajorNumber );

    /** Now setup the cdev **/
    setup_cdev(&basicCdev,NUMBER_OF_MINOR_DEVICE, &fops);

    /** Linux thread is just another process, and is treated like that only **/
    /** string that stores the name - kthreadBasic**/
    /** struct task_struct *kthread_create(int (*function)(void *data),void *data const char name[], ...)**/
    basicCharThread = kthread_create(basicCharThread_fn,(void *)NULL,kthreadBasic);
    /** To run the process, we need to call wake_up_process by passing the thread-id, obtained by kthread_create **/
    /** To stop the thread, call kthread_stop pass the thread-id **/
    if( basicCharThread == NULL)
    {
        printk( KERN_ALERT "Thread creation failed\r\n");
        return -1;
    }

    /** inialize spin lock **/
    spin_lock_init( &mLock);
    /** Initialize wait queue **/
    init_waitqueue_head(&chrDriverQueue);

    /** Start executing the thread now **/
    /** Disabled thread here- not in use **/
    //wake_up_process(basicCharThread);
    /** Free IRQ - if already registered **/
    free_irq(1, NULL);
    /** register the keyboard IRQ on shared mode,note this is specific to X86 architecture **/
    return request_irq(1,   /* The number of the keyboard IRQ on PCs */
                       (irqreturn_t *)irq_handler,  /* our handler */
                       IRQF_SHARED, "test_keyboard_irq_handler",
                       (void *)(irq_handler));

    return 0;

}

/** Driver Exit - Module_Exit() .. called on rmmod **/

static void chrDriverExit(void)
{
    //free_irq(1, NULL);
    /** stop the running thread **/
    tasklet_kill( &my_tasklet );
    //kthread_stop(basicCharThread);
    /** A reverse - destroy mechansim -- the way it was created **/
    printk("Releasing Simple Devs -- %s\r\n",  __FUNCTION__);
    /** delete the character driver added **/
    cdev_del(&basicCdev);
    /** destroy the device created **/
    device_destroy(basicDriverClass, MKDEV(basicMajorNumber, 0));
    /** destroy the class created **/
    class_destroy(basicDriverClass);
    /** unregister the chr dev **/
    unregister_chrdev(basicMajorNumber, NUMBER_OF_MINOR_DEVICE);

}

/** Module Entry and Exit ***/
module_init(chrDriverInit);
module_exit(chrDriverExit);

1 个答案:

答案 0 :(得分:1)

崩溃讯息是什么?当你终止它时,my_tasklet可能在运行Q中。您可以在退出例程中设置一些printk调试消息。