s3c2410 4X4矩阵键盘驱动

参考了一些例子,写了一个GEC2410 上的4X4矩阵键盘的驱动,中间因为使用了开发板自带内核中的一些中断设置函数而导致错误不断,事实证明,不要去相信任何东西~~    


//驱动代码如下.主设备号设为232 ,适用GEC2410 十六键矩阵键盘

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <asm/system.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/arch-s3c2410/regs-irq.h>
#include <linux/ioport.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <asm/hardware.h>
#include <linux/interrupt.h>
#include <asm/signal.h>

#define S3C2410_GPG11 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 11) // S3C2410_GPG11 undefined in regs-gpio.h


#define DEVICE_NAME "matrix_button"
#define BUTTON_MAJOR 232

unsigned int s3c2410_gpio_getpin(unsigned int pin)
{
     unsigned long base = S3C2410_GPIO_BASE(pin);
     unsigned long offs = S3C2410_GPIO_OFFSET(pin);
         return __raw_readl(base + 0x04) & (1<< offs);
}

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
    unsigned long base = S3C2410_GPIO_BASE(pin);
    unsigned long mask;
    unsigned long con;
    unsigned long flags;
    if(pin<S3C2410_GPIO_BANKB)
        mask=1<<S3C2410_GPIO_OFFSET(pin);
    else
        mask =3<< (S3C2410_GPIO_OFFSET(pin)*2);
    local_irq_save(flags);

    con = __raw_readl(base + 0x00);

    con &= ~mask;
    con |= function;

    __raw_writel(con, base + 0x00);

    local_irq_restore(flags);
}

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)
{
    unsigned long base = S3C2410_GPIO_BASE(pin);
    unsigned long offs = S3C2410_GPIO_OFFSET(pin);
    unsigned long flags;
    unsigned long dat;

    local_irq_save(flags);

    dat = __raw_readl(base + 0x04);
    dat &= ~(1 << offs);
    dat |= to << offs;
    __raw_writel(dat, base + 0x04);

    local_irq_restore(flags);
}

#define EXTINT_OFF (IRQ_EINT4 - 4)

// clear SRCPND&INTPND
void s3c_irq_ack(unsigned int irqno)
{
    unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
    __raw_writel(bitval, S3C2410_SRCPND);
    __raw_writel(bitval, S3C2410_INTPND);
    return;
}
void s3c_irqext_ack(unsigned int irqno)
{
    unsigned long req;
    unsigned long bit;
    bit = 1UL << (irqno - EXTINT_OFF);
    __raw_writel(bit, S3C2410_EINTPEND);
    req = __raw_readl(S3C2410_EINTPEND);
    if (irqno <= IRQ_EINT7 )
    {
        if ((req & 0xf0) == 0)
        s3c_irq_ack(IRQ_EINT4t7);
    }
    else
    {
        if ((req >> 8) == 0)
        s3c_irq_ack(IRQ_EINT8t23);
    }
    return;
}


// set external interrupt mode, trigger type and gpio registers
int realarm_interrupt_init(unsigned int irq, unsigned int type)
{
    unsigned long gpcon_reg;
    unsigned long gpcon_offset;
    unsigned long extint_reg;
    unsigned long extint_offset;
    unsigned long newvalue = 0;
    unsigned long value;
    if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
    {
        gpcon_reg = S3C2410_GPFCON;
        extint_reg = S3C2410_EXTINT0;
        gpcon_offset = (irq - IRQ_EINT0) * 2;
        extint_offset = (irq - IRQ_EINT0) * 4;
    }
    else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
    {
        gpcon_reg = S3C2410_GPFCON;
        extint_reg = S3C2410_EXTINT0;
        gpcon_offset = (irq - EXTINT_OFF) * 2;
        extint_offset = (irq - EXTINT_OFF) * 4;
    }
    else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
    {
        gpcon_reg = S3C2410_GPGCON;
        extint_reg = S3C2410_EXTINT1;
        gpcon_offset = (irq - IRQ_EINT8) * 2;
        extint_offset = (irq - IRQ_EINT8) * 4;
    }
    else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
    {
        gpcon_reg = S3C2410_GPGCON;
        extint_reg = S3C2410_EXTINT2;
        gpcon_offset = (irq - IRQ_EINT8) * 2;
        extint_offset = (irq - IRQ_EINT16) * 4;
    } else
    {
        return -1;
    }
    /* Set the GPIO to external interrupt mode */
    value = __raw_readl(gpcon_reg);
    value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
    __raw_writel(value, gpcon_reg);
    /* Set the external interrupt to pointed trigger type */
    switch (type)
    {
    case IRQT_NOEDGE:
        printk(KERN_WARNING "No edge setting!/n");
        break;
    case IRQT_RISING:
        newvalue = S3C2410_EXTINT_RISEEDGE;
        break;
    case IRQT_FALLING:
        newvalue = S3C2410_EXTINT_FALLEDGE;
        break;
    case IRQT_BOTHEDGE:
        newvalue = S3C2410_EXTINT_BOTHEDGE;
        break;
    case IRQT_LOW:
        newvalue = S3C2410_EXTINT_LOWLEV;
        break;
    case IRQT_HIGH:
        newvalue = S3C2410_EXTINT_HILEV;
        break;
    default:
        printk(KERN_ERR "No such irq type %d", type);
        return -1;
    }
    value = __raw_readl(extint_reg);
    value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
    __raw_writel(value, extint_reg);
    return 0;
}



static struct key_info {
int irq_no;
unsigned int gpio_port;
int function_inp;
int function_eintn;
}key_info_tab[4]={
        { IRQ_EINT0, S3C2410_GPF0,S3C2410_GPF0_INP,S3C2410_GPF0_EINT0},
        { IRQ_EINT2, S3C2410_GPF2,S3C2410_GPF2_INP,S3C2410_GPF2_EINT2},
        { IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_INP,S3C2410_GPG3_EINT11 },
        { IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_INP,S3C2410_GPG11_EINT19}};

struct matrix_button
{
    int key_value;
    int ready;
    int open_count;
    wait_queue_head_t buttons_wait;
    struct cdev cdev;
    struct semaphore sem;
};

static struct matrix_button* matrix_button_dev;

static struct output_pin
{
    unsigned int gpio_pin;
    int function_outp;
}output_pin_tab[4]={
    {S3C2410_GPE11,S3C2410_GPE11_OUTP},
    {S3C2410_GPG6,S3C2410_GPG6_OUTP},
    {S3C2410_GPE13,S3C2410_GPE13_OUTP},
    {S3C2410_GPG2,S3C2410_GPG2_OUTP}
};

static int  key_scan(void)
{

    int i,j,k,ret=0;
    for(i=0;i<4;i++)
    {
                
            for(j=0;j<4;j++)
            {
                if(i==j)s3c2410_gpio_setpin(output_pin_tab[j].gpio_pin,0);
                 else s3c2410_gpio_setpin(output_pin_tab[j].gpio_pin,1);
            }
            for(k=0;k<4;k++)
            {
                if(s3c2410_gpio_getpin(key_info_tab[k].gpio_port)==0)
                {
                    ret=i*10+k+1;
                    return ret;
                }
            }
    }    
    return ret;
}

static void port_init(void)
{
    int i;
    for(i=0;i<4;i++)
    {
        s3c2410_gpio_cfgpin(output_pin_tab[i].gpio_pin,output_pin_tab[i].function_outp);
        s3c2410_gpio_setpin(output_pin_tab[i].gpio_pin,0);
        s3c2410_gpio_cfgpin(key_info_tab[i].gpio_port,key_info_tab[i].function_eintn);
        realarm_interrupt_init(key_info_tab[i].irq_no,IRQT_FALLING);
    }
}

static irqreturn_t matrix_button_irq(int irq, void *dev_id, struct pt_regs *reg)
{
    struct matrix_button *dev=dev_id;
    struct key_info *k;
    int i;
    int found = 0;
    int flags;
    down(&dev->sem);
    for (i = 0; i < sizeof(key_info_tab)/ sizeof(key_info_tab[0]); i++)
    {
            k = key_info_tab + i;
            if (k->irq_no == irq)
            {
                    found = 1;
                    break;
            }
    }
    if (!found)
    {
        printk(KERN_NOTICE "bad irq %d in button/n", irq);
        return IRQ_HANDLED;
    }
    save_flags(flags); // save CPSR
    cli();     // Close interrupt
    if(k->irq_no<IRQ_EINT3)
        s3c_irq_ack(k->irq_no);
    else
        s3c_irqext_ack(k->irq_no);
    for(i=0;i<sizeof(key_info_tab)/sizeof(key_info_tab[0]);i++)
    {
        k=key_info_tab+i;
        s3c2410_gpio_cfgpin(k->gpio_port,k->function_inp);
    }
    mdelay(100);
    dev->key_value=key_scan();
    if(dev->key_value==0)
    {
        up(&dev->sem);
        port_init();
        restore_flags(flags);
        return IRQ_HANDLED;
    }
    dev->ready=1;
    up(&dev->sem);
    wake_up(&dev->buttons_wait);
    port_init();
    restore_flags(flags);
    return IRQ_HANDLED;
}


static int request_irqs(struct matrix_button *dev)
{
    struct key_info *k;
    int i;
    for (i = 0; i < sizeof(key_info_tab) / sizeof(key_info_tab[0]); i++)
    {
        k = key_info_tab + i;
        if (request_irq(k->irq_no, &matrix_button_irq, SA_INTERRUPT, DEVICE_NAME, dev)) return -1;  
    }
    return 0;
}

static void free_irqs(struct matrix_button *dev)
{
    struct key_info *k;
    int i;
    for (i = 0; i < sizeof(key_info_tab) / sizeof(key_info_tab[0]); i++)
    {
        k = key_info_tab + i;
        free_irq(k->irq_no,dev);
    }
}

static int matrix_button_read(struct file * filp, char __user * buffer, size_t count, loff_t *f_pos)
{
    struct matrix_button *dev=filp->private_data;
    down(&dev->sem);
    if(!dev->ready)
       return -EAGAIN;
    copy_to_user(buffer, &dev->key_value, sizeof(int));
    dev->ready = 0;
    up(&dev->sem);
    return count;
}

static unsigned int matrix_button_poll( struct file *filp, struct poll_table_struct *wait)
{
    struct matrix_button *dev=filp->private_data;
    poll_wait(filp, &dev->buttons_wait, wait);
    if (dev->ready)
        return POLLIN;
    else
        return 0;
}


static int matrix_button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
    switch(cmd) {
    default:
    return -EINVAL;
    }
}

static int matrix_button_open(struct inode *inode,struct file *filp)
{
    int ret;
    struct matrix_button *dev;
    dev=container_of(inode->i_cdev,struct matrix_button,cdev);
    down(&dev->sem);
    filp->private_data=dev;
    if(dev->open_count==0)
    {
        ret = request_irqs(dev);
        if (ret)
        {
        cdev_del(&matrix_button_dev->cdev);
        unregister_chrdev_region(MKDEV(BUTTON_MAJOR,0),1);
        kfree(matrix_button_dev);
        printk(DEVICE_NAME " can't request irqs/n");
        return ret;
        }
    }
    dev->open_count++;
    up(&dev->sem);
    return 0;
}

static int matrix_button_release(struct inode *inode,struct file *filp)
{
    struct matrix_button *dev=filp->private_data;
    down(&dev->sem);
    if((dev->open_count--)==1)
        free_irqs(dev);
    up(&dev->sem);
    return 0;
}

static struct file_operations matrix_button_fops = {
owner: THIS_MODULE,
ioctl: matrix_button_ioctl,
poll: matrix_button_poll,
read: matrix_button_read,
open: matrix_button_open,
release:matrix_button_release
};

static int __init matrix_button_init(void)
{
    int ret;
    dev_t dev=MKDEV(BUTTON_MAJOR,0);
    ret=register_chrdev_region(dev,1,"matrix_button");
    if(ret<0)
        return ret;
    matrix_button_dev=kmalloc(sizeof(struct matrix_button),GFP_KERNEL);
    memset(matrix_button_dev,0,sizeof(struct matrix_button));
    matrix_button_dev->ready=0;
    matrix_button_dev->key_value=0;
    matrix_button_dev->open_count=0;
    init_waitqueue_head(&matrix_button_dev->buttons_wait);
    init_MUTEX(&matrix_button_dev->sem);    
    cdev_init(&matrix_button_dev->cdev,&matrix_button_fops);
    matrix_button_dev->cdev.owner=THIS_MODULE;
    matrix_button_dev->cdev.ops=&matrix_button_fops;
    port_init();
    ret=cdev_add(&matrix_button_dev->cdev,dev,1);
    if(ret)
        printk(KERN_NOTICE "Error %d",ret);
    
    return 0;
}

static void __exit matrix_button_exit(void)
{
    cdev_del(&matrix_button_dev->cdev);
    unregister_chrdev_region(MKDEV(BUTTON_MAJOR,0),1);
    kfree(matrix_button_dev);
    free_irqs(matrix_button_dev);  
}

module_init(matrix_button_init);
module_exit(matrix_button_exit);
MODULE_LICENSE("GPL");


//以下为测试代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

int main(void)
{
    int buttons_fd,i;
    int key_value;
    buttons_fd = open("/dev/matrix_button", 0);
    if (buttons_fd < 0) {
    perror("open device buttons");
    exit(1);
    }
    for (;;)
    {
        fd_set rds;
        int ret;
        FD_ZERO(&rds);
        FD_SET(buttons_fd, &rds);
        ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);
        if (ret < 0)
        {
            perror("select");
            exit(1);
        }
        if (ret == 0)
        {
            printf("Timeout./n");
        }
        else if (FD_ISSET(buttons_fd, &rds))
        {
            int ret = read(buttons_fd, &key_value, sizeof key_value);
            if (ret != sizeof key_value)
            {
                if (errno != EAGAIN)
                    perror("read buttons/n");
                continue;
            }
            else
            {
                printf("buttons_value: %d/n", key_value);
            }
        }
    }
    close(buttons_fd);
    return 0;
}


linuxdriver_code_tool |-- 03 | `-- 2.6内核升级工具 | |-- device-mapper-1.00.19-2.i386.rpm | |-- lvm2-2.00.25-1.01.i386.rpm | |-- mkinitrd-4.2.0.3.tar.tar | |-- module-init-tools-3.2.2.tar.bz2 | `-- modutils-2.4.5-1.src.rpm |-- 04 | |-- 内核模块参数范例 | | `-- book.c | |-- 内核模块导出符号 | | `-- export_symb.c | `-- 最简单的内核模块 | `-- hello.c |-- 05 | `-- udev源代码 | `-- udev-114.tar.gz |-- 06 | |-- globalmem驱动 | | `-- globalmem.c | `-- 包含2个globalmem设备的驱动 | `-- globalmem_two.c |-- 07 | `-- 含并发控制的globalmem驱动 | `-- globalmem_lock.c |-- 08 | |-- globalfifo驱动 | | `-- globalfifo.c | `-- poll应用程序范例 | `-- pollmonitor.c |-- 09 | |-- 异步通知应用程序范例 | | `-- asyncmonitor.c | `-- 支持异步通知的globalfifo | `-- globalfifo_async.c |-- 10 | |-- S3C2410实时钟驱动 | | `-- s3c2410-rtc.c | `-- 秒设备驱动与应用程序 | |-- second.c | `-- second_test.c |-- 11 | |-- DMA范例 | | |-- 3c505.c | | |-- 3c505.h | | `-- dma.h | `-- 静态映射范例 | `-- mach-smdk2440.c |-- 12 | |-- NVRAM驱动 | | `-- generic_nvram.c | |-- 触摸屏驱动 | | |-- 作为input设备 | | | |-- s3c2410_ts.c | | | `-- s3c2410_ts.h | | `-- 作为普通字符设备 | | `-- s3c2410-ts.c | |-- 看门狗驱动 | | `-- s3c2410_wdt.c | `-- 平台设备 | `-- devs.c |-- 13 | |-- IDE驱动 | | |-- ide-disk.c | | `-- ide-h8300.c | `-- RAMDISK驱动 | `-- rd.c |-- 14 | |-- S3C2410串口驱动 | | |-- regs-gpio.h | | |-- regs-serial.h | | `-- s3c2410.c | `-- 串口核心层 | |-- serial_core.c | `-- serial_core.h |-- 15 | |-- S3C2410 I2C主机驱动 | | |-- i2c-s3c2410.c | | |-- iic.h | | |-- regs-gpio.h | | `-- regs-iic.h | `-- SAA711x I2C设备驱动 | `-- saa711x.c |-- 16 | `-- CS8900以太网设备驱动 | |-- cs89x0.c | `-- cs89x0.h |-- 17 | |-- ALSA工具及库 | | |-- alsa-driver-1.0.15.tar.bz2 | | |-- alsa-firmware-1.0.15.tar.bz2 | | |-- alsa-lib-1.0.15.tar.bz2 | | |-- alsa-oss-1.0.15.tar.bz2 | | |-- alsa-tools-1.0.15.tar.bz2 | | |-- alsa-utils-1.0.13.tar.bz2 | | `-- pyalsa-1.0.15.tar.bz2 | |-- ALSA驱动范例 | | |-- sa11xx-uda1341.c | | `-- uda1341.h | |-- ALSA应用程序范例 | | |-- pcm.c | | `-- pcm_min.c | |-- OSS驱动范例 | | `-- s3c2410-uda1341.c | `-- OSS应用程序范例 | |-- mixer.c | `-- sound.c |-- 18 | |-- FRAMEBUFFER应用程序范例 | | `-- fb_display | | |-- fb_display.c | | |-- fb_display.h | | |-- Makefile | | |-- README | | `-- test.c | `-- S3C2410 LCD驱动 | |-- s3c2410fb.c | `-- s3c2410fb.h |-- 19 | |-- busybox源代码 | | `-- busybox-1.2.1.tar.bz2 | |-- MTD工具 | | `-- mtd-utils-1.0.0.tar.gz | |-- nand驱动范例 | | `-- s3c2410.c | |-- nor驱动范例 | | `-- s3c2410nor.c | `-- yaffs&yaffs2源代码 | |-- yaffs.tar.gz | `-- yaffs2.tar.gz |-- 20 | |-- USB串口驱动 | | |-- usb-serial.c | | `-- usb-serial.h | |-- USB工具 | | `-- usbview-1.0.tar.tar | |-- USB骨架程序 | | `-- usb-skeleton.c | |-- USB键盘驱动 | | |-- input.h | | |-- usb_input.h | | `-- usbkbd.c | `-- usb主机控制器驱动范例 | |-- ohci-s3c2410.c | `-- usb-control.h |-- 21 | |-- PCI骨架程序 | | `-- pci-skeleton.c | `-- PCI驱动范例 | `-- i810_audio.c `-- 22 |-- 范例代码 | |-- oops范例 | | |-- oops_example.asm | | `-- oops_example.c | `-- proc范例 | `-- sim_proc.c `-- 内核调试工具 |-- ddd-3.3.11.tar.gz |-- gdbmod-2.4.bz2 |-- kdb-v4.4-2.6.15-rc5-common-1.bz2 |-- kdb-v4.4-2.6.15-rc5-common-2.bz2 |-- kdb-v4.4-2.6.15-rc5-i386-1.bz2 `-- linux-2.6.15.5-kgdb-2.4.tar.tar 73 directories, 91 files
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值