linux下的GPIO驅動

編寫驅動程序,首先要了解是什麼類型的設備。linux下的設備分爲三類,分別爲:字符設備,塊設備和網絡設備。字符設備類型是根據是否以字符流爲數據的交換方式,大部分設備都是字符設備,如鍵盤,串口等,塊設備則是以塊爲單位進行管理的設備,如,磁盤。網絡設備就是網卡等。

其次要了解應用程序和驅動程序的區別,兩者的主要區別分爲以下三點:

1入口函數的任務不相同,應用程序完成一個任務,驅動只完成初始化工作,比如中斷

      申請,寄存器設置,定時器設置。

2運行時的cpu模式不相同,驅動具有很高的權限,應用程序是在用戶態下運行,而驅

  動程序是在內核態下執行。

3 驅動程序不能調用C庫函數,內核爲驅動程序提供一些函數。如printk(KERN_NOTICE fmt, ##arg),第一個參數爲打印級別,有如下的打印級別:

KERN_EMERG 用於緊急事件,一般是系統崩潰前的提示信息

KERN_ALERT 用於需要立即採取動作的場合

KERN_CRIT 臨界狀態,通常設計驗證的硬件或軟件操作失敗

KERN_ERR 用於報告錯誤狀態.設備驅動程序通常會用它報告來自硬件的問題

KERN_WARNING 就可能出現的問題提出警告.這些問題通常不會對系統造成嚴重破壞

KERN_NOTICE 有必要提示的正常情況.許多安全相關的情況用這個級別彙報

KERN_INFO 提示性信息.有很多驅動程序在啓動時用這個級別打印相關信息

KERN_DEBUG 用於調試的信息

u_long copy_from_user(void *to, const void *from, u_long len),由用戶態拷貝到內核態;

u_long copy_to_user(void * to, const void *from, u_long len),由內核態拷貝到用戶態。

鑑於以上區別,驅動程序需要完成以下三點基本功能:

1:要對設備進行初始化和釋放功能模塊,就如上面的寄存器設置,中斷的申請,向內核注 

   冊驅動程序(register_chrdev()),卸載驅動程序(unregister_chrdev())。

2:能進行數據傳輸,在read(),write()函數裏具體實現,數據傳輸工作。

3:能進行控制操作,給用戶提供的ioctl()函數裏可實現一些用戶的選擇性設置功能。

確定一個設備的執行函數集(結構體)

static struct file_operations myGPIO_fops = {

owner: THIS_MODULE,

write: myGPIO_write,

read: myGPIO_read,

ioctl: myGPIO_ioctl,

open: myGPIO_open,

release: myGPIO_release,

};

接下來是初始化工作,需要寫在一個init()函數中,這個函數是獨立的也是自動執行的,在這之中主要是對一些寄存器進行初始化操作。同樣需要完成卸載驅動模塊。

myGPIO_Major = register_chrdev(0, DRIVER_NAME, &myDriver_fops);

上面的程序完成設備號的註冊,第一個參數爲主設備號,一般爲0,由系統來分配。

第二個參數爲設備名,這需要在/dev/(/dev目錄下設備名由命令 <mknod  設備名 C 主設備號  從設備號>來生成)目錄下出現的設備名相符合。相反的在卸載中就取消註冊

unregister_chrdev(myGPIO_Major, DRIVER_NAME);

最後將這兩個模塊加入到內核中,由程序段的最後兩行完成。

static int __init myGPIO_init(void)

{

PRINTK("GPIO init/n");

myGPIO_Major = register_chrdev(0, DRIVER_NAME, &myGPIO_fops);

if(myGPIO_Major < 0)

{

PRINTK("register char device fail!/n");

return myGPIO_Major;

}

PRINTK("register myGPIO OK! Major = %d/n", myGPIO_Major);

#ifdef CONFIG_DEVFS_FS

devfs_myDriver_dir = devfs_mk_dir(NULL, "GPIO", NULL);

devfs_myDriver_raw = devfs_register(devfs_myDriver_dir, "raw0", DEVFS_FL_DEFAULT, myGPIO_Major, 0, S_IFCHR | S_IRUSR | S_IWUSR, &myGPIO_fops, NULL);

PRINTK("add dev file to devfs OK!/n");

#endif

return 0;

}

static void __exit myGPIO_exit(void)

{

/* Module exit code */

PRINTK("GPIO exit/n");

/* Driver unregister */

if(myGPIO_Major > 0)

{

#ifdef CONFIG_DEVFS_FS

devfs_unregister(devfs_myDriver_raw);

devfs_unregister(devfs_myDriver_dir);

#endif

unregister_chrdev(myGPIO_Major, DRIVER_NAME);

}

return;

}

MODULE_AUTHOR("LiuFan");

MODULE_LICENSE("Dual BSD/GPL");

module_init(myGPIO_init);

module_exit(myGPIO_exit);

設備執行函數功能的實現將在下面完成。如結構體的函數,但並不是全都需要實現。open()函數中是執行一些設備工作前的初始化工作。rlease()則是將設備的相關寄存器恢復到原來的值。read()函數是將設備中的數據拷貝到內核,write()函數是將內核數據拷貝到對應的設備中。MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT兩個宏是提供給系統對硬件資源進行控制訪問的。在open()和rlease()兩個函數中最基本的操作應是實現以上兩個宏的操作。

static unsigned char myGPIO_Buffer[1024*1024];

/* Driver Operation Functions */

static int myGPIO_open(struct inode *inode, struct file *filp)

{

// int Minor = MINOR(inode->i_rdev);

// filp->private_data = 0;

MOD_INC_USE_COUNT;

PRINTK("myDriver open called!/n");

return 0;

}

static int myGPIO_release(struct inode *inode, struct file *filp)

{

// int Minor = MINOR(inode->i_rdev);

MOD_DEC_USE_COUNT;

PRINTK("myDriver release called!/n");

return 0;

}

static ssize_t myGPIO_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)

{

char dat;

size_t read_size = count;

PRINTK("GPIO read called!/n");

PRINTK("/tcount=%d, pos=%d/n", count, (int)*f_pos);

/* if(*f_pos >= sizeof(myGPIO_Buffer))

{

PRINTK("[GPIO read]Buffer Overlap/n");

*f_pos = sizeof(myGPIO_Buffer);

return 0;

}

if((count + *f_pos) > sizeof(myGPIO_Buffer))

{

PRINTK("count + f_pos > sizeof buffer/n");

read_size = sizeof(myGPIO_Buffer) - *f_pos;

}*/

dat= GPFDAT;

copy_to_user(buf,&dat,1);

// *f_pos += read_size;

return read_size;

}

static ssize_t myGPIO_write(struct file *filp,const char *buf, size_t count, loff_t *f_pos)

{

char dat;

size_t fill_size = count;

PRINTK("myDriver write called!/n");

PRINTK("/tcount=%d, pos=%d/n", count, (int)*f_pos);

if(*f_pos >= sizeof(myGPIO_Buffer))

{

PRINTK("[myDriver write]Buffer Overlap/n");

*f_pos = sizeof(myGPIO_Buffer);

return 0;

}

if((count + *f_pos) > sizeof(myGPIO_Buffer))

{

PRINTK("count + f_pos > sizeof buffer/n");

fill_size = sizeof(myGPIO_Buffer) - *f_pos;

}

copy_from_user(&dat,buf,fill_size);

GPFDAT = dat;

// *f_pos += fill_size;

return fill_size;

}

控制ioctl() 函數則是提供給應用層的接口函數,功能並不是固定的,由開發者定義,一般都是對硬件的一些除過上述功能的其他操作。

static int myGPIO_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

int i;

unsigned int mask=0x01;

GPFUP = 0x00;

PRINTK("myGPIO ioctl called(%d)!/n", cmd);

    switch(cmd)

{

case MOD_IN:

for(i=0;i<8;i++)

{

if((mask & arg)!=0x0)

{

GPFCON &=~(3<<i*2); 

}

mask =mask << 1;

}

break;

case MOD_OUT:

 PRINTK("IOCTRL 0 called(0x%lx)!/n", arg);

 for(i=0;i<8;i++)

 {

 if((mask & arg)!=0x00)

 {

        GPFCON &= ~(3 <<( i*2));

GPFCON |=(0x01<<(i*2));

 }

mask=mask<<1;

 }

 break;

case MOD_EXIT_INT:

PRINTK("IOCTRL 1 called(0x%lx)!/n", arg);

GPFDAT = 0xFFFFFF00;

break;

default:

break;

}

return 0;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章