Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1223081
  • 博文数量: 322
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 3276
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-17 09:21
文章分类

全部博文(322)

文章存档

2010年(155)

2009年(167)

我的朋友

分类: 嵌入式

2010-04-05 10:52:41

#include
#include
#include

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include
#include

#define MAX_KEY_BUF 16

#define UP 0
#define DOWN 1
#define NAME "s3c2410-key"

static int key_major;
dev_t devno;

typedef struct {
        unsigned char pressure;
        unsigned char number;
} KEY_RET;

typedef struct
{
        KEY_RET buf[MAX_KEY_BUF];
        int head,tail;        
        wait_queue_head_t wq;
        spinlock_t lock;
        struct cdev cdev;
} KEY_DEV;
KEY_DEV s3c2410_key_dev;

#define BUF_HEAD                (s3c2410_key_dev.buf[s3c2410_key_dev.head])
#define BUF_TAIL                (s3c2410_key_dev.buf[s3c2410_key_dev.tail])
#define DELAY                 (HZ/100) /* 10 ms */
#define INCBUF(x,mod)         ((++(x)) & ((mod) - 1))

static struct timer_list key_timer[4];

static struct key_info
{
        int irq_no;
        int key_no;
} key_info_tab[16] =
{
  {IRQ_EINT19,1},{IRQ_EINT19,2},{IRQ_EINT19,3},{IRQ_EINT19,13},
  {IRQ_EINT11,4},{IRQ_EINT11,5},{IRQ_EINT11,6},{IRQ_EINT11,14},
  {IRQ_EINT2,7}, {IRQ_EINT2,8}, {IRQ_EINT2,9}, {IRQ_EINT2,15},
  {IRQ_EINT0,10},{IRQ_EINT0,11},{IRQ_EINT0,12},{IRQ_EINT0,16},
};

static void gpio_set(void)
{
        s3c2410_gpio_cfgpin(S3C2410_GPE11,S3C2410_GPE11_OUTP);s3c2410_gpio_cfgpin(S3C2410_GPE13,S3C2410_GPE13_OUTP);
        s3c2410_gpio_cfgpin(S3C2410_GPG2,S3C2410_GPG2_OUTP);s3c2410_gpio_cfgpin(S3C2410_GPG6,S3C2410_GPG6_OUTP);

        s3c2410_gpio_setpin(S3C2410_GPE11,0);s3c2410_gpio_setpin(S3C2410_GPE13,0);
        s3c2410_gpio_setpin(S3C2410_GPG2,0);s3c2410_gpio_setpin(S3C2410_GPG6,0);

        s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_EINT0);s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_EINT2);
        s3c2410_gpio_cfgpin(S3C2410_GPG3,S3C2410_GPG3_EINT11);s3c2410_gpio_cfgpin(S3C2410_GPG11,S3C2410_GPG11_EINT19);

        set_irq_type(IRQ_EINT0,IRQT_FALLING);set_irq_type(IRQ_EINT2,IRQT_FALLING);
        set_irq_type(IRQ_EINT11,IRQT_FALLING);set_irq_type(IRQ_EINT19,IRQT_FALLING);
}

static void irq_unmask(void)
{
        enable_irq(IRQ_EINT0);enable_irq(IRQ_EINT2);enable_irq(IRQ_EINT11);enable_irq(IRQ_EINT19);
}

static void irq_mask(void)
{
        disable_irq(IRQ_EINT0);disable_irq(IRQ_EINT2);disable_irq(IRQ_EINT11);disable_irq(IRQ_EINT19);
}

static unsigned char key_scan(void)
{
        s3c2410_gpio_cfgpin(S3C2410_GPF0,S3C2410_GPF0_INP);s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_INP);
        s3c2410_gpio_cfgpin(S3C2410_GPG3,S3C2410_GPG3_INP);s3c2410_gpio_cfgpin(S3C2410_GPG11,S3C2410_GPG11_INP);

        s3c2410_gpio_setpin(S3C2410_GPE11,0);s3c2410_gpio_setpin(S3C2410_GPE13,1);
        s3c2410_gpio_setpin(S3C2410_GPG2,1);s3c2410_gpio_setpin(S3C2410_GPG6,1);
        if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x0a;
        if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x07;
        if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x04;
        if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x01;

        s3c2410_gpio_setpin(S3C2410_GPE11,1);s3c2410_gpio_setpin(S3C2410_GPE13,0);
        s3c2410_gpio_setpin(S3C2410_GPG2,1);s3c2410_gpio_setpin(S3C2410_GPG6,1);
        if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x0C;
        if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x09;
        if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x06;
        if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x03;

        s3c2410_gpio_setpin(S3C2410_GPE11,1);s3c2410_gpio_setpin(S3C2410_GPE13,1);
        s3c2410_gpio_setpin(S3C2410_GPG2,0);s3c2410_gpio_setpin(S3C2410_GPG6,1);
        if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x10;
        if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x0F;
        if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x0E;
        if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x0D;

        s3c2410_gpio_setpin(S3C2410_GPE11,1);s3c2410_gpio_setpin(S3C2410_GPE13,1);
        s3c2410_gpio_setpin(S3C2410_GPG2,1);s3c2410_gpio_setpin(S3C2410_GPG6,0);
        if(s3c2410_gpio_getpin(S3C2410_GPF0) == 0) return 0x0B;
        if(s3c2410_gpio_getpin(S3C2410_GPF2) == 0) return 0x08;
        if(s3c2410_gpio_getpin(S3C2410_GPG3) == 0) return 0x05;
        if(s3c2410_gpio_getpin(S3C2410_GPG11) == 0) return 0x02;
}

static irqreturn_t irq_isr(int irq, void *dev_id, struct pt_regs *reg)
{
        int i = *((int *)dev_id);

        spin_lock_irq(&(s3c2410_key_dev.lock));//防止此中断被再次调用发生死锁故而关闭此CPU中断
        BUF_HEAD.pressure = DOWN;
        BUF_HEAD.number = key_scan();
        if(i == IRQ_EINT19) {key_timer[0].expires =jiffies+DELAY;add_timer(key_timer);}
        if(i == IRQ_EINT11) {key_timer[1].expires =jiffies+DELAY;add_timer(key_timer+1);}
        if(i == IRQ_EINT2) {key_timer[2].expires =jiffies+DELAY;add_timer(key_timer+2);}
        if(i == IRQ_EINT0) {key_timer[3].expires =jiffies+DELAY;add_timer(key_timer+3);}
        gpio_set();
        spin_unlock_irq(&(s3c2410_key_dev.lock));
        return IRQ_HANDLED;
}

static void key_timer_handler(unsigned long data)
{
        unsigned long i = data;

        if(BUF_HEAD.number == key_scan())
        {
                if(BUF_HEAD.pressure == DOWN)
                {
                        BUF_HEAD.pressure = UP;
                        INCBUF(s3c2410_key_dev.head,MAX_KEY_BUF);
                        wake_up_interruptible(&(s3c2410_key_dev.wq));//唤醒进程               
                }
        }
        gpio_set();
        if(i == IRQ_EINT19) {key_timer[0].expires =jiffies+DELAY;add_timer(key_timer);}
        if(i == IRQ_EINT11) {key_timer[1].expires =jiffies+DELAY;add_timer(key_timer+1);}
        if(i == IRQ_EINT2)  {key_timer[2].expires =jiffies+DELAY;add_timer(key_timer+2);}
        if(i == IRQ_EINT0)  {key_timer[3].expires =jiffies+DELAY;add_timer(key_timer+3);}
}

static int request_irqs(void)
{
        struct key_info *k=key_info_tab;
        int i,j;

        for(i=0;i<16;i+=4)
        {
                j=request_irq((k+i)->irq_no,irq_isr,SA_INTERRUPT,NAME,&((k+i)->irq_no));
                if(j)
                        return j;
        }
        return 0;
}

static void free_irqs(void)
{
        struct key_info *k=key_info_tab;
        int i;

        for(i=0;i<16;i+=4)
                free_irq((k+i)->irq_no,NULL);
}

static int s3c2410key_open(struct inode *inode, struct file *file)
{
        s3c2410_key_dev.head = s3c2410_key_dev.tail = 0;
        gpio_set();
        irq_unmask();
        return 0;
}

static int s3c2410key_release(struct inode *inode, struct file *file)
{
        int i;

        irq_mask();
        for(i = 0 ;i < 4 ; i++) del_timer(key_timer+i);
        return 0;
}

static ssize_t read_key(KEY_RET *addr,size_t data)
{
        size_t i;

        spin_lock_irq(&(s3c2410_key_dev.lock));
        if(!data) return 0;
        for(i= 0;i < data;i++)
        {
                addr->pressure = BUF_TAIL.pressure;
                addr->number = BUF_TAIL.number;
                addr++;
                INCBUF(s3c2410_key_dev.tail,MAX_KEY_BUF);
        }
        spin_unlock_irq(&(s3c2410_key_dev.lock));
        return (data*sizeof(KEY_RET));
}

static ssize_t s3c2410key_read(struct file *filp,char __user *buffer,size_t count, loff_t *ppos)
{
        KEY_RET ret;
next:
        if(s3c2410_key_dev.head != s3c2410_key_dev.tail)
        {
                if(count >= s3c2410_key_dev.head-s3c2410_key_dev.tail)
                        count = s3c2410_key_dev.head-s3c2410_key_dev.tail;
                count = read_key(&ret,count);
                if (count) copy_to_user(buffer, (char *)&ret, count);
                return count;
        }
        else
        {
                if(filp->f_flags & O_NONBLOCK)
                        return -EAGAIN;
                interruptible_sleep_on(&(s3c2410_key_dev.wq));
                goto next;
        }
}

static struct file_operations s3c2410_key_ops = {
        .owner                = THIS_MODULE,
        .open                = s3c2410key_open,
        .release        = s3c2410key_release,
        .read                 = s3c2410key_read,
};
 
static char __initdata banner[] = "S3C2410 keydriver, 2009 yangdan edit\n";

static int __init s3c2410_key_init(void)
{
        int result,i;

        printk(banner);
        result=alloc_chrdev_region(&devno,0,1,"keydev");
        key_major =MAJOR(devno);
        if(result < 0)
        {
                printk(KERN_NOTICE "error %d request device no",result);               
                return result;
        }

        cdev_init(&s3c2410_key_dev.cdev,&s3c2410_key_ops);
        s3c2410_key_dev.cdev.owner = THIS_MODULE;
        result = cdev_add(&s3c2410_key_dev.cdev,devno,1);
        if(result)
        {
                printk(KERN_NOTICE "error %d adding keydev",result);
                goto error;
        }
                        
        for(i=0;i < MAX_KEY_BUF ; i++)
                s3c2410_key_dev.buf.pressure = UP;
        init_waitqueue_head(&(s3c2410_key_dev.wq));
        
#ifdef CONFIG_DEVFS_FS
        devfs_mk_dir("char/key");
        devfs_mk_cdev(MKDEV(key_major,0),S_IFCHR|S_IRUGO|S_IWUSR,"char/key/%s",NAME);
#endif

        result=request_irqs();
        if(result)
        {
                printk(KERN_NOTICE "error %d request irq",result);
                goto error;
        }

        for(i=0;i < 4 ; i++)
        {        
                if(i == 0)
                        setup_timer(key_timer+i,key_timer_handler,IRQ_EINT19);
                else if(i == 1)
                        setup_timer(key_timer+i,key_timer_handler,IRQ_EINT11);
                else if(i == 2)
                        setup_timer(key_timer+i,key_timer_handler,IRQ_EINT2);
                else
                        setup_timer(key_timer+i,key_timer_handler,IRQ_EINT0);
        }
        return 0;

error:
        unregister_chrdev_region(devno,1);
        return result;
}

static void __exit s3c2410_key_exit(void)
{
        free_irqs();

#ifdef CONFIG_DEVFS_FS
        devfs_remove("char/key/%s", NAME);
        devfs_remove("char/key");
#endif        

        cdev_del(&s3c2410_key_dev.cdev);
        unregister_chrdev_region(devno,1);        
}

module_init(s3c2410_key_init);
module_exit(s3c2410_key_exit);

MODULE_AUTHOR("yangdan, <>");
MODULE_DESCRIPTION("ucdragon fs2410 key Driver");
MODULE_LICENSE("GPL");


添加了devfs功能用于自动生成设备文件,所以使用时内核要添加devfs选项。
阅读(869) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~