按照實例一,實現了從應用程序空間向內核空間傳遞數據,這一例實現從內核讀取按鍵值到應用空間,然後把剛剛保存在應用空間按鍵值寫到內核空間,內核空間按鍵值來操作對應的LED
驅動源碼:keys_leds.c
驅動源碼Makefile
測試源碼: keys_leds_test.c
keys_leds.c:
/***************************************************************
* filename: keys_leds.c
* description: 無按鍵按下,熄滅全部LED,按鍵按下,點亮相應LED,鬆開熄滅相應LED
* author: xyc
* create time: 2014/6/2
* version:1
* modify info:
******************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <asm-arm/io.h>
#include <asm-arm/uaccess.h>
static int KEYS_LEDS_MAJOR;
static struct class *keys_leds;
static struct class_device *keys_leds_dev;
static volatile unsigned long *gpfcon=NULL;
static volatile unsigned long *gpfdat=NULL;
static int keys_leds_open(struct inode *inode, struct file *file)
{
/*配置按鍵爲輸入,LED爲輸出*/
*gpfcon &= ~(0x3<<(4*2)) & (~(0x3<<(0*2))) & (~(0x3<<(5*2)) ) & (~(0x3<<(2*2)));
*gpfcon |= 0x1<<(4*2) |(0x0<<(0*2)) | (0x1<<(5*2) )|(0x0<<(2*2));
*gpfdat |=1<<4|1<<5;
return 0;
}
static ssize_t keys_leds_read(struct file *file, char __user *userbuf, size_t count, loff_t *off)
{
char val =0;//初始化爲0不要忘
/*記錄按下的鍵*/
if( !(*gpfdat & (1<<0)) )
{
/*s2按下*/
val =1;
}
if (!(*gpfdat & (1<<2)))
{
/*s3按下*/
val =2;
}
copy_to_user(userbuf, &val, 1);
return 1;
}
static ssize_t keys_leds_write(struct file * file, const char __user * userbuf,
size_t count, loff_t * off)
{
char val=0; //初始化爲0,不要忘
/*
*按鍵沒按下keys_leds_read傳給應用read的按鍵值爲0,
*應用write將按鍵值0傳給 keys_leds_write的val,LED均熄滅
*/
/*
*按鍵按下,keys_leds_read傳給應用read的按鍵值爲1或2,
*應用write將按鍵值1或2傳給 keys_leds_write的val相應的LED亮
*/
copy_from_user(&val, userbuf, 1);
switch(val)
{
case 1:
{
/*點亮LED4*/
*gpfdat &=~(1<<4);
break;
}
case 2:
{ /*點亮LED5*/
*gpfdat &=~(1<<5);
break;
}
default:
{
/*倆個按鍵均沒按下,此時read()讀到的值爲0,
*再將0寫入到驅動keys_leds_write()函數的自動變量val
*/
*gpfdat |= (1<<4)|(1<<5); //不要忘記沒按鍵或按鍵鬆開時滅燈
break;
}
}
}
static struct file_operations keys_leds_op = {
.owner = THIS_MODULE,
.open = keys_leds_open,
.read = keys_leds_read,
.write = keys_leds_write,
};
static int __init keys_leds_init()
{
int minor;
KEYS_LEDS_MAJOR = register_chrdev(0, "keys_leds", &keys_leds_op);
if(KEYS_LEDS_MAJOR <0){
printk("register char device failed\n");
return KEYS_LEDS_MAJOR;
}
keys_leds = class_create(THIS_MODULE, "keys_leds");
keys_leds_dev =class_device_create(keys_leds, NULL, MKDEV(KEYS_LEDS_MAJOR, 0), NULL, "leds");
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon +1;
return 0;
}
static void __exit keys_leds_exit()
{
unregister_chrdev(KEYS_LEDS_MAJOR, keys_leds);
class_device_destroy(keys_leds, MKDEV(KEYS_LEDS_MAJOR, 0));
class_destroy(keys_leds);
iounmap(gpfcon);
}
module_init(keys_leds_init);
module_exit(keys_leds_exit);
MODULE_LICENSE("GPL");
程序編寫過程中,編譯出現的錯誤:
1.keys_leds_op後面忘掉等號,2.將按鍵驅動分爲幾個設備節點,沒編譯之前發現,但static struct class_device *keys_leds_dev[3]沒有改過來3.頭文件不記得寫了
4.keys_leds_read/keys_leds_write中val忘記初始化爲0,keys_leds_write對val = 0即沒有按鍵按下或按鍵鬆開時進行滅燈處理
Makefile:
跟實例一差不多
KERN_DIR = /work/system/linux-2.6.22.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-m += keys_leds.o
這裏obj-m表示以模塊動態加載的方式啓用,並沒有編入內核,所以開發板斷電重啓後,斷電前的模塊已經卸載掉了,同理對應的在模塊中創建的設備文件也不存在了
測試模塊keys_leds_test.c:
/***************************************************************
* filename: keys_leds_test.c
* description: 測試keys_leds.c驅動
* author: xyc
* create time: 2014/6/2
* version: 1
* modify info:
****************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int fd1;
char key_value=0;
if(argc !=2 ){
printf("Usage:\n");
printf("%s <dev> <on|off>\n",argv[0]);
printf("eg. \n");
printf("%s /dev/leds \n", argv[0]);
return -1;
}
fd1 = open(argv[1], O_RDWR);
if( fd1<0){
printf("%s open failed", argv[1]);
return -1;
}
while(1){
read(fd1, &key_value, 1);
if( (key_value==1) |(key_value==2) )
write(fd1, &key_value, 1);
else
write(fd1, &key_value, 1);
}
close(fd1);
return 0;
}
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
這3個頭文件,導致O_RDWR未定義,此時因爲O_RDWR是open使用的,這時在ubuntu下man 2 open看其需要包含的頭文件爲
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
還有可能忘了的對key_value==1或2調用的write將按鍵值傳給內核空間,但沒按下時的按鍵值key_value==0沒調用write即沒把無按鍵按下的值傳給內核空間,導致鬆開按鍵後,LED燈不熄滅,因爲程序不斷read ->第一個write寫1或2->read->第一個write寫1或2... 在第一個write寫1或2都是點亮LED4 或LED5,這樣按下S2 LED4,LED4亮,鬆開S2 LED4不滅,所以需要對驅動沒有讀到按鍵值時在測試程序中做判斷,然後將無按鍵的值0傳給驅動keys_leds_write中的臨時變量val使得在switch(0)中關掉LED4 LED5
,
測試看按鍵按下,相應的LED燈是否亮,鬆開,相應的LED燈黑,同時按下的反應
本驅動代碼因爲是在測試代碼中輪詢按鍵,所以CPU佔用率幾乎達到了100%,所以實用中均是用的中斷方式
Mem: 6696K used, 54488K free, 0K shrd, 0K buff, 2068K cached
CPU: 8% usr 91% sys 0% nice 0% idle 0% io 0% irq 0% softirq
Load average: 0.99 0.96 0.84
PID PPID USER STAT VSZ %MEM %CPU COMMAND
793 770 0 R 1308 2% 99% ./keys_leds_test /dev/leds
806 770 0 R 3096 5% 0% top
770 1 0 S 3096 5% 0% -sh
1 0 0 S 3092 5% 0% init
762 2 0 SW< 0 0% 0% [rpciod/0]
6 2 0 SW< 0 0% 0% [khelper]
745 2 0 SW< 0 0% 0% [kmmcd]
2 0 0 SW< 0 0% 0% [kthreadd]
3 2 0 SWN 0 0% 0% [ksoftirqd/0]
4 2 0 SW< 0 0% 0% [watchdog/0]
5 2 0 SW< 0 0% 0% [events/0]
55 2 0 SW< 0 0% 0% [kblockd/0]
56 2 0 SW< 0 0% 0% [ksuspend_usbd]
59 2 0 SW< 0 0% 0% [khubd]
61 2 0 SW< 0 0% 0% [kseriod]
73 2 0 SW 0 0% 0% [pdflush]
74 2 0 SW 0 0% 0% [pdflush]
75 2 0 SW< 0 0% 0% [kswapd0]
76 2 0 SW< 0 0% 0% [aio/0]
nfs: server 192.168.1.19 not responding, still trying