I have implemented a program that reads from the keyboad, and scans code and put it into the tasklet. The tasklet unblocks the read(). Thus, my QT-application can read the data and if it finds the scan code of l, it fires a callback to Qt-webkit. However, when I am doing rmmod of my character driver. The entire kernel crashes. What is the problem in my character driver.
#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);
What's the crash message? Perhaps my_tasklet is in the run Q while you terminate it. you can set up some printk debug msg in the exit routine.