1. 程式人生 > >【Tiny6410 And Linux】—(2.3)—使用工作佇列處理按鍵中斷——程式碼

【Tiny6410 And Linux】—(2.3)—使用工作佇列處理按鍵中斷——程式碼

做了做用工作佇列處理按鍵中斷的實驗,對中斷開始明白~~

呵呵~~其實今天就是加了個全域性變數(雖然這在驅動程式中不是很合適吧),還有就是加了個消抖(就是通過延時等待而已)!

1、驅動程式

①、plat_btn_dev.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/device.h>

#define DEVICE_NAME     "tiny6410_buttons"

/* 平臺資源的定義 */
static struct resource tiny6410_buttons_resource[] = {
        [0] = {
                .start = IRQ_EINT(0),
                .end   = IRQ_EINT(0),
                .flags = IORESOURCE_IRQ,
        },
        [1] = {
                .start = IRQ_EINT(1),
                .end   = IRQ_EINT(1),
                .flags = IORESOURCE_IRQ,
        },
        [2] = {
                .start = IRQ_EINT(2),
                .end   = IRQ_EINT(2),
                .flags = IORESOURCE_IRQ,
        },
        [3] = {
                .start = IRQ_EINT(3),
                .end   = IRQ_EINT(3),
                .flags = IORESOURCE_IRQ,
        },
        [4] = {
                .start = IRQ_EINT(4),
                .end   = IRQ_EINT(4),
                .flags = IORESOURCE_IRQ,
        },
        [5] = {
                .start = IRQ_EINT(5),
                .end   = IRQ_EINT(5),
                .flags = IORESOURCE_IRQ,
        },
        [6] = {
                .start = IRQ_EINT(19),
                .end   = IRQ_EINT(19),
                .flags = IORESOURCE_IRQ,
        },
        [7] = {
                .start = IRQ_EINT(20),
                .end   = IRQ_EINT(20),
                .flags = IORESOURCE_IRQ,
        }                                       /* 這裡不需要加逗號 */
};

static struct platform_device *tiny6410_buttons_dev;


static int __init platform_init(void)
{
        printk("[Call platform_init!]\n");
        /* 分配一個 platform_device 結構 */
        tiny6410_buttons_dev = platform_device_alloc(DEVICE_NAME, -1);

        /* 為平臺裝置新增平臺裝置資源 */
        platform_device_add_resources(tiny6410_buttons_dev,
                                        tiny6410_buttons_resource, 8);

        /* 註冊平臺裝置 */
        platform_device_add(tiny6410_buttons_dev);

        return 0;
}

static void __exit platform_exit(void)
{
        printk("[Call platform_exit!]\n");

        platform_device_unregister(tiny6410_buttons_dev);
}

module_init(platform_init);
module_exit(platform_exit);

MODULE_AUTHOR("_Justin");
MODULE_LICENSE("GPL");


②、plat_but_drv.c

#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/unistd.h>
#include <linux/device.h>
#include <linux/workqueue.h>

#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>

#define DRIVER_NAME     "tiny6410_buttons"
#define DEVICE_NAME     "tiny6410_buttons"

/* 注意:
 * 在一個較規範的驅動程式中,通常不會將裝置資源在平臺驅動程式中
 * 以全域性變數的形式給出(這裡是為了簡化操作,將部分裝置資源以全域性變數形式
 * 給出),一個更好的辦法是,將這些資源都新增到對應的平臺裝置中,以平臺
 * 裝置資源或者平臺裝置似有資料的形式管理。
 */
/* 記錄按鍵中斷號 */
typedef struct
{
        int irq;                        /* 中斷號,初始化為 0,將在
                                         * probe 獲取資源 */
        int num;                        /* 對應的按鍵編號 */
        char *name;                     /* 中斷所屬的名字 */
} button_irq_t;

/* 按鍵資料結構 */
typedef struct 
{
	wait_queue_head_t bwq;		/* 按鍵等待佇列 */
	struct delayed_work dwork;	/* 用於按鍵中斷下半部分的延遲工作 */
	button_irq_t *birqs;		/* 指向記錄按鍵中斷資源陣列 */
	int nirqs;			/* 裝置支援的按鍵中斷數 */
	int val;			/* 記錄鍵值 */
	volatile int cnum;		/* 記錄產生中斷的按鍵編號 */
	volatile int press;		/* 等待佇列條件 */
} buttons_data_t;

button_irq_t  button_irqs[] =
{
        {0, 0, "KEY0"},
        {0, 1, "KEY1"},
        {0, 2, "KEY2"},
        {0, 3, "KEY3"},
        {0, 4, "KEY4"},
        {0, 5, "KEY5"},
        {0, 6, "KEY6"},
        {0, 7, "KEY7"},
};

/* 按鍵驅動資料全域性變數 g_bd */
static buttons_data_t g_bd =
{
	.birqs	= button_irqs,
	.nirqs	= sizeof(button_irqs) / sizeof(button_irqs[0]),
	.press	= 0,
};

/* 
 * buttons_work_func 函式
 * 延遲工作的回撥函式則負責完成中斷處理程式中未完成的工作 
 */
static void buttons_work_func(struct work_struct *w)
{
	int down;
	unsigned tmp;
	int num = g_bd.cnum;

        switch(num) 
        {
                case 0: case 1: case 2: case 3: case 4: case 5:
                        tmp = readl(S3C64XX_GPNDAT);
                        down = !(tmp & (1 << num));
                        break;
                
                        case 6: case 7:
                        tmp = readl(S3C64XX_GPLDAT);
                        down = !(tmp & (1 << (num + 5)));
                        break;
                default:
                        down = 0;
        }

        if(down == !(g_bd.val & (1 << num)))
        {
                g_bd.val = down ? g_bd.val | (1<<num) : g_bd.val & ~(1 << num);

                g_bd.press = 1;
                wake_up_interruptible(&g_bd.bwq);
        }

}

/*
 * buttons_interrupt
 * 使用一個整形變數 key_value 的 0~7 位來記錄鍵值,0~7
 * 位分別對應 KEY0~KEY7 的擡起或者按下情況( 1 表示按下)。
 */
static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
        /* 定義一個指標,指向所對應的中斷號 */
        button_irq_t *birq = (button_irq_t *)dev_id;

	g_bd.cnum = birq->num;
	
	/* 延遲 20ms,用作按鍵消抖 */
	schedule_delayed_work(&g_bd.dwork,HZ/50);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

/*
 * tiny6410_buttons_open
 */
static int tiny6410_buttons_open(struct inode *inode,struct file *file)
{
        int i;
        int err = 0;

        for(i = 0;i < sizeof(button_irqs) / sizeof(button_irqs[0]);i ++ )
        {
                if (button_irqs[i].irq < 0)
                        continue;

                /* 申請中斷 */ 
                err = request_irq(button_irqs[i].irq,buttons_interrupt,
                                        IRQ_TYPE_EDGE_BOTH,
                                        button_irqs[i].name,
                                        (void *)&button_irqs[i]);
                if (err)
                        break;
        }
 
        if(err) 
        {
                i--;
                for(;i >= 0;i -- )
                {
                        if(button_irqs[i].irq < 0)
                                continue;

                        disable_irq(button_irqs[i].irq);
                        free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
                }

                return -EBUSY;
        }

//	ev_press = 1;

        return 0;
}

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

        for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i ++ )
        {
                if(button_irqs[i].irq < 0)
                        continue;

                free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
        }

        return 0;
}

/*
 * tiny6410_buttons_read
 */
static int tiny6410_buttons_read(struct file *filp,char __user *buff,
                                        size_t count,loff_t *offp)
{
        unsigned long err;

        if(!g_bd.press)
        {
                if(filp->f_flags & O_NONBLOCK)
                        return -EAGAIN;
                else
                        wait_event_interruptible(g_bd.bwq,g_bd.press);
        }

        g_bd.press = 0;

        err = copy_to_user((void *)buff,(const void *)(&g_bd.val),
                                min(sizeof(&g_bd.val),count));

        return err ? -EFAULT : min(sizeof(&g_bd.val),count);
}

/*
 * poll 實現函式 
 */
static unsigned int tiny6410_buttons_poll(struct file *file,
                                                struct poll_table_struct *wait)
{
        unsigned int mask = 0; 
        poll_wait(file,&g_bd.bwq,wait);

        if(g_bd.press)
                mask |= POLLIN | POLLRDNORM;
                 
        return mask;
}

/*
 * file_operation
 */
static struct file_operations dev_fops =
{
        .owner	= THIS_MODULE,
        .open   = tiny6410_buttons_open,
        .release = tiny6410_buttons_close,
        .read   = tiny6410_buttons_read,
        .poll   = tiny6410_buttons_poll,
};

/*
 * 混合裝置結構體
 */
static struct miscdevice tiny6410_buttons_misc =
{
        .minor = MISC_DYNAMIC_MINOR,
        .name  = DEVICE_NAME,
        .fops  = &dev_fops,
}; 
 
/* 
 * probe 實現函式
 */
static int tiny6410_buttons_probe(struct platform_device *pdev)
{
        int ret;
        int i;  
        static struct resource   *buttons_irq;
  
        printk("[call %s]\n", __func__);

	/* 初始化等待佇列 */
	init_waitqueue_head(&g_bd.bwq);

	/* 初始化 delay_work 結構,來使用延遲排程工作 */
	INIT_DELAYED_WORK(&g_bd.dwork,buttons_work_func);   

        /* 獲取裝置資源 */
        for(i = 0;i < 8;i ++ )
        {
                buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i);
                button_irqs[i].irq = buttons_irq->start;
        }

        ret = misc_register(&tiny6410_buttons_misc);

        return 0;
}

/*
 * remove 實現函式
 */
static int tiny6410_buttons_remove(struct platform_device *dev)
{
        misc_deregister(&tiny6410_buttons_misc);
        return 0;
}

/* 平臺裝置驅動結構 */
static struct platform_driver tiny6410_buttons_driver = {
        .probe  = tiny6410_buttons_probe,
        .remove = tiny6410_buttons_remove,
        .driver =
        {
                .owner  = THIS_MODULE,
                .name   = DRIVER_NAME,
        },
};

static int __init buttons_init(void)
{
        printk("[Call buttons_init!]\n");       

        /* 註冊驅動 */
        platform_driver_register(&tiny6410_buttons_driver);
        return 0;
}

static void __exit buttons_exit(void)
{
        printk("[Call buttons_exit!]\n");

        platform_driver_unregister(&tiny6410_buttons_driver);
}

module_init(buttons_init);
module_exit(buttons_exit);

MODULE_AUTHOR("_Justin");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Tiny6410 Buttons Driver");


③、Makefile

ifneq ($(KERNELRELEASE),)
obj-m := plat_btn_dev.o plat_btn_drv.o

else
KDIR := /home/_Jana/linux-2.6.38

all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-

clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

endif


2、測試程式

①、app_btn.c

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

#define DEVICE_NAME     "/dev/tiny6410_buttons"

static int key_value = 0;

int main(void)
{
        int fd, ret;
        fd_set rfds;
        int last_kval = key_value;

        if(-1 == (fd = open(DEVICE_NAME,O_RDONLY)))
        {
                printf("open %s error\n",DEVICE_NAME);
                _exit(EXIT_FAILURE);
        }

        /* 先清空集合 */
        FD_ZERO(&rfds);

        /* 設定要監控的檔案描述符 */
        FD_SET(fd,&rfds);

        printf("Test for tiny6410_buttons: ...\n");

        while(1)
        {
                if(-1 == (ret = select(fd + 1,&rfds,NULL,NULL,NULL)))
                {
                        printf("select error\n");
                        _exit(EXIT_FAILURE);
                }

                if(FD_ISSET(fd,&rfds))
                {
                        read(fd, &key_value, sizeof(key_value));

                        int i;
                        for(i = 0;i < 8;i ++ )
                        {
                                if((key_value & (1 << i)) != (last_kval & (1 << i)))
                                {
                                        printf("KEY%d: %s (key_value=0x%x)\n", i+1,
                                               (key_value& (1<<i))? "DOWN": "UP", key_value);                   
                                }
                        }
                        last_kval = key_value;
                }
    }
        _exit(EXIT_SUCCESS);
}


3、測試結果