歡迎您光臨本站 註冊首頁

linux下寫驅動的簡單流程

←手機掃碼閱讀     火星人 @ 2014-03-26 , reply:0

這是2410板子上的按鍵的驅動程序,我在代碼裡面標上看代碼的順序(1、2、3...)和說明。程序比較簡單
只是入門,沒有涉及到中斷、併發等等的東西。這只是一篇代碼,在另一篇日記中要記下怎麼編譯和運用。

/*****************************
key_test.c
****************************/
/*#ifndef __KERNEL__
#define __KERNEL__
#endif
#ifndef MODULE
#define MODULE
#endif
*/
/* 以上語句是用來判斷驅動是直接編譯到內核裡面,還是以模塊形式來調用的。驅動程序要在Kconfig
文件裡面定義了,才能出現在make menuconfig的菜單中,到時可選擇是作為模塊還是直接編譯進內核。*/
#include
#include
#include
#include
#include
#include "def.h" //定義了按鍵的地址值和數據類型等。
//以上是頭文件,開始看代碼,找到最後面的----(1),從那看起。

static loff_t key_test_llseek(struct file *filp,loff_t off, int whence);
static ssize_t key_test_read(struct file *filp,char *buf, size_t count,loff_t *f_pos);
static ssize_t key_test_write(struct file *filp,const char *buf,size_t count,loff_t *f_pos);
static int key_test_open(struct inode *inode, struct file *filp);
static int key_test_release(struct inode *inode, struct file *filp);
static int key_test_ioctl(struct inode *inode,struct file *filp, unsigned int cmd, unsigned long param);
int key_test_init(void);
void key_test_cleanup(void);
#define MAJOR_NR 121 //這個定義的主設備號,在linux下用ls -l看得到(在日期項前)
#define DEVICE_NAME "key_test_" //設備名稱,出理在/dev/char目錄下
//#define DEVICE_NR(device) MINOR(device)
//#define DEVICE_NO_RANDOM
//#define DEVICE_OFF(d)

//這裡是-----(7),這裡的內容屬於介面與技術這門課的內容了
U8 Key_Scan( void )
{
Delay( 50 ) ;
rGPGDAT = rGPGDAT & (~((1<<6)|(1<<2))) | (1<<6) | (0<<2) ; //GPG6,2 output 0
rGPEDAT = rGPEDAT & (~((1<<13)|(1<<11))) | (1<<13) | (1<<11) ; //GPE13,11 output 0
if( (rGPFDAT&(1<< 0)) == 0 ) return 16 ;
else if( (rGPFDAT&(1<< 2)) == 0 ) return 15 ;
else if( (rGPGDAT&(1<< 3)) == 0 ) return 14 ;
else if( (rGPGDAT&(1<<11)) == 0 ) return 13 ;
rGPGDAT = rGPGDAT & (~((1<<6)|(1<<2))) | (0<<6) | (1<<2) ; //GPG6,2 output 0
rGPEDAT = rGPEDAT & (~((1<<13)|(1<<11))) | (1<<13) | (1<<11) ; //GPE13,11 output 0
if( (rGPFDAT&(1<< 0)) == 0 ) return 11 ;
else if( (rGPFDAT&(1<< 2)) == 0 ) return 8 ;
else if( (rGPGDAT&(1<< 3)) == 0 ) return 5 ;
else if( (rGPGDAT&(1<<11)) == 0 ) return 2 ;
rGPGDAT = rGPGDAT & (~((1<<6)|(1<<2))) | (1<<6) | (1<<2) ; //GPG6,2 output 0
rGPEDAT = rGPEDAT & (~((1<<13)|(1<<11))) | (1<<13) | (0<<11) ; //GPE13,11 output 0
if( (rGPFDAT&(1<< 0)) == 0 ) return 10 ;
else if( (rGPFDAT&(1<< 2)) == 0 ) return 7 ;
else if( (rGPGDAT&(1<< 3)) == 0 ) return 4 ;
else if( (rGPGDAT&(1<<11)) == 0 ) return 1 ;
rGPGDAT = rGPGDAT & (~((1<<6)|(1<<2))) | (1<<6) | (1<<2) ; //GPG6,2 output 0
rGPEDAT = rGPEDAT & (~((1<<13)|(1<<11))) | (0<<13) | (1<<11) ; //GPE13,11 output 0
if( (rGPFDAT&(1<< 0)) == 0 ) return 12 ;
else if( (rGPFDAT&(1<< 2)) == 0 ) return 9 ;
else if( (rGPGDAT&(1<< 3)) == 0 ) return 6 ;
else if( (rGPGDAT&(1<<11)) == 0 ) return 3 ;
else return 0xff ;
}

static void __irq KeyISR(void)
{
U8 key ;
rGPGCON = rGPGCON & (~((3<<22)|(3<<6))) | ((0<<22)|(0<<6)) ; //GPG11,3 set input
rGPFCON = rGPFCON & (~((3<<4)|(3<<0))) | ((0<<4)|(0<<0)) ; //GPF2,0 set input

if(rINTPND==BIT_EINT8_23)
{
ClearPending(BIT_EINT8_23);
if(rEINTPEND&(1<<11))
{
//puts("Interrupt eint11 occur...");
rEINTPEND |= 1<< 11;
}

if(rEINTPEND&(1<<19))
{
//puts("Interrupt eint19 occur...");
rEINTPEND |= 1<< 19;
}
}

else if(rINTPND==BIT_EINT0)
{
//puts("Interrupt eint0 occur...");
ClearPending(BIT_EINT0);
}

else if(rINTPND==BIT_EINT2)
{
//puts("Interrupt eint2 occur...");
ClearPending(BIT_EINT2);
}
//這裡是-----(6),這裡(5)裡面實現函數時調用的函數的內容
//查詢按鍵鍵值
key = Key_Scan() ;
if( key != 0xff )
printf( "Interrupt occur... K%d is pressed!\n", key ) ;
//Beep( 2000, 3000 ) ;
//重新初始化IO口
rGPGCON = rGPGCON & (~((3<<12)|(3<<4))) | ((1<<12)|(1<<4)) ; //GPG6,2 set output
rGPGDAT = rGPGDAT & (~((1<<6)|(1<<2))); //GPG6,2 output 0

rGPECON = rGPECON & (~((3<<26)|(3<<22))) | ((1<<26)|(1<<22)); //GPE13,11 set output
rGPEDAT = rGPEDAT & (~((1<<13)|(1<<11))); //GPE13,11 output 0

rGPGCON = rGPGCON & (~((3<<22)|(3<<6))) | ((2<<22)|(2<<6)) ; //GPG11,3 set EINT
rGPFCON = rGPFCON & (~((3<<4)|(3<<0))) | ((2<<4)|(2<<0)) ; //GPF2,0 set EINT
}
//這裡是-----(5),這裡(4)裡面實現KeyScanInit函數時調用的函數的內容
void KeyScanInit(void)
{
rGPGCON = rGPGCON & (~((3<<12)|(3<<4))) | ((1<<12)|(1<<4)) ; //GPG6,2 set output
rGPGDAT = rGPGDAT & (~((1<<6)|(1<<2))); //GPG6,2 output 0

rGPECON = rGPECON & (~((3<<26)|(3<<22))) | ((1<<26)|(1<<22)); //GPE13,11 set output
rGPEDAT = rGPEDAT & (~((1<<13)|(1<<11))); //GPE13,11 output 0

rGPGCON = rGPGCON & (~((3<<22)|(3<<6))) | ((2<<22)|(2<<6)) ; //GPG11,3 set EINT
rGPFCON = rGPFCON & (~((3<<4)|(3<<0))) | ((2<<4)|(2<<0)) ; //GPF2,0 set EINT

rEXTINT0 &= ~(7|(7<<8));
rEXTINT0 |= (2|(2<<8)); //set eint0,2 falling edge int
rEXTINT1 &= ~(7<<12);
rEXTINT1 |= (2<<12); //set eint11 falling edge int
rEXTINT2 &= ~(0xf<<12);
rEXTINT2 |= (2<<12); //set eint19 falling edge int
rEINTPEND |= (1<<11)|(1<<19); //clear eint 11,19
rEINTMASK &= ~((1<<11)|(1<<19)); //enable eint11,19
ClearPending(BIT_EINT0|BIT_EINT2|BIT_EINT8_23);
pISR_EINT0 = pISR_EINT2 = pISR_EINT8_23 = (U32)KeyISR;
EnableIrq(BIT_EINT0|BIT_EINT2|BIT_EINT8_23);
}
//這裡是-----(4),這裡(3)裡面實現open函數時調用的函數的內容
void Key_Scan_Test( void )
{

Uart_Printf( "\n8X2 KEY array TEST ( Interrupt MODE )\n" );
Uart_Printf( "Press 'ESC' key to Exit this program !\n\n" );
KeyScanInit() ;//鍵值
while( Uart_GetKey() != ESC_KEY ) ;
rGPGCON = rGPGCON & (~((3<<22)|(3<<6))) | ((0<<22)|(0<<6)) ; //GPG11,3 set input
rGPFCON = rGPFCON & (~((3<<4)|(3<<0))) | ((0<<4)|(0<<0)) ; //GPF2,0 set input
}

//這裡是-----(3),這裡file_operations結構的內容
static struct file_operations key_test_fops=
{
llseek: key_test_llseek, //llseek函數;
read:key_test_read, //read函數;
write:key_test_write, //write函數
open: key_test_open, //open函數
release:key_test_release, //....
ioctl: key_test_ioctl, // ....這個在CD-ROM裡面就是什麼彈出,關倉等等的控制了
};
//上面的每一個欄位就指向下面的每一個相應的函數,這些函數就是linux編程時調用的函數(介面)。

static loff_t key_test_llseek(struct file *filp,loff_t off, int whence)
{
printk(KERN_DEBUG"Function llseek...\n");
return 0;
}
static int key_test_read(struct file *filp,char *buf, size_t count,loff_t *f_pos)
{
printk(KERN_DEBUG"Device is reading...\n");
return 0;
}
//上面五句就是實現read函數的具體語句了,下面的都類似,不過真正寫時不會這麼簡單吧。
static int key_test_write(struct file *filp,char *buf, size_t count,loff_t *f_pos)
{
printk(KERN_DEBUG"Device is writting...\n");
return 0;
}
static int key_test_open(struct inode *inode, struct file *filp)
{
printk(KERN_DEBUG"Device is opening...\n");
Key_Scan_Test(); //這是檢測你按的是哪個鍵的函數
return 0;
//這個函數的參數的*filp就是我們平時調用open()得到的描述符的結構了,inode則表示文件描述符指向
的是哪個文件,因為一個文件可能會有許多個描述符打開。
}
static int key_test_release(struct inode *inode, struct file *filp)
{
printk(KERN_DEBUG"Releasing this device...\n");
return 0;
}
static int key_test_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long arg)
{
return 0;
}
//這裡是-----(2),這裡寫的是初始化和移除模塊裡面的內容
int key_test_init(void)
{
int result;
result=register_chrdev(MAJOR_NR,DEVICE_NAME,&key_test_fops);
/*上面這句是註冊設備號和設備名稱到內核,它們會出現在/proc和sysfs文件夾中。和一個
file_operations結構,裡面的每個欄位就是指向一個實現驅動的一個函數。*/
if(result<0)
{
printk(KERN_ERR DEVICE_NAME":get major %d wrong\n",MAJOR_NR);
return(result);
} //當返回結果不為0時,表示初始化失敗
printk("Device KEY initialized OK\n");
return 0;
}

void key_test_cleanup(void)
{
unregister_blkdev(MAJOR_NR,DEVICE_NAME);//釋放設備號和設備名
}
//從這裡開始看起,這是-----(1)
module_init(key_test_init); //這是模塊初始化,也就是被內核裝載時調用,記住參數名是什麼?
module_exit(key_test_cleanup); //驅動模塊被移除時調用
MODULE_AUTHOR("aaaaa"); //作者名字
MODULE_DESCRIPTION("SIMPLE Character"); //描述一下這是什麼驅動
MODULE_LICENSE("dual BSD/GPL"); //遵循BSD/GPL協議,沒有這句的話可能內核會產生抱怨.

[火星人 ] linux下寫驅動的簡單流程已經有672次圍觀

http://coctec.com/docs/linux/show-post-189036.html