//#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/irq.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/ioctl.h>
#include <asm/uaccess.h>
#include <asm/hardware.h>
#include <asm/arch/regs-gpio.h>
#define DEVICE_NAME "button"
#define MAX_KEY_COUNT 32
#define EXTINT0 *(volatile unsigned int *)S3C2410_EXTINT0
#define EXTINT1 *(volatile unsigned int *)S3C2410_EXTINT1
#define EXTINT2 *(volatile unsigned int *)S3C2410_EXTINT2
MODULE_LICENSE("GPL");//模塊應該指定代碼所使用的許可證
typedef struct
{
unsigned long jiffy[MAX_KEY_COUNT]; //按鍵時間, 如果讀鍵時, 5秒鐘以前的銨鍵作廢
unsigned char buf[MAX_KEY_COUNT]; //按鍵緩衝區
unsigned int head,tail; //按鍵緩衝區頭和尾
}KEY_BUFFER;
static KEY_BUFFER g_keyBuffer; //鍵盤緩衝區
static spinlock_t buffer_lock; //緩衝區鎖
static int button_major = 255; //Define device major add by yoyo
static void *gpecon;
static void *gpedat;
static void *gpfcon;
static void *gpfdat;
static void *gpgcon;
static void *gpgdat;
/*
*功能: 獲取當前的毫秒數(從系統啓動開始)
*入口:
*/
static unsigned long GetTickCount(void)
{
struct timeval currTick;
unsigned long ulRet;
do_gettimeofday(&currTick);
ulRet = currTick.tv_sec;
ulRet *= 1000;
ulRet += (currTick.tv_usec + 500) / 1000;
return ulRet;
}
/*
*功能: 初始化鍵盤緩衝區
*入口:
*/
static void init_keybuffer(void)
{
int i;
spin_lock_irq(&buffer_lock); //獲得一個自旋鎖具有不會受中斷的干擾
g_keyBuffer.head = 0;
g_keyBuffer.tail = 0;
for(i = 0; i < MAX_KEY_COUNT; i++)
{
g_keyBuffer.buf[i] = 0;
g_keyBuffer.jiffy[i] = 0;
}
spin_unlock_irq(&buffer_lock);//釋放自旋鎖
}
/*
*功能: 刪除過時(5秒前的按鍵值)
*入口:
*/
static void remove_timeoutkey(void)
{
unsigned long ulTick;
spin_lock_irq(&buffer_lock); //獲得一個自旋鎖具有不會受中斷的干擾
while(g_keyBuffer.head != g_keyBuffer.tail)
{
ulTick = GetTickCount() - g_keyBuffer.jiffy[g_keyBuffer.head];
if (ulTick < 5000) //5秒
break;
g_keyBuffer.buf[g_keyBuffer.head] = 0;
g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
g_keyBuffer.head ++;
g_keyBuffer.head &= (MAX_KEY_COUNT -1);
}
spin_unlock_irq(&buffer_lock);//釋放自旋鎖
}
/*
*功能: 初始化GPIO, 設置中斷0, 2, 11, 19爲下降沿中斷
*入口:
*/
static void init_gpio(void)
{
//將GPE13 11 設置低位
writel((readl(gpecon) | ((3<<26)|(3<<22))) & (~((1<<27)|(1<<23))), gpecon); //GPE13,11 設置爲輸出
writel(readl(gpedat) & 0xffffd7ff, gpedat); //GPE13,11 輸出爲0
//將GPG6, 2 設置低位
writel((readl(gpgcon) | 0x3030) & 0xffffdfdf, gpgcon); //GPG6,2 設置爲輸出
writel(readl(gpgdat) & 0xffffffbb, gpgdat); //GPG6,2 輸出爲0
writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon); //GPF2, 0 設置爲中斷
writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((1<<22) | (1<<6))), gpgcon); //GPG11,3 設置爲中斷
set_irq_type(IRQ_EINT0, IRQT_FALLING); //下降沿觸發(設置中斷類型)
EXTINT0=(EXTINT0&(~0x07))+0x02;
set_irq_type(IRQ_EINT2, IRQT_FALLING);
EXTINT0=(EXTINT0&(~(0x07<<8)))+(0x02<<8);
set_irq_type(IRQ_EINT11, IRQT_FALLING);
EXTINT1=(EXTINT1&(~(0x07<<12)))+(0x02<<12);
set_irq_type(IRQ_EINT19, IRQT_FALLING);
EXTINT2=(EXTINT2&(~(0x07<<12)))+(0x02<<12);
}
/*
*功能: 激活中斷
*入口:
*/
static __inline void enable_irqs(void)
{
enable_irq(IRQ_EINT0);
enable_irq(IRQ_EINT2);
enable_irq(IRQ_EINT11);
enable_irq(IRQ_EINT19);
}
/*
*功能: 屏蔽中斷
*入口:
*/
static __inline void disable_irqs(void)
{
disable_irq(IRQ_EINT0);
disable_irq(IRQ_EINT2);
disable_irq(IRQ_EINT11);
disable_irq(IRQ_EINT19);
}
/*
*功能: 進入中斷後, 掃描銨鍵碼
*入口:
*返回: 按鍵碼(1-16), 0xff表示錯誤
*/
static __inline unsigned char button_scan(int irq)
{
long lGPF, lGPG;
writel((readl(gpfcon) | 0x33) & 0xffffffcc, gpfcon); //GPF2,0 input
writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((3<<22) | (3<<6))), gpgcon); //GPG11,3 input
//不利用irq號, 直接掃描鍵盤
//設置G2低位, G6, E11, E13高位
writel((readl(gpgdat) | (1<<6)) & (~(1<<2)), gpgdat);
writel(readl(gpedat) | (1<<11) | (1<<13), gpedat);
//取GPF0, GPF2, GPG3, GPG11的值
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
//判斷按鍵
if ((lGPF & (1<<0)) == 0) return 16;
else if((lGPF & (1<<2)) == 0) return 15;
else if((lGPG & (1<<3)) == 0) return 14;
else if((lGPG & (1<<11)) == 0) return 13;
//設置G6低位, G2, E11, E13高位
writel((readl(gpgdat) | (1<<2)) & (~(1<<6)), gpgdat);
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
if ((lGPF & (1<<0)) == 0) return 11;
else if((lGPF & (1<<2)) == 0) return 8;
else if((lGPG & (1<<3)) == 0) return 5;
else if((lGPG & (1<<11)) == 0) return 2;
//設置E11低位, G2, G6, E13高位
writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
writel((readl(gpedat) | (1<<13)) & (~(1<<11)), gpedat);
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
if ((lGPF & (1<<0)) == 0) return 10;
else if((lGPF & (1<<2)) == 0) return 7;
else if((lGPG & (1<<3)) == 0) return 4;
else if((lGPG & (1<<11)) == 0) return 1;
//設置E13低位, G2, G6, E11高位
//writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);
writel((readl(gpedat) | (1<<11)) & (~(1<<13)), gpedat);
lGPF = readl(gpfdat);
lGPG = readl(gpgdat);
if ((lGPF & (1<<0)) == 0) return 12;
else if((lGPF & (1<<2)) == 0) return 9;
else if((lGPG & (1<<3)) == 0) return 6;
else if((lGPG & (1<<11)) == 0) return 3;
return 0xff ;
}
/*
*功能: 中斷函數,
*入口: irq 中斷號
*
*/
//struct pt_regs:This struct defines the way the registers are stored on the
//stack during a system call
static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned char ucKey;
disable_irqs();
printk("in irq\n");
//延遲50毫秒, 屏蔽按鍵毛刺
__udelay(50000);
ucKey = button_scan(irq);
if ((ucKey >= 1) && (ucKey <= 16))
{
//如果緩衝區已滿, 則不添加
if (((g_keyBuffer.head + 1) & (MAX_KEY_COUNT - 1)) != g_keyBuffer.tail)
{
spin_lock_irq(&buffer_lock);
g_keyBuffer.buf[g_keyBuffer.tail] = ucKey;
g_keyBuffer.jiffy[g_keyBuffer.tail] = GetTickCount();
g_keyBuffer.tail ++;
g_keyBuffer.tail &= (MAX_KEY_COUNT -1);
spin_unlock_irq(&buffer_lock);
}
}
init_gpio();
enable_irqs();
//printk("in irq! %x\n",EXTINT0);
return IRQ_HANDLED;//2.6內核返回值一般是這個宏。
}
/*
*功能: 申請中斷
*入口:
*
*/
static int request_irqs()
{
int ret;
ret = request_irq(IRQ_EINT0, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret < 0)
return ret;
ret = request_irq(IRQ_EINT2, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret >= 0)
{
ret = request_irq(IRQ_EINT11, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret >= 0)
{
ret = request_irq(IRQ_EINT19, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
if (ret >= 0)
return ret;
free_irq(IRQ_EINT11, button_irq);
}
free_irq(IRQ_EINT2, button_irq);
}
free_irq(IRQ_EINT0, button_irq);
return ret;
}
/*
*功能: 釋放中斷
*入口:
*
*/
static __inline void free_irqs()
{
free_irq(IRQ_EINT0, NULL);//button_irq);
free_irq(IRQ_EINT2, NULL);//button_irq);
free_irq(IRQ_EINT11, NULL);//button_irq);
free_irq(IRQ_EINT19, NULL);//button_irq);
}
/*
*功能: 打開文件, 開始中斷
*入口:
*
*/
static int button_open(struct inode *inode,struct file *filp)
{
int ret = nonseekable_open(inode, filp);
if (ret >= 0)
{
init_keybuffer();
enable_irqs();
}
return ret;
}
/*
*功能: 關閉文件, 屏蔽中斷
*入口:
*
*/
static int button_release(struct inode *inode,struct file *filp)
{
disable_irqs();
return 0;
}
/*
*功能: 讀鍵盤
*入口:
*
*/
static ssize_t button_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
ssize_t ret = 0;
remove_timeoutkey();
spin_lock_irq(&buffer_lock);
while((g_keyBuffer.head != g_keyBuffer.tail) && (((size_t)ret) < count) )
{
buffer[ret] = (char)(g_keyBuffer.buf[g_keyBuffer.head]);
g_keyBuffer.buf[g_keyBuffer.head] = 0;
g_keyBuffer.jiffy[g_keyBuffer.head] = 0;
g_keyBuffer.head ++;
g_keyBuffer.head &= (MAX_KEY_COUNT -1);
ret ++;
}
spin_unlock_irq(&buffer_lock);
return ret;
}
/*
*功能: 清空鍵盤緩衝區
*入口:
*
*/
static int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
init_keybuffer();
return 1;
}
//add by yoyo
/*
*初始化並添加結構提struct cdev到系統之中
*/
static void led_setup_cdev(struct cdev *dev,int minor,struct file_operations *fops)
{
int err;
int devno=MKDEV(button_major,minor);
cdev_init(dev,fops);//初始化結構體struct cdev
dev->owner=THIS_MODULE;
dev->ops=fops;//給結構體裏的ops成員賦初值,這裏是對設備操作的具體的實現函數
err=cdev_add(dev,devno,1);//將結構提struct cdev添加到系統之中
if(err)
printk(KERN_INFO"Error %d adding button %d\n",err,minor);
}
/*
*定義一個file_operations結構體,來實現對設備的具體操作的功能
*/
static struct file_operations button_fops =
{
.owner = THIS_MODULE,
.ioctl = button_ioctl,
.open = button_open,
.read = button_read,
.release = button_release,
};
static struct cdev SimpleDevs; //add by yoyo
/*
*功能: 驅動初始化
*入口:
*
*/
static int button_init(void)
{
int ret;
int result; //add by yoyo
gpecon = ioremap(0x56000040, 0x04);//得到相應IO口的虛擬地址,下同
gpedat = ioremap(0x56000044, 0x04);
gpfcon = ioremap(0x56000050, 0x04);
gpfdat = ioremap(0x56000054, 0x04);
gpgcon = ioremap(0x56000060, 0x04);
gpgdat = ioremap(0x56000064, 0x04);
init_gpio();
ret = request_irqs();
if (ret < 0) return ret;
disable_irqs();
//add by yoyo
dev_t dev=MKDEV(button_major,0);//將主設備號和次設備號定義到一個dev_t數據類型的結構體之中
if(button_major)
result=register_chrdev_region(dev,1,"button");//靜態註冊一個設備,設備號先前指定好,並得到一個設備名,cat /proc/device來查看信息
else
{
result=alloc_chrdev_region(&dev,0,1,"button");//如果主設備號被佔用,則由系統提供一個主設備號給設備驅動程序
button_major=MAJOR(dev);//得到主設備號
}
if(result<0)
{
printk(KERN_WARNING"button:unable to get major %d\n",button_major);
return result;
}
if(button_major==0)
button_major=result;//如果靜態分配失敗。把動態非配的設備號給設備驅動程序
printk(KERN_INFO"button register ok!!!!!!!!!!\n");
led_setup_cdev(&SimpleDevs,0,&button_fops);//初始化和添加結構體struct cdev到系統之中
//return 0;
printk("button initialized.\n");
return 0;
}
/*
*功能: 驅動釋放
*入口:
*
*/
static void __exit button_exit(void)
{
disable_irqs();
free_irqs();
iounmap(gpecon);
iounmap( gpedat);
iounmap(gpfcon);
iounmap(gpfdat);
iounmap(gpgcon);
iounmap(gpgdat);
cdev_del(&SimpleDevs);//刪除結構體struct cdev
printk("button_major=%d\n",button_major);
unregister_chrdev_region(MKDEV(button_major,0),1);//卸載設備驅動所佔有的資源
printk("button device uninstalled\n");
}
module_init(button_init);//初始化設備驅動程序的入口
module_exit(button_exit);//卸載設備驅動程序的入口
應用編程:
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
main()
{
int fd;
char key=0;
fd = open("/dev/button_scan", O_RDWR);//打開設備
if (fd == -1)
{
printf("open device button errr!\n");
return 0;
}
ioctl(fd,0,0); //清空鍵盤緩衝區, 後面兩個參數沒有意義,
while(key != 16)
{
if (read(fd, &key, 1) > 0)//讀鍵盤設備,得到相應的鍵值
{
printf("*********************Key Value = %d*****************************\n", key);
}
}
close(fd);// //關閉設備
return 0;
}
網上資料:
/*
1、阻塞讀取,隊列概念
2、睡眠等待中斷產生
3、定時器產生及相應函數
疑問:去除按鍵緩衝
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-irq.h>
#define KEYSTATUS_DOWNX 2 // 定義按鍵不確定狀態
#define KEYSTATUS_DOWN 0 // 定義按鍵按下後電平
#define KEYSTATUS_UP 1 // 定義按鍵擡起後電平
#define BUF_CLEAR _IO(0xFF, 0) // 清除鍵盤緩衝區命令
#define DEVICE_NAME "utukey"
#define MAX_KEY_BUF 16 // 按鍵緩衝區大小
#define KEY_NUM 6 // 按鍵個數
#define BUF_HEAD (utukey_dev.buf[utukey_dev.head]) //緩衝頭
#define BUF_TAIL (utukey_dev.buf[utukey_dev.tail]) //緩衝尾
#define ISKEY_DOWN(key) (s3c2410_gpio_getpin(key_info_tab[key].gpio_port) == KEYSTATUS_DOWN)
#define INCBUF(x,mod) ((++(x))&((mod)-1))
#define KEY_TIME_DELAY (HZ/10) // 100ms
#define KEY_TIME_DELAY1 (HZ/100) // 10ms
#define UTUKEY_MAJOR 0 // 定義0使用自動分配設備號
unsigned int utukey_major = UTUKEY_MAJOR;
/* 定義按鍵設備結構體 */
struct utukey_dev
{
struct cdev cdev; // cdev結構體
unsigned int key_status[KEY_NUM]; // 記錄按鍵狀態
unsigned int buf[MAX_KEY_BUF]; // 按鍵環形緩衝區
unsigned int head, tail; // 按鍵緩衝區頭和尾
wait_queue_head_t wq; // 等待隊列
};
struct utukey_dev utukey_dev; // 定義設備結構體
struct utukey_dev *utukey_devp; // 定義設備結構體指針
struct timer_list key_timer[KEY_NUM]; // 定義6個按鍵去抖動定時器
static struct key_info // 定義按鍵所用資源結構體
{
int irq_no; // 佔用的中斷號
int irq_type; // 中斷類型
unsigned int gpio_port; // 佔用的引腳
unsigned int gpio_setting; // 引腳設置值
int key_code; // 按鍵值
char *name; // 按鍵的對應字符串
}key_info_tab[] =
{
{
IRQ_EINT11, IRQT_FALLING, S3C2410_GPG3, S3C2410_GPG3_INP, 1, "Key Up" //下降沿觸發
},
{
IRQ_EINT0, IRQT_FALLING, S3C2410_GPF0, S3C2410_GPF0_INP, 2, "Key Down"
},
{
IRQ_EINT19, IRQT_FALLING, S3C2410_GPG11,S3C2410_GPG11_INP, 3, "Key Left"
},
{
IRQ_EINT2, IRQT_FALLING, S3C2410_GPF2,S3C2410_GPF2_INP, 4, "Key Right"
},
{
IRQ_EINT6, IRQT_FALLING, S3C2410_GPF6,S3C2410_GPF6_INP, 5, "Key Enter"
},
{
IRQ_EINT5, IRQT_FALLING, S3C2410_GPF5,S3C2410_GPF5_INP, 6, "Key Exit"
},
};
/* 按鍵中斷服務程序 */
static irqreturn_t utukey_irq(int irq, void *dev_id)
{
int key = (int)dev_id; // 傳遞key_info_tab[]索引
int i;
int found = 0;
for (i = 0; i < ARRAY_SIZE(key_info_tab); i++) // 查找產生中斷的按鍵
{
if (key_info_tab[i].irq_no == irq)
{
found = 1;
break;
}
}
if (!found) // 沒找到
{
printk(KERN_NOTICE"bad irq %d in button\n", irq);
return IRQ_NONE; //錯誤的中斷
}
disable_irq(key_info_tab[key].irq_no); // 找到,關閉對應中斷
utukey_dev.key_status[key] = KEYSTATUS_DOWNX; // 按鍵處於不確定狀態
key_timer[key].expires = jiffies + KEY_TIME_DELAY1; // 去抖動延時
add_timer(&key_timer[key]); //add_timer
return IRQ_HANDLED; //正確的中斷
}
/* 申請irq中斷 */
static int request_irqs(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)
{
s3c2410_gpio_cfgpin(key_info_tab[i].gpio_port, key_info_tab[i].gpio_setting); // 設置按鍵引腳的模式(輸入)
set_irq_type(key_info_tab[i].irq_no, key_info_tab[i].irq_type); // 設置中斷類型
if (request_irq(key_info_tab[i].irq_no, utukey_irq, SA_INTERRUPT, DEVICE_NAME, (void *)i)) // 向內核註冊中斷
{
printk(KERN_WARNING "buttons:can't get irq no.%d\n", key_info_tab[i].irq_no);
return 1;
}
}
return 0;
}
/* 釋放irq中斷 */
static void free_irqs(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(key_info_tab); i++)
{
free_irq(key_info_tab[i].irq_no, (void *)i);
}
}
/* 記錄鍵值並喚醒等待隊列 */
static void keyEvent(unsigned key)
{
BUF_HEAD = key_info_tab[key].key_code; // 記錄鍵值
utukey_dev.head = INCBUF(utukey_dev.head, MAX_KEY_BUF); // 調整緩衝區頭指針
wake_up_interruptible(&(utukey_dev.wq)); // 喚醒等待隊列
}
/* 驅動讀函數 */
static ssize_t utukey_read(struct file *filp,char __user *buffer, size_t count, loff_t *ppos)
{
unsigned int key_ret;
unsigned long flags;
retry:
if (utukey_dev.head != utukey_dev.tail) // 緩衝區有數據?
{
local_irq_save(flags); // 進入臨界區 ,關中斷
key_ret = BUF_TAIL; // 讀出鍵值
utukey_dev.tail = INCBUF(utukey_dev.tail, MAX_KEY_BUF); // 調整緩衝區尾指針
local_irq_restore(flags); // 退出臨界區,開中斷
copy_to_user(buffer, &key_ret, sizeof(unsigned int)); // 拷貝到用戶空間
return sizeof(unsigned int);
}else // 緩衝區沒數據
{
if (filp->f_flags & O_NONBLOCK) // 若採用非阻塞方式讀取則返回錯誤
{
return -EAGAIN;
}
interruptible_sleep_on(&(utukey_dev.wq)); // 使進程睡眠
if (signal_pending(current)) //在這裏等中斷
{ // 如果是信號中斷
return -ERESTARTSYS;
}
goto retry;
}
return sizeof(unsigned int);
}
/* ioctl設備控制函數 */
static int utukey_ioctl(struct inode *inodep, struct file *filp, unsigned
int cmd, unsigned long arg)
{
unsigned long flags;
switch (cmd)
{
case BUF_CLEAR: // 清除按鍵緩衝區
local_irq_save(flags);
utukey_dev.head = utukey_dev.tail = 0;
local_irq_restore(flags);
printk(KERN_INFO "key buffer is cleared\n");
break;
default:
return -EINVAL;
}
return 0;
}
/* 定時器中斷回調函數 */
static void utukey_timer_callback(unsigned long data) //定時器時間到了調用 參數代表哪個定時器
{
int key = data;
if (ISKEY_DOWN(key)) // 按鍵處於按下狀態?
{
if (utukey_dev.key_status[key] == KEYSTATUS_DOWNX) // 已經延時10ms,完成去抖動?
{
utukey_dev.key_status[key] = KEYSTATUS_DOWN;
key_timer[key].expires = jiffies + KEY_TIME_DELAY;
keyEvent(key); // 記錄鍵值,喚醒等待隊列
add_timer(&key_timer[key]); //擡起按鍵去抖動延時
}else
{ // 已經完成去抖動延時
key_timer[key].expires = jiffies + KEY_TIME_DELAY;
add_timer(&key_timer[key]);
}
}else
{ // 按鍵已經擡起
utukey_dev.key_status[key] = KEYSTATUS_UP;
enable_irq(key_info_tab[key].irq_no);
}
}
static int utukey_open(struct inode *inode, struct file *filp)
{
printk(KERN_NOTICE "utukey opened\n");
return 0;
}
static int utukey_release(struct inode *inode, struct file *filp)
{
printk(KERN_NOTICE "utukey released\n");
return 0;
}
static const struct file_operations utukey_fops =
{
.owner = THIS_MODULE,
.read = utukey_read,
.ioctl = utukey_ioctl,
.open = utukey_open,
.release = utukey_release,
};
/* 初始化並註冊cdev */
static void utukey_setup_cdev(void)
{
int err,devno = MKDEV(utukey_major,0);
cdev_init(&utukey_dev.cdev,&utukey_fops);
utukey_dev.cdev.owner = THIS_MODULE;
utukey_dev.cdev.ops = &utukey_fops;
err = cdev_add(&utukey_dev.cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding utukey", err);
}
/* 初始化函數 */
static int __init utukey_init(void)
{
int result, i;
dev_t devno = MKDEV(utukey_major,0); // 用主次設備號生成設備號
/* 申請中斷 */
result = request_irqs();
if (result) {
unregister_chrdev_region(devno,1);
return result;
}
/* 申請設備號 */
if (utukey_major)
result = register_chrdev_region(devno, 1, DEVICE_NAME);
else /* 動態申請設備號 */
{
result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
utukey_major = MAJOR(devno);
printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, utukey_major);
}
if (result < 0)
return result;
/* 動態申請設備結構體的內存 */
utukey_devp = kmalloc(sizeof(struct utukey_dev), GFP_KERNEL);
if (!utukey_devp) /* 申請失敗 */
{
result = -ENOMEM;
goto fail_malloc;
}
memset(utukey_devp, 0, sizeof(struct utukey_dev)); /* 清零分配的設備結構體內存 */
utukey_setup_cdev();
init_waitqueue_head(&(utukey_dev.wq)); /* 初始化等待隊列頭 */
utukey_dev.head = utukey_dev.tail = 0;
/* 初始化按鍵狀態 */
for(i = 0; i < KEY_NUM; i++)
{
utukey_dev.key_status[i] = KEYSTATUS_UP;
}
/* 初始化定時器 */
for(i = 0; i < KEY_NUM; i++)
{
key_timer[i].function = utukey_timer_callback;
key_timer[i].data = i;
init_timer(&key_timer[i]);
}
return 0;
fail_malloc: unregister_chrdev_region(devno, 1);
return result;
}
/* 退出函數 */
static void __exit utukey_exit(void)
{
int i;
cdev_del(&utukey_dev.cdev); // 註銷cdev
kfree(utukey_devp); // 釋放結構體內存
unregister_chrdev_region(MKDEV(utukey_major, 0), 1); // 釋放設備號
free_irqs(); // 釋放中斷
for(i = 0; i < KEY_NUM; i++) // 註銷定時器
{
del_timer(&key_timer[i]);
}
}
MODULE_AUTHOR("lxm<[email protected]>");
MODULE_LICENSE("Dual BSD/GPL");
module_init(utukey_init);
module_exit(utukey_exit);
//測試程序
#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>
#define BUF_CLEAR _IO(0xFF, 0)
int main(void)
{
int buttons_fd;
int key_value;
buttons_fd = open("/dev/utukey", 0);
if (buttons_fd < 0) {
perror("cann't open device /dev/utukey");
exit(1);
}
else
{
if (ioctl(buttons_fd, BUF_CLEAR, 0) < 0)
printf(" ioctl command failed\n");
}
while(1)
{
int ret = read(buttons_fd, &key_value, sizeof key_value);
printf("You pressed buttons %d\n", key_value);
}
close(buttons_fd);
return 0;
}