Chinaunix首页 | 论坛 | 博客
  • 博客访问: 17762
  • 博文数量: 12
  • 博客积分: 1420
  • 博客等级: 上尉
  • 技术积分: 105
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-24 13:10
文章存档

2010年(10)

2009年(2)

我的朋友

分类:

2010-05-29 17:19:31

   自从接触嵌入式linux以来,经常到各大技术论坛逛,从各位大虾的文章中吸取到不少精华。以下贴出项目中的字符设备驱动的实例,它利用Linux内核提供的API函数(kfifo),创建环形队列,驱动中用到了状态机、信号量、等待队列,是学习字符设备驱动的典型例子。
 
/*
 * linux/drivers/char/keypad.c
 *
 */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

int keypad_major =   0;
int keypad_minor =   0;
#define KEYPAD_BUFFER_SIZE 32
static struct fasync_struct *fasync;
static struct timer_list timer;
static size_t key_push(const char *buf, size_t count);
 
struct key_pipe_t {
 struct semaphore sem;              /* mutual exclusion semaphore */
 struct cdev cdev;                  /* Char device structure  */
 wait_queue_head_t queue;           /* read and write queues */
 struct kfifo *keypad_fifo;
 spinlock_t lock;
};
 
static volatile void *gpio_base[9];
#define GPIO_DATA(n) (gpio_base[n] + 0x3fc)
#define GPIO_DIR(n) (gpio_base[n] + 0x400)
static struct key_pipe_t *key_pipe;
unsigned char *keypad_buffer;
static struct timer_list key_timer;

static unsigned char keypad_scan_keyval(void)
{
 unsigned char i,temp;
for (i=0;i<4;i++)
 {
  writel((~( 0x1 << i)), GPIO_DATA(6));//键盘线扫描
  temp = readl(GPIO_DATA(5));
  temp &=0xf;
 if(temp!=0xf)
 {
  temp&=0xf;
  switch(temp)     //计算键值
  {
    case 0x7:temp=16-(3-i)*4;break;
    case 0xd:temp=15-(3-i)*4;break;
    case 0xb:temp=14-(3-i)*4;break;
    case 0xe:temp=13-(3-i)*4;break;
    default:temp=0x7f;
}
return temp;
}
}
return 0x7f;
}
 
#define KPD_NO_KEY            0x7f
#define KPD_TWO_KEY           0xfe
enum kpdKeyMacheStateEn
{
    KPD_START,
    KPD_CHECK_PRESS,
    KPD_WAIT_RELEASE,
    KPD_CHECK_RELEASE
};

static unsigned char kpdKeyMachineState;
static unsigned int kpdPressCount;
static unsigned int kpdRepeatCount;
static unsigned char kpdCurrButtonCode;
static unsigned char kpdCurrScanCode;
static unsigned char kpdKeyCount;
 
static void kpdKeyMachineInit( void )
{
  unsigned int value;
    /* reset machine state */
    kpdKeyMachineState = KPD_START;
    kpdKeyCount = 0;
    value=readl(GPIO_DATA(6));
   value&=0xf0;
   writel(value, GPIO_DATA(6));
    return;
}

static void keypad_timer_handler(unsigned long data)
{
 unsigned char key;
 key = keypad_scan_keyval();
 switch( kpdKeyMachineState )
 {
 case KPD_START:
     if( key == KPD_NO_KEY )
     {
         kpdKeyMachineInit();
         break;
     }
     /* get current scan code and change state machine */
     kpdCurrScanCode = key;
     kpdKeyMachineState = KPD_CHECK_PRESS;
     break;
 
 case KPD_CHECK_PRESS:
     if( key == kpdCurrScanCode )
     {
         /* ok make sure the key is pressed, and change scan code to button code*/
         kpdCurrButtonCode = keypad_scan_keyval();
       key = kpdCurrButtonCode | 0x80;
      
         key_push((const char*)&key, 1);
//   printk("%d down\n", kpdCurrButtonCode);
       kpdKeyCount++;
         kpdKeyMachineState = KPD_WAIT_RELEASE;
     }
     else        /* key bounce,enable keypad interrupt */
     {
         kpdKeyMachineInit();
     }
     break;
 case KPD_WAIT_RELEASE:
     if( key == KPD_NO_KEY )
     {
         kpdKeyMachineState = KPD_CHECK_RELEASE;
     }
     else if( key == kpdCurrScanCode)
     {
         /* The key still pressed*/
         kpdPressCount++;
         /* if reached repeat count, produce a repeat key */
         if( kpdPressCount == kpdRepeatCount )
         {
             kpdRepeatCount += 4;
             if(kpdCurrButtonCode>=1 && kpdCurrButtonCode<=63)
             {
     key = kpdCurrButtonCode | 0xC0;
     key_push((const char*)&key, 1);
//     printk("%d repeat\n", kpdCurrButtonCode); 
    }
         }
     }
     else
     {
  kpdKeyMachineInit();
     }
     break;
 case KPD_CHECK_RELEASE:
     if( key != KPD_NO_KEY )
     {
         kpdKeyMachineState = KPD_WAIT_RELEASE;
     }
     else
     {
         kpdPressCount = 0;
         kpdRepeatCount = 15;
 
         kpdKeyMachineInit();
  key = kpdCurrButtonCode & ~0x80;
  key_push((const char*)&key, 1);
  kpdKeyCount--;
//  printk("%d up\n", kpdCurrButtonCode); 
     }
     break;
 
 default:
     break;
 }//switch( kpdKeyMachineState )
 key_timer.expires = jiffies + 10;
 add_timer(&key_timer);
}
 
 
static int keypad_timer_init(void)
{
 init_timer(&key_timer);
 key_timer.function = keypad_timer_handler;
 key_timer.expires = jiffies + 20;
 return 0; 
}
 
static void keypad_hardware_init(void)

 unsigned int value;
 value=readl(GPIO_DIR(5));
 value&=0xf0;
 writel(value, GPIO_DIR(5));
 value=readl(GPIO_DIR(6));
 value|=0x0f;
 writel(value, GPIO_DIR(6));
 value=readl(GPIO_DATA(6));
 value&=0xf0;
 writel(value, GPIO_DATA(6)); 
}
 
static void keypad_context_init(void)
{  
 init_MUTEX(&key_pipe->sem);
 spin_lock_init (&key_pipe->lock);
 key_pipe->keypad_fifo = kfifo_alloc(KEYPAD_BUFFER_SIZE, GFP_KERNEL, &key_pipe->lock);
 init_waitqueue_head(&key_pipe->queue);
 keypad_timer_init();
 kpdKeyMachineInit();
}
 
static size_t key_push(const char *buf, size_t count)
{
 ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
 if (down_interruptible(&key_pipe->sem))
  return -ERESTARTSYS;
 
 if ( count>KEYPAD_BUFFER_SIZE ) count = KEYPAD_BUFFER_SIZE;
 count = kfifo_put(key_pipe->keypad_fifo, buf, count);
 retval = count;
 up(&key_pipe->sem);
 /* finally, awake any reader */
 wake_up_interruptible(&key_pipe->queue);  /* blocked in read() and select() */

 return retval ;
}
 
static int keypad_open(struct inode *inode, struct file *filp)
{
 struct key_pipe_t *dev; /* device information */
 dev = container_of(inode->i_cdev, struct key_pipe_t, cdev);
 filp->private_data = dev; /* for other methods */
 
 //printk("%s: keypad_open\n", current->comm);
 key_timer.expires = jiffies + 20;
 add_timer(&key_timer);
 
 return nonseekable_open(inode, filp);          /* success */
}
 
 
static int keypad_release(struct inode *inode, struct file *filp)
{
 //printk("%s: keypad_release\n", current->comm);
 del_timer(&key_timer);
 return 0;
}

/*
 * Data management: read and write
 */
static ssize_t keypad_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
 struct key_pipe_t *dev = filp->private_data;
 ssize_t retval = 0;
 if (down_interruptible(&dev->sem))
  return -ERESTARTSYS;
 while (!kfifo_len(dev->keypad_fifo)) { /* nothing to read */
  up(&dev->sem); /* release the lock */
  if (filp->f_flags & O_NONBLOCK)
   return -EAGAIN;
  printk("\"%s\" reading: going to sleep\n", current->comm);
  if (wait_event_interruptible(dev->queue, kfifo_len(dev->keypad_fifo)))
   return -ERESTARTSYS; /* signal: tell the fs layer to handle it */
  /* otherwise loop, but first reacquire the lock */
  if (down_interruptible(&dev->sem))
   return -ERESTARTSYS;
 }
 if (count > kfifo_len(dev->keypad_fifo))
  count = kfifo_len(dev->keypad_fifo);
 count = kfifo_get(dev->keypad_fifo, keypad_buffer, count);
 if (copy_to_user(buf, keypad_buffer, count)) {
  retval = -EFAULT;
  goto out;
 }
 retval = count;
  out:
 up(&dev->sem);

 return retval;
}

static unsigned int keypad_poll(struct file *filp, poll_table *wait)
{
 struct key_pipe_t *dev = filp->private_data;
 unsigned int mask=0;
 poll_wait(filp, &dev->queue, wait);

 if(kfifo_len(dev->keypad_fifo))
  mask |= POLLIN | POLLRDNORM; /* readable */
 return mask;
}
 
/*
 * The file operations for the keypad device
 */
struct file_operations keypad_dev_fops = {
 .owner = THIS_MODULE,
 .llseek = no_llseek,
 .read =  keypad_read,
 .poll =  keypad_poll,
 .open =  keypad_open,
 .release = keypad_release,
};
 
/*
 * Set up the char_dev structure for this device.
 */
static void keypad_setup_cdev(struct key_pipe_t *dev)
{
 int err, devno = MKDEV(keypad_major, keypad_minor );
   
 cdev_init(&dev->cdev, &keypad_dev_fops);
 dev->cdev.owner = THIS_MODULE;
 err = cdev_add (&dev->cdev, devno, 1);
 /* Fail gracefully if need be */
 if (err)
  printk(KERN_NOTICE "Error %d adding keypad", err);
}
 
int __init keypad_init(void)
{
 int result;
 dev_t dev = 0;
/*
 * Get a range of minor numbers to work with, asking for a dynamic
 * major unless directed otherwise at load time.
 */
 if (keypad_major) {
  dev = MKDEV(keypad_major, keypad_minor);
  result = register_chrdev_region(dev, 1, "keypad");
 } else {
  result = alloc_chrdev_region(&dev, keypad_minor, 1,
    "keypad");
  keypad_major = MAJOR(dev);
 }
 if (result < 0) {
  printk(KERN_WARNING "keypad: can't get major %d\n", keypad_major);
  return result;
 }
   /*
  * allocate the devices -- we can't have them static, as the number
  * can be specified at load time
  */
  gpio_base[5] = ioremap(0x2002c000, SZ_4K);
 if(gpio_base[5]==NULL){
  printk("<1> remap 2002c000 error\n");
  return -ENODEV;
 }
 gpio_base[6] = ioremap(0x2002d000, SZ_4K);
 if(gpio_base[6]==NULL){
  printk("<1> remap 2002d000 error\n");
  iounmap(gpio_base[5]);
  return -ENODEV;
 }
 
 key_pipe = kmalloc(sizeof(struct key_pipe_t), GFP_KERNEL);
 if (!key_pipe) {
  result = -ENOMEM;
  goto fail;  /* Make this more graceful */
 }
 memset(key_pipe, 0, sizeof(struct key_pipe_t));
 
 keypad_buffer = kmalloc(KEYPAD_BUFFER_SIZE, GFP_KERNEL);
 if (!keypad_buffer) {
  result = -ENOMEM;
  goto fail;  /* Make this more graceful */
 }
 
 keypad_hardware_init();
 keypad_context_init();
 keypad_setup_cdev(key_pipe);
 
 return 0;
fail:
 kfree(key_pipe);
 if (keypad_buffer) kfree(keypad_buffer);
 return result;
}
 
void __exit keypad_cleanup(void)
{
 dev_t devno = MKDEV(keypad_major, keypad_minor);
 /* Get rid of our char dev entries */
 if (key_pipe) {
  if (key_pipe->keypad_fifo)  { 
   kfifo_free(key_pipe->keypad_fifo);
  }
  cdev_del(&key_pipe->cdev);
  kfree(key_pipe);
 }
 /* cleanup_module is never called if registering failed */
 del_timer(&timer);
 if (keypad_buffer) kfree(keypad_buffer);
 iounmap(gpio_base[5]);
 iounmap(gpio_base[6]); 
 unregister_chrdev_region(devno, 1);
}

module_init(keypad_init);
module_exit(keypad_cleanup);
MODULE_AUTHOR("Li Huiwu <>");
MODULE_DESCRIPTION("KeyPad Driver for SAT");
MODULE_LICENSE("GPL");
阅读(837) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~