生活随笔
收集整理的這篇文章主要介紹了
Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?現在,我們來編寫自己第一個字符設備驅動 —— 點亮LED。(不完善,后面再完善)
硬件平臺:Exynos4412(FS4412)
編寫驅動分下面幾步:
a -- 查看原理圖、數據手冊,了解設備的操作方法;
b -- 在內核中找到相近的驅動程序,以它為模板進行開發,有時候需要從零開始;
c -- 實現驅動程序的初始化:比如向內核注冊這個驅動程序,這樣應用程序傳入文件名,內核才能找到相應的驅動程序;
d -- 設計所要實現的操作,比如 open、close、read、write 等函數;
e -- 實現中斷服務(中斷不是每個設備驅動所必須的);
f -- 編譯該驅動程序到內核中,或者用 insmod 命令加載;
g-- 測試驅動程序;
下面是一個點亮LED 的驅動:
第一步,當然是查看手冊,查看原理圖,找到相應寄存器;
查看手冊,四個LED 所用寄存器為:
led2
GPX2CON ? ?0x11000c40
GPX2DAT ? ? 0x11000c44
led3
GPX1CON ? ?0x11000c20
GPX1DAT ? ? 0x11000c24
led4 ?3-4 3-5
GPF3CON ? 0x114001e0
GPF3DAT ? ?0x114001e4
這里要注意:arm體系架構是io內存,必須要映射 ? ioremap( );? 其作用是物理內存向虛擬內存的映射。 用到?writel ? readl這兩個函數,詳細解釋會在后面不上,先看一下簡單用法:
以LED2為例,下面是地址映射及讀寫:
[cpp]?view plaincopy
int?*pgpx2con??;?? int?*pgpx2dat;?? ?? pgpx2con?=?ioremap(?GPX2CON,?4);?? pgpx2dat?=?ioremap(GPX2DAT,4);?? readl(pgpx2con);?? writel(0x01,?pgpx2dat?);??
下面是驅動程序,后面會更完善
[cpp]?view plaincopy
#include?<linux/module.h>?? #include?<linux/fs.h>?? #include?<linux/cdev.h>?? #include?<linux/device.h>?? #include?<asm/io.h>?? #include?<asm/uaccess.h>?? ?? static?int?major?=?250;?? static?int?minor=0;?? static?dev_t?devno;?? static?struct?class?*cls;?? static?struct?device?*test_device;?? ?? #define??GPX2CON????0x11000c40?? #define??GPX2DAT????0x11000c44?? #define??GPX1CON????0x11000c20?? #define??GPX1DAT????0x11000c24?? #define??GPF3CON????0x114001e0?? #define??GPF3DAT????0x114001e4?? ?? static?int?*pgpx2con??;?? static?int?*pgpx2dat;?? ?? static?int?*pgpx1con??;?? static?int?*pgpx1dat;?? ?? static?int?*pgpf3con??;?? static?int?*pgpf3dat;?? ?? void?fs4412_led_off(int?num);?? ?? void?fs4412_led_on(int?num)?? {?? ????switch(num)?? ????{?? ????????case?1:?? ????????????writel(readl(pgpx2dat)?|(0x1<<7),?pgpx2dat);?? ????????????break;?? ????????case?2:?? ????????????writel(readl(pgpx1dat)?|(0x1<<0),?pgpx1dat);???????????? ????????????break;???????????? ????????case?3:?? ????????????writel(readl(pgpf3dat)?|(0x1<<4),?pgpf3dat);???? ????????????break;?? ????????case?4:?? ????????????writel(readl(pgpf3dat)?|(0x1<<5),?pgpf3dat);???????????? ????????????break;???? ????????default:?? ????????????fs4412_led_off(1);?? ????????????fs4412_led_off(2);?? ????????????fs4412_led_off(3);?? ????????????fs4412_led_off(4);?? ????????????break;?? ?????????????? ????}?? }?? ?? void?fs4412_led_off(int?num)?? {?? ????switch(num)?? ????{?? ????????case?1:?? ????????????writel(readl(pgpx2dat)?&(~(0x1<<7)),?pgpx2dat);?? ????????????break;?? ????????case?2:?? ????????????writel(readl(pgpx1dat)&(~(0x1<<0)),?pgpx1dat);?????????????? ????????????break;???????????? ????????case?3:?? ????????????writel(readl(pgpf3dat)?&(~(0x1<<4)),?pgpf3dat);????? ????????????break;?? ????????case?4:?? ????????????writel(readl(pgpf3dat)?&(~(0x1<<5)),?pgpf3dat);????????????? ????????????break;???????????? ????}?? }?? ?? static?int?led_open?(struct?inode?*inode,?struct?file?*filep)?? {?? ????fs4412_led_off(1);?? ????fs4412_led_off(2);?? ????fs4412_led_off(3);?? ????fs4412_led_off(4);???? ????return?0;?? }?? ?? static?int?led_release(struct?inode?*inode,?struct?file?*filep)?? {?? ????fs4412_led_off(1);?? ????fs4412_led_off(2);?? ????fs4412_led_off(3);?? ????fs4412_led_off(4);???? ????return?0;?? }?? ?? static?ssize_t?led_read(struct?file?*filep,?char?__user?*buf,?size_t?len,?loff_t?*pos)?? {?? ????return?0;?? }?? ?? static?ssize_t?led_write(struct?file?*filep,?const?char?__user?*buf,?size_t?len,?loff_t?*pos)?? {?? ????int?led_num;?? ?? ????if(len?!=4)?? ????{?? ????????return?-EINVAL;?? ????}?? ????if(copy_from_user(&led_num,buf,len))?? ????{?? ????????return?-EFAULT;??? ????}?? ?? ????fs4412_led_on(led_num);?? ????printk("led_num?=%d?\n",led_num);?? ?? ????return?0;?? }?? ?? static?struct?file_operations?hello_ops=?? {?? ????.open?????=?led_open,?? ????.release?=?led_release,?? ????.read?????=?led_read,?? ????.write????=?led_write,?? };?? ?? static?void?fs4412_led_init(void)?? {?? ????pgpx2con?=?ioremap(GPX2CON,4);?? ????pgpx2dat?=?ioremap(GPX2DAT,4);?? ?? ????pgpx1con?=?ioremap(GPX1CON,4);?? ????pgpx1dat?=ioremap(GPX1DAT,4);?? ?? ????pgpf3con??=?ioremap(GPF3CON,4);?? ????pgpf3dat?=ioremap(GPF3DAT,4);?? ?? ????writel((readl(pgpx2con)&?~(0xf<<28))?|(0x1<<28),pgpx2con)?;?? ????writel((readl(pgpx1con)&?~(0xf<<0))?|(0x1<<0),pgpx1con)?;????? ????writel((readl(pgpf3con)&?~(0xff<<16))?|(0x11<<16),pgpf3con)?;????? }?? ?? static?int?led_init(void)?? {?? ????int?ret;?????? ????devno?=?MKDEV(major,minor);?? ????ret?=?register_chrdev(major,"led",&hello_ops);?? ?? ????cls?=?class_create(THIS_MODULE,?"myclass");?? ????if(IS_ERR(cls))?? ????{?? ????????unregister_chrdev(major,"led");?? ????????return?-EBUSY;?? ????}?? ????test_device?=?device_create(cls,NULL,devno,NULL,"led");?? ????if(IS_ERR(test_device))?? ????{?? ????????class_destroy(cls);?? ????????unregister_chrdev(major,"led");?? ????????return?-EBUSY;?? ????}????? ????fs4412_led_init();?? ????return?0;?? }?? ?? void?fs4412_led_unmap(void)?? {?? ????iounmap(pgpx2con);?? ????iounmap(pgpx2dat?);?? ?? ????iounmap(pgpx1con);?? ????iounmap(pgpx1dat?);?? ?? ????iounmap(pgpf3con?);?? ????iounmap(pgpf3dat?);?? }?? ?? static?void?led_exit(void)?? {?? ????fs4412_led_unmap();?? ????device_destroy(cls,devno);?? ????class_destroy(cls);??? ????unregister_chrdev(major,"led");?? ????printk("led_exit?\n");?? }?? ?? MODULE_LICENSE("GPL");?? module_init(led_init);?? module_exit(led_exit);??
測試程序:
[cpp]?view plaincopy
#include?<sys/types.h>?? #include?<sys/stat.h>?? #include?<fcntl.h>?? #include?<stdio.h>?? ?? main()?? {?? ????int?fd,i,lednum;?? ?? ????fd?=?open("/dev/led",O_RDWR);?? ????if(fd<0)?? ????{?? ????????perror("open?fail?\n");?? ????????return?;?? ????}?? ????for(i=0;i<100;i++)?? ????{?? ????????lednum=0;?? ????????write(fd,&lednum,sizeof(int));?? ????????lednum?=?i%4+1;?? ????????write(fd,&lednum,sizeof(int));???? ????????sleep(1);?? ????}?? ????close(fd);?? }??
makefile:
[cpp]?view plaincopy
ifneq??($(KERNELRELEASE),)?? obj-m:=hello.o?? $(info?"2nd")?? else?? #KDIR?:=?/lib/modules/$(shell?uname?-r)/build?? KDIR?:=?/home/xiaoming/linux-3.14-fs4412?? PWD:=$(shell?pwd)?? all:?? ????$(info?"1st")?? ????make?-C?$(KDIR)?M=$(PWD)?modules?? ????arm-none-linux-gnueabi-gcc?test.c?? ????sudo?cp?hello.ko?a.out?/rootfs/test/?? clean:?? ????rm?-f?*.ko?*.o?*.symvers?*.mod.c?*.mod.o?*.order?? endif??
編譯結束后,將a.out 和 hello.ko 拷貝到開發板中:
# insmod hello.ko
#mknod /dev/hello c 250 0
#./a.out
會看到跑馬燈效果。
后面會對該驅動完善。
總結
以上是生活随笔為你收集整理的Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。