Linux下中断驱动中最典型的就是键盘驱动了,在初始化的时候调用request_irq注册中断,在退出的时候free_irq释放中断。下面是示例:

keyboardInt.h

 

#ifndef _KEYBOARD_STATS_
#define _KEYBOARD_STATS_

#ifndef KYBRD_MAJOR
#define K_MAJOR 0		/* dynamic major number */
#endif

#ifndef KYBRD_STR_SIZE
#define K_STR_SIZE 1024*4*4     /* data size that is to be printed out */
#endif

/* our device structure */
struct keyboard_stats_dev {
  char *stat_str;		/* string that would be shown to user */
  struct semaphore sem;		/* mutual exclusion semaphore  */
  struct cdev cdev;		/* Char device structure       */
};

/* scan code */
static unsigned char scancode;

#endif	/* _KEYBOARD_STATS_ */


keyboard_mapping.h

 

 

 

 

// Please DO-NOT edit this file manually (a hardware specific file).
// This is generated by the Makefile.
// Read License Agreement before using.
// Author: Vigith Maurice <v@vigith.com>


struct keyboard_stat {
  char str[10];
  char hex[4];
  int dec;
  long int count_p;
  long int count_r;
} keyboard_stats[] = {
  { "DUMMY", "0", 0, 0, 0 }, 
  { "Escape", "1", 1, 0, 0 },
  { "one", "2", 2, 0, 0 },
  { "two", "3", 3, 0, 0 },
  { "three", "4", 4, 0, 0 },
  { "four", "5", 5, 0, 0 },
  { "five", "6", 6, 0, 0 },
  { "six", "7", 7, 0, 0 },
  { "seven", "8", 8, 0, 0 },
  { "eight", "9", 9, 0, 0 },
  { "nine", "a", 10, 0, 0 },
  { "zero", "b", 11, 0, 0 },
  { "minus", "c", 12, 0, 0 },
  { "equal", "d", 13, 0, 0 },
  { "Delete", "e", 14, 0, 0 },
  { "Tab", "f", 15, 0, 0 },
  { "+q", "10", 16, 0, 0 },
  { "+w", "11", 17, 0, 0 },
  { "+e", "12", 18, 0, 0 },
  { "+r", "13", 19, 0, 0 },
  { "+t", "14", 20, 0, 0 },
  { "+y", "15", 21, 0, 0 },
  { "+u", "16", 22, 0, 0 },
  { "+i", "17", 23, 0, 0 },
  { "+o", "18", 24, 0, 0 },
  { "+p", "19", 25, 0, 0 },
  { "bracketlef", "1a", 26, 0, 0 },
  { "bracketrig", "1b", 27, 0, 0 },
  { "Return", "1c", 28, 0, 0 },
  { "Control", "1d", 29, 0, 0 },
  { "+a", "1e", 30, 0, 0 },
  { "+s", "1f", 31, 0, 0 },
  { "+d", "20", 32, 0, 0 },
  { "+f", "21", 33, 0, 0 },
  { "+g", "22", 34, 0, 0 },
  { "+h", "23", 35, 0, 0 },
  { "+j", "24", 36, 0, 0 },
  { "+k", "25", 37, 0, 0 },
  { "+l", "26", 38, 0, 0 },
  { "semicolon", "27", 39, 0, 0 },
  { "apostrophe", "28", 40, 0, 0 },
  { "grave", "29", 41, 0, 0 },
  { "Shift", "2a", 42, 0, 0 },
  { "backslash", "2b", 43, 0, 0 },
  { "+z", "2c", 44, 0, 0 },
  { "+x", "2d", 45, 0, 0 },
  { "+c", "2e", 46, 0, 0 },
  { "+v", "2f", 47, 0, 0 },
  { "+b", "30", 48, 0, 0 },
  { "+n", "31", 49, 0, 0 },
  { "+m", "32", 50, 0, 0 },
  { "comma", "33", 51, 0, 0 },
  { "period", "34", 52, 0, 0 },
  { "slash", "35", 53, 0, 0 },
  { "Shift", "36", 54, 0, 0 },
  { "KP_Multipl", "37", 55, 0, 0 },
  { "Alt", "38", 56, 0, 0 },
  { "space", "39", 57, 0, 0 },
  { "CtrlL_Lock", "3a", 58, 0, 0 },
  { "F1", "3b", 59, 0, 0 },
  { "F2", "3c", 60, 0, 0 },
  { "F3", "3d", 61, 0, 0 },
  { "F4", "3e", 62, 0, 0 },
  { "F5", "3f", 63, 0, 0 },
  { "F6", "40", 64, 0, 0 },
  { "F7", "41", 65, 0, 0 },
  { "F8", "42", 66, 0, 0 },
  { "F9", "43", 67, 0, 0 },
  { "F10", "44", 68, 0, 0 },
  { "Num_Lock", "45", 69, 0, 0 },
  { "Scroll_Loc", "46", 70, 0, 0 },
  { "KP_7", "47", 71, 0, 0 },
  { "KP_8", "48", 72, 0, 0 },
  { "KP_9", "49", 73, 0, 0 },
  { "KP_Subtrac", "4a", 74, 0, 0 },
  { "KP_4", "4b", 75, 0, 0 },
  { "KP_5", "4c", 76, 0, 0 },
  { "KP_6", "4d", 77, 0, 0 },
  { "KP_Add", "4e", 78, 0, 0 },
  { "KP_1", "4f", 79, 0, 0 },
  { "KP_2", "50", 80, 0, 0 },
  { "KP_3", "51", 81, 0, 0 },
  { "KP_0", "52", 82, 0, 0 },
  { "KP_Period", "53", 83, 0, 0 },
  { "Last_Conso", "54", 84, 0, 0 },
  { "less", "56", 86, 0, 0 },
  { "F11", "57", 87, 0, 0 },
  { "F12", "58", 88, 0, 0 },
  { "KP_Enter", "60", 96, 0, 0 },
  { "Control", "61", 97, 0, 0 },
  { "KP_Divide", "62", 98, 0, 0 },
  { "Alt", "64", 100, 0, 0 },
  { "Break", "65", 101, 0, 0 },
  { "Find", "66", 102, 0, 0 },
  { "Up", "67", 103, 0, 0 },
  { "Prior", "68", 104, 0, 0 },
  { "Left", "69", 105, 0, 0 },
  { "Right", "6a", 106, 0, 0 },
  { "Select", "6b", 107, 0, 0 },
  { "Down", "6c", 108, 0, 0 },
  { "Next", "6d", 109, 0, 0 },
  { "Insert", "6e", 110, 0, 0 },
  { "Remove", "6f", 111, 0, 0 },
  { "Macro", "70", 112, 0, 0 },
  { "F13", "71", 113, 0, 0 },
  { "F14", "72", 114, 0, 0 },
  { "Help", "73", 115, 0, 0 },
  { "Do", "74", 116, 0, 0 },
  { "F17", "75", 117, 0, 0 },
  { "KP_MinPlus", "76", 118, 0, 0 },
  { "Pause", "77", 119, 0, 0 },
  { "KP_Period", "79", 121, 0, 0 },
  { "Alt", "7d", 125, 0, 0 },
  { "Alt", "7e", 126, 0, 0 },
};

int SCAN_CODES = 114;

keyboardInt.c

 

 

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>		/* kmalloc() */
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/string.h>
#include <linux/fs.h>		/* everything... */
#include <linux/cdev.h>		/* character device */

#include <linux/string.h>		/* strcat */
#include <asm/system.h>		/* cli(), *_flags */
#include <asm/uaccess.h>        /* copy_*_user */

#include "keyboardInt.h"
#include "keyboard_mapping.h"

int major = K_MAJOR;
int minor = 0;
struct keyboard_stats_dev *k_dev;

/* Spin lock */
DEFINE_SPINLOCK(mr_lock);

/* Tasklets for BH */
void keyboard_tasklet_bh(unsigned long);
/* PROTO: DECLARE_TASKLET(name, function, data); */
DECLARE_TASKLET(keyboard_tasklet, keyboard_tasklet_bh, 0);

void keyboard_tasklet_bh(unsigned long hits) {
  int binary;
  unsigned char scode;		/* tmp scancode */
  char *bin, *tmp;

  /* GFP_ATOMIC is compulsory as this won't sleep */
  bin= kmalloc(sizeof(unsigned char) * 1, GFP_ATOMIC); /* pid is read a a string */
  tmp = kmalloc(sizeof(unsigned char) * 1, GFP_ATOMIC); /* needed for simple_strtol */

  /* save the scan code */
  spin_lock(&mr_lock);
  scode = scancode;
  spin_unlock(&mr_lock);

  /* since no TASKLETs run together, i don't have to worry about locks here */
  sprintf(bin, "%d", scancode);
  binary = simple_strtol(bin, &tmp, 10);
  
  /* save the key press/release stat */
  if (scancode & 0x80) {
    binary -= 128;		/* key release - 128 is key press */
    keyboard_stats[binary].count_r += 1;
  } else {
    keyboard_stats[binary].count_p += 1;
  }

  /* Please don't UNCOMMENT this block, in interrupt context printk can be blocked and put a system to a serious unstable state.   
     ('coz, interrupt state can't wake up becuase it has no process context and need_schedule() func won't be called (long story, read LINUX DEVICE DRIVERS 3rd ed).
     I tested in a VM box, where there are no other activities were taking place and rebooting was not at-all a problem :-)
  */
    printk(KERN_INFO "You pressed [%x] [%d] [%s] (%lu %lu)\n", scancode, scancode, scancode & 0x80 ? "Released" : "Pressed", keyboard_stats[binary].count_r, keyboard_stats[binary].count_p);
  

  return;
}

/* This function services keyboard interrupts */
irq_handler_t irq_handler (int irq, void *dev_id, struct pt_regs *regs) {

  /*
    Read keyboard status.
    Obtain a spin lock and update scancode.
  */
  spin_lock(&mr_lock);
  scancode = inb (0x60);
  spin_unlock(&mr_lock);

  /* schedule the tasklet */
  tasklet_schedule(&keyboard_tasklet);

  return (irq_handler_t) IRQ_HANDLED;
}

/*
 * 'open' system call
 */
int k_dev_open(struct inode *inode, struct file *filp) {
  struct keyboard_stats_dev *dev;		/* our device (cdev), which contains 'cdev' */

  /* takes a pointer to a field of type container_field, within a  structure of 
     type container_type, and returns a pointer to the containing structure.
     This is for getting the parent keyboard_stats_dev struct from cdev which is
     encapsulated inside keyboard_stats_dev. (we don't need dev, but incase for future! */
  dev = container_of(inode->i_cdev, struct keyboard_stats_dev, cdev);
  /* for easier access, else we will have to call container_of everytime */
  filp->private_data = dev;

  /* we are planning only for O_RDONLY, so no special handler for O_WRONLY
     what will you write to the stats file? :-) */

  return 0;
}

/* 
 * Read from the device (write to userspace), 'read' syscall
 */
ssize_t k_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) {
  struct keyboard_stats_dev *dev = filp->private_data; /* we had stored 'dev' in private_data, else use container_of */
  ssize_t retval = 0;
  char tmp[256];		/* tmp space for sprintf to put the keyboard stats */
  int i = 0;
  char sep = '\t';
  
  /* acquire lock for concurrency */
  if(down_interruptible(&dev->sem))
    return -ERESTARTSYS;

  /* We expect all calls to be full requests, not partial ones (ie, not using offsets), yeah lazy Maurice, bad hack :-) */
  if (*f_pos != 0) {
    goto out;
  }

  dev->stat_str[0] = '\0';

  /* Populate the stats string! */
  sprintf(tmp, "\t\tKey Name (Release Count\tPress Count)\n");  
  strcat(dev->stat_str, tmp);
  for (i=1; i<=SCAN_CODES;) {
    sprintf(tmp, "%10s (%lu %lu)%c", keyboard_stats[i].str, keyboard_stats[i].count_r, keyboard_stats[i].count_p, sep);  
    strcat(dev->stat_str, tmp);
    if (++i % 3 == 0) {
      sep = '\n';
      continue;
    }
    sep = '\t';
  }

  /* write pd_str to userspace */
  if (copy_to_user(buf, dev->stat_str, strlen(dev->stat_str))) {
    retval = -EFAULT;
    goto out;
  }

  *f_pos = strlen(dev->stat_str);
  retval = strlen(dev->stat_str);

 out:
  up(&dev->sem);
  return retval;
}


/*
 * 'close' system call
 */
int k_dev_release(struct inode *inode, struct file *filp) {
  struct keyboard_stats_dev *dev;		/* our device (cdev), which contains 'cdev' */

  /* get the device */
  dev = container_of(inode->i_cdev, struct keyboard_stats_dev, cdev);

  return 0;
}


/* 
 * setting up the file operations for k_dev
 */
struct file_operations k_fops = {
  .owner = THIS_MODULE,
  .open  = k_dev_open,
  .read  = k_dev_read,
  .release = k_dev_release,
};


/* 
 * Device Setup for Keyboard Stats Device
 */
int k_dev_setup(struct keyboard_stats_dev *dev) {
  int err;
  dev_t devno = MKDEV(major, minor);
  
  /* device registration, could have done static registration too! */
  cdev_init(&dev->cdev, &k_fops);
  dev->cdev.ops = &k_fops;
  dev->cdev.owner = THIS_MODULE;

  /* tells the kernel about the registration */
  err = cdev_add(&dev->cdev, devno, 1);

  if (err) {
    printk(KERN_WARNING "Error during setting up 'keyboard_stats'\n");
    return err;
  }

  /* k_dev string space */
  dev->stat_str = kmalloc(sizeof(char) * K_STR_SIZE, GFP_KERNEL);
  memset(dev->stat_str, 0, sizeof(char) * K_STR_SIZE);

  return 0;
}


/* Initialize the module and Register the IRQ handler */
static int __init keybrd_int_register(void) {
  int result = -1;
  dev_t dev  = 0;
  
  /* register the character device 
  (dev_t *dev, firstminor, count, name); 
  */
  result = alloc_chrdev_region(&dev, minor, 1, "keyboard_stats");
  if (result < 0) {
    major = MAJOR(dev);
    printk(KERN_WARNING "keyboard_stats: can't get major %d\n", major);
    return result;
  }

  major = MAJOR(dev);

  /* allocate the devices */
  /* GFP_KERNEL is the flag to use in process context code when it is safe to sleep. The 
     kernel will do whatever it has to do to obtain the memory requested by the caller. */     
  k_dev = kmalloc(1 * sizeof(struct keyboard_stats_dev), GFP_KERNEL);


  /* setup each device */
  sema_init(&(k_dev->sem),1);	/* addr of sem */
  result = k_dev_setup(k_dev); /* TODO: catch error and do good cleanup */
  if (result)
    return result;

  printk(KERN_INFO "Inserted Module 'keyboard_stats' [%d]\n", major);

  /* Request IRQ 1, the keyboard IRQ */    
  result = request_irq (1, (irq_handler_t) irq_handler, IRQF_SHARED, "keyboard_stats", (void *)(irq_handler));
  if (result)
    printk(KERN_INFO "can't get shared interrupt for keyboard\n");
  
  return result;
}

/* Remove the interrupt handler and the device file */
static void __exit keybrd_int_unregister(void) {
  dev_t devno = MKDEV(major, minor);

  free_irq(1, (void *)(irq_handler)); /* i can't pass NULL, this is a shared interrupt handler! */

  /* free the memory we acquired */
  /* clean the keyboard_stats_dev string space */
    kfree(k_dev->stat_str);
    cdev_del(&(k_dev->cdev));

  /* clean the device sctructure */
  kfree(k_dev);

  unregister_chrdev_region(devno, 1);
  printk(KERN_INFO "Removed Module 'keyboard_stats' [%d]\n", MAJOR(devno));
}

MODULE_LICENSE ("GPL");
MODULE_AUTHOR("Vigith Maurice");
module_init(keybrd_int_register);
module_exit(keybrd_int_unregister);



keyboard_stats_load

 

 

#!/bin/bash -x

module="keyboardInt"
device="keyboard_stats"
mode="664"


# Group: since distributions do it differently, look for wheel or use staff
if grep -q '^staff:' /etc/group; then
    group="staff"
else
    group="wheel"
fi


# invoke insmod with all arguments we got
# and use a pathname, as insmod doesn't look in . by default
/sbin/insmod ./$module.ko $* || exit 1


# retrieve major number
major=$(awk "\$2==\"$device\" {print \$1}" /proc/devices)


# Remove stale nodes and replace them, then give gid and perms
rm -f /dev/${device}
mknod /dev/${device} c $major 0	# create device file
chgrp $group /dev/${device}
chmod $mode  /dev/${device}



 

 

 

 

 

keyboard_stats_unload

 

#!/bin/bash -x

module="keyboardInt"
device="keyboard_stats"


# invoke rmmod with all arguments we got
/sbin/rmmod $module $* || exit 1


# Remove stale nodes
rm -f /dev/${device}

 


使用说明如下:

 

 

 

##########
# README #
##########

Purpose
-------
To help understand which is the most commonly 'pressed' and 'released' key in 
the keyboard. Holding a key down will generate 'pressed' events more so, count
of pressing need not be equal to release count. (This code works only for ps/2 
keyboards, not usb which generates interrupt in different IRQ line.)

How To Use
----------
Please don't proceed if you see any error in the below steps.

0. 0rth Law (as in thermodynamics)
cat LICENSE

1. compile 
a. create the header files
$ make header

(
sometimes the above may not work, as it won't be able to get 
file descriptor. please run as sudo 
$ sudo make header
)

b. compile the source
$ make

2. Insert the module into kernel and setup the system
$ keyboard_stats_load

3. Read the statistics
$ cat /dev/keyboard_stats

4. Remove the module and system
$ keyboard_stats_unload

5. Cleaning the dir
$ make clean

DEBUGGING
---------
Mail the author or read /var/log/messages and try your luck 
by setting the scancodes etc.

Misc
----
I wrote this code because few of my keys are more shiny than others
and i had to find out why!! :-)

Bugs
----
In my MAC VM, some keys were not working, like arrow keys!

Author
------
Vigith Maurice (www.vigith.com)
<vigith@gmail.com> <v@vigith.com>


按照上面的使用方法,首先能够看到Tab值为0

 

 

按键后Tab变成3了

 

Logo

更多推荐