linux编写简单的驱动程序(linux驱动怎么写)

本文主要介绍linux写简单驱动(如何写linux驱动),下面一起看看linux写简单驱动(如何写linux驱动)相关资讯。
作者:道格,10年嵌入式开发经验,专注于:c/c,嵌入式,linux。
关注下方官方账号,回复【书籍】获取linux及嵌入式领域经典书籍;回复【pdf】获取所有原创文章(pdf格式)。
目录
目录示例程序目标编写驱动程序创建驱动程序目录和驱动程序创建makefile编译驱动程序模块加载驱动程序模块设备节点应用程序卸载驱动程序模块其他 体验,我们的阶梯!
大家好,我 我是刀哥。
在之前的文章中,我们一起讨论过:在linux系统中,编写字符设备驱动程序的基本框架主要由代码流和api函数触发。
在本文中,我们将基于此编写一个具有实际应用功能的驱动程序:
在驱动中,初始化gpio设备,自动创建设备节点;
在应用程序中,打开gpio设备,发送控制命令,设置gpio端口的状态;
示例程序旨在编写一个驱动程序模块:mygpio.ko
当这个驱动模块被加载时,在系统中创建一个mygpio类的设备,并在/dev目录中创建四个设备节点:
/dev/mygpio0
/dev/mygpio1
/dev/mygpio2
/dev/mygpio3
因为我们现在是在x86平台上模拟gpio的控制操作,没有实际的gpio硬件设备。
所以在驱动代码中,使用宏mygpio_hw_enable来控制硬件相关的代码,使用printk输出打印信息来体现硬件的操作。
在应用中,可以分别开启上述四个gpio设备,通过发送控制指令来设置gpio的状态。
下面所有写驱动的操作的工作目录和上一篇一样,即:~/tmp/linux-4.15/drivers/。
创建驱动目录和驱动$ cd linux-4.15/drivers/$ mkdir mygpio _ driver $ cd mygpio _ driver $ touchmygpio . cmygpio . c .文件内容如下(无需手敲,文末有代码下载链接):
#包含linux模块 #包含linux内核 #包含linux ctype.h = #包括linux设备。h = # includelinux cdev.h = //gpio硬件相关宏定义#define mygpio_hw_enable//设备名称# define mygpio _ name mygpio //有4个gpio端口# define mygpio _ number 4//device class static struct class * gpio _ class;//用于保存设备结构cdev gpio _ cd:% dgpio );}//硬件释放静态void gpio _ hw _ r:% d,gpio);}//设置硬件gpio的状态,控制gpio (gpio_ioctl)时研究为static void gpio _ hw _ set(unsigned long gpio _ no,unsigned int val){ printk( 调用gpio _ hw _ set。gpio _ no =% ld,val =% d。} # endif//static int gpio _ open(struct inode * inode,struct file * file){ printk( 调用gpio _ open。\ n )在应用程序打开设备时被调用;返回0;}//静态long gpio _ ioctl (struct file * file,unsigned int val,unsigned long gpio _ no){ printk( 调用gpio _ ioctl。\ n )在应用控制gpio时调用;//检查设置的状态值为没有合法的if (0!= val 1!= val){ printk( val无效!\ n );返回0;}//检查设备范围是否合法if(gpio _ no = mygpio _ numb: % ld设置为%d,gpio_no,val);#ifdef mygpio_hw_enable //操作gpio硬件gpio_hw_set(gpio_no,val);# endifreturn 0;}静态构造结构file_operations gpio_ops={。owner = this_module,。open = gpio_open,。unlock _ ioctl = gpio _ ioctl };static int _ _ init gpio _ driver _ init(void){ int i,devnodev_t数字_ devprintk( 调用gpio_driver_init。\ n );//动态申请设备号(严格来说要检查函数返回值)alloc _ chrdev _ region (num _ dev,gpio _ minor,mygpio _ number,mygpio _ name);//获取主设备号gpio _ major = major(num _ dev);printk( gpio_major = %d,gpio _ major);//创建一个设备类gpio _ class = class _ create(this _ module,mygpio _ name);//为(i = 0创建一个设备节点;i mygpio _ numberi){//设备号devno = mkdev (gpio _ major,gpio _ minor i);//初始化cdev结构cdev _ init (gpio _ cdev [i],gpio _ ops);//注册字符设备cdev_add(gpio_cdev[i],devno,1);//创建设备节点device _ create (gpio _ class,null,devno,null,mygpio _ name %)d ,我);}#ifdef mygpio_hw_enable //初始化(i = 0;i mygpio _ numberi){ gpio _ hw _ init(i);} # endifreturn 0;}静态void _ _ exit gpio _ driver _ exit(void){ int i;printk( 调用gpio_driver_exit。\ n );//删除设备和设备节点(i = 0;i mygpio _ numberi){ cdev _ del(gpio _ cdev[i]);device_destroy(gpio_class,mkdev(gpio_major,gpio _ minor i));}//释放设备class _ destroy(gpio _ class);#ifdef mygpio_hw_enable //释放gpio硬件for(i = 0;i mygpio _ numberi){ gpio _ hw _ release(i);}#endif//取消设备号unregister _ chrdev _ region(mkdev(gpio _ major,gpio _ minor),mygpio _ number);}模块许可证( gpl );模块_初始化(gpio _驱动程序_初始化);模块_出口(gpio _驱动程序_出口);与前面的文章相比,上面的代码稍微复杂了一点,主要是定义mygpio_hw_enable的控制部分的代码比较多。
例如,该宏定义控制下的三个硬件相关功能:
gpio _硬件_初始化
gpio _硬件_发布
gpio_hw_set
它是与gpio硬件的初始化、释放和状态设置相关的操作。
代码中的注释已经完善,结合前面文章中的函数描述,很容易理解。
从代码中可以看出,驱动使用alloc_chrdev_region函数动态注册设备号,使用linux应用层的udev服务在/dev目录下自动创建设备节点。
还有一点:上面的示例代码中,对于设备的操作功能,只实现了open和ioctl两个功能,具体根据实际使用场景来确定。
在本例中,只有。如何控制gpio的状态?
您还可以添加读取功能来读取gpio端口的状态。
要控制gpio设备,可以使用write或ioctl函数来达到目的,但ioctl更灵活。
create makefile $ touch makefile内容如下:
ifneq ($(kernelrelease),)obj-m : = mygpio . oelsekerneldir?=/lib/modules/$(shell uname-r)/build pwd echo 4-@
加载驱动模块在加载驱动模块之前,让 ■检查系统中与驱动设备相关的几个地方。
让 让我们先看看/d:;/d:没有这样的文件或目录。让 让我们再次检查/proc/devices目录。也没有mygpio设备的设备号。
$ cat /proc/devices为了方便查看打印的信息,清理dmesg的输出信息:
$ sudo dmesg -c现在将加载驱动程序模块并执行以下指令:。
$ sudo insmod mygpio.ko加载驱动程序时,会执行module_init注册的函数gpio_driver_init,输出打印的信息。
或者通过dmesg指令检查驱动模块的打印信息:
$ dmesg可以看到操作系统分配给这个设备的主设备号是244,还打印了gpio硬件初始化函数的调用信息。
至此,驱动模块已经加载完毕!
让 让我们看看/proc/devices目录中显示的设备号:
$卡特彼勒/专业c/devic: 244。
设备节点通过使用驱动程序初始化函数中的cdev_add和device_create自动创建设备节点。
因此,此时,我们可以在/dev目录中看到以下四个设备节点:
既然已经加载了设备驱动程序并创建了设备节点,应用程序就可以控制gpio硬件设备了。
应用程序应用程序仍然在~/tmp/app/目录中。
$ mkdir ~/tmp/app/app _ mygpio $ cd ~/tmp/app/app _ mygpio $ touch app _ mygpio . c文件的内容如下:
# include stdio . h # include stdlib . h # include unistd . h # include assert . h # include fcntl . h # include sys ioctl . h = #define my_gpio_number4//四个设备节点chargpio _ name[my _ gpio _ number][16]= { /dev/mygpio 0 , /dev/mygpio 1 , /dev/mygpio 2 , /dev/mygpio 3 };int main(int argc,char *argv[]){int fd,gpio_no,val//检查是否(3!= argc){printf(;usag:。/app_gpio gpio_no valu:打开失败!\ n ,gpio _ nam:公开赛成功了!\ n ,gpio_name[gpio_ no]);//控制gpio设备ioctl的状态(fd,val,gpio _ no);//关闭(fd)设备(fd);}以上代码不需要过多解释,注意参数的顺序即可。
接下来就是编译和测试:
$ gcapp _ mygpio.c-oapp _ mygpio执行一个应用程序时,需要携带两个参数:gpio设备号(0 ~ 3)和设置状态值(0或1)。
在这里设置设备/d:输入用户密码/d:打开成功!如何确认/d测试:将mygpio0的状态设置为0:
$须藤。/app_mygpio 0 0当然,设置其他几个gpio端口的状态也是可以正确进行的!
卸载驱动模块卸载指令:
此时/proc/devices下主设备号为244的mygpio已经不存在了。
让 让我们看看dmesg的打印信息:
可以看到驱动中的gpio_driver_exit被调用执行。
而且/dev目录下的四个设备节点也被函数devic测试代码已经放到网盘上了。
官方账号【iot物联网小镇】后台回复关键词:1128获取下载地址。
谢谢你
推荐阅读[1]《linux 从头学》系列文章。
【2】c语言指针——从底层原理到花式技巧,用图形和代码帮你讲解透彻。
【3】原来gdb的底层调试原理就是这么简单。
【4】内联汇编可怕吗?看完这篇文章,结束!
其他系列专辑:精选文章、应用设计、物联网、c语言。
明星官方账号,第一时间看文章!/fcntl . h/assert . h/unist d . h/stdlib . h/stdio . h
标签:
设备节点
了解更多linux写简单驱动(如何写linux驱动)相关内容请关注本站点。