博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux驱动初探之字符驱动
阅读量:4945 次
发布时间:2019-06-11

本文共 8220 字,大约阅读时间需要 27 分钟。

关键字:字符驱动、动态生成设备节点、helloworld

 

linux驱动编程,个人觉得第一件事就是配置好平台文件,这里以字符设备,也就是传说中的helloworld为例~

 

  此驱动程序基于linux3.0的内核,exynos4412开发板。

 

首先,打开平台文件,此开发板的平台文件是arch\arm\mach-exynos\mach-itop4412.c,不同平台位置是不一样的。

  申明一下设备信息,这里以编译进kernel为例

1 #ifdef CONFIG_HELLO_CHAR_CTL2 struct platform_device s3c_device_hello_char_ctl = {3         .name   = "Hello_Jni_Char",4         .id             = -1,5 };6 #endif

然后在struct platform_device *smdk4x12_devices[]数组里注册设备信息

1 #ifdef CONFIG_HELLO_CHAR_CTL2     &s3c_device_hello_char_ctl,3 #endif

至此,平台文件配置完毕。

 

接下来,开始大餐,驱动程序的编写!

注意:在linux的每一个驱动程序里面都需要加上如下信息,表明开源

1 MODULE_LICENSE("Dual BSD/GPL");2 MODULE_AUTHOR("pngcui");

每一个驱动程序都是从init函数开始的,同时也需要编写exit函数,在初始化函数中需要生成主设备号与从设备号,因为linux是根据主从设备号来寻找相应的硬件的

1 #define DEV_MAJOR 0 2 #define DEV_MINOR 0 3  4 int numdev_major = DEV_MAJOR; 5 int numdev_minor = DEV_MINOR; 6  7  8 /*输入主设备号*/ 9 module_param(numdev_major,int,S_IRUSR);10 /*输入次设备号*/11 module_param(numdev_minor,int,S_IRUSR);

进入驱动程序中的init函数中,进行注册设备号,生成设备节点DEVICE_NAME

1 #define DEVICE_NAME "chardevnode"

注意:此驱动由于控制了两个gpio引脚所以出现了gpio_init(),在这里可以忽略,对于gpio的控制,在下一篇博客中会写到。

1 static int scdev_init(void) 2 { 3     int ret = 0,i; 4     dev_t num_dev; 5      6      7     printk(KERN_EMERG "numdev_major is %d!\n",numdev_major); 8     printk(KERN_EMERG "numdev_minor is %d!\n",numdev_minor); 9     10     if(numdev_major){11         num_dev = MKDEV(numdev_major,numdev_minor);12         ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NAME);13     }14     else{15         /*动态注册设备号*/16         ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NAME);17         /*获得主设备号*/18         numdev_major = MAJOR(num_dev);19         printk(KERN_EMERG "adev_region req %d !\n",numdev_major);20     }21     if(ret<0){22         printk(KERN_EMERG "register_chrdev_region req %d is failed!\n",numdev_major);        23     }24     myclass = class_create(THIS_MODULE,DEVICE_NAME);25     26     27     my_devices = kmalloc(DEVICE_MINOR_NUM * sizeof(struct reg_dev),GFP_KERNEL);28     if(!my_devices){29         ret = -ENOMEM;30         goto fail;31     }32     memset(my_devices,0,DEVICE_MINOR_NUM * sizeof(struct reg_dev));33     34     /*设备初始化*/35     for(i=0;i

然后把设备注册到系统

1 static void reg_init_cdev(struct reg_dev *dev,int index){ 2     int err; 3     int devno = MKDEV(numdev_major,numdev_minor+index); 4  5     /*数据初始化*/ 6     cdev_init(&dev->cdev,&my_fops); 7     dev->cdev.owner = THIS_MODULE; 8     dev->cdev.ops = &my_fops; 9     10     /*注册到系统*/11     err = cdev_add(&dev->cdev,devno,1);12     if(err){13         printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);14     }15     else{16         printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);17     }18 }
dev->cdev.ops = &my_fops;这一句就指明了此驱动程序的函数接口
1 struct file_operations my_fops = {2     .owner = THIS_MODULE,3     .open = chardevnode_open,4     .release = chardevnode_release,5     .unlocked_ioctl = chardevnode_ioctl,6     .read = chardevnode_read,7     .write = chardevnode_write,8     .llseek = chardevnode_llseek,9 };

在这里的所有函数接口必须都实现,否则会编译报错。

其中最重要的两个函数是open、ioctl这两个函数,open函数用来打开设备节点,然后才可以对设备进行驱动。

然后是ioctl函数,一般上层应用都是通过调用此函数来对设备进行操作的。

1 /*打开操作*/ 2 static int chardevnode_open(struct inode *inode, struct file *file){ 3     printk(KERN_EMERG "chardevnode_open is success!\n"); 4      5     return 0; 6 } 7 /*关闭操作*/ 8 static int chardevnode_release(struct inode *inode, struct file *file){ 9     printk(KERN_EMERG "chardevnode_release is success!\n");10     11     return 0;12 }13 /*IO操作*/14 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){15     16     switch(cmd)17     {18         case 0:19         case 1:20             if (arg > LED_NUM) {21                 return -EINVAL;22             }23 24             gpio_set_value(led_gpios[arg], cmd);25             break;26 27         default:28             return -EINVAL;29     }30     31     printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);32     33     return 0;34 }35 36 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){37     return 0;38 }39 40 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){41     return 0;42 }43 44 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){45     return 0;46 }

到这里一个完整的字符驱动就编写完毕了。

 

最后附上完整的驱动程序

1 /*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/  2 #include 
3 /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/ 4 #include
5 /*定义module_param module_param_array的头文件*/ 6 #include
7 /*定义module_param module_param_array中perm的头文件*/ 8 #include
9 /*三个字符设备函数*/ 10 #include
11 /*MKDEV转换设备号数据类型的宏定义*/ 12 #include
13 /*定义字符设备的结构体*/ 14 #include
15 /*分配内存空间函数头文件*/ 16 #include
17 /*包含函数device_create 结构体class等头文件*/ 18 #include
19 20 /*自定义头文件*/ 21 #include "char_driver_leds.h" 22 23 /*Linux中申请GPIO的头文件*/ 24 #include
25 /*三星平台的GPIO配置函数头文件*/ 26 /*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/ 27 #include
28 /*三星平台4412平台,GPIO宏定义头文件*/ 29 #include
30 31 32 MODULE_LICENSE("Dual BSD/GPL"); 33 /*声明是开源的,没有内核版本限制*/ 34 MODULE_AUTHOR("iTOPEET_dz"); 35 /*声明作者*/ 36 37 static int led_gpios[] = { 38 EXYNOS4_GPL2(0),EXYNOS4_GPK1(1), 39 }; 40 #define LED_NUM ARRAY_SIZE(led_gpios) 41 42 #define DEVICE_NAME "chardevnode" 43 44 #define DEVICE_MINOR_NUM 1 45 46 #define DEV_MAJOR 0 47 #define DEV_MINOR 0 48 #define REGDEV_SIZE 3000 49 50 51 struct reg_dev 52 { 53 char *data; 54 unsigned long size; 55 56 struct cdev cdev; 57 }; 58 59 60 int numdev_major = DEV_MAJOR; 61 int numdev_minor = DEV_MINOR; 62 63 /*输入主设备号*/ 64 module_param(numdev_major,int,S_IRUSR); 65 /*输入次设备号*/ 66 module_param(numdev_minor,int,S_IRUSR); 67 68 static struct class *myclass; 69 struct reg_dev *my_devices; 70 71 /*打开操作*/ 72 static int chardevnode_open(struct inode *inode, struct file *file){ 73 printk(KERN_EMERG "chardevnode_open is success!\n"); 74 75 return 0; 76 } 77 /*关闭操作*/ 78 static int chardevnode_release(struct inode *inode, struct file *file){ 79 printk(KERN_EMERG "chardevnode_release is success!\n"); 80 81 return 0; 82 } 83 /*IO操作*/ 84 static long chardevnode_ioctl(struct file *file, unsigned int cmd, unsigned long arg){ 85 86 switch(cmd) 87 { 88 case 0: 89 case 1: 90 if (arg > LED_NUM) { 91 return -EINVAL; 92 } 93 94 gpio_set_value(led_gpios[arg], cmd); 95 break; 96 97 default: 98 return -EINVAL; 99 }100 101 printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d,arg is %d \n",cmd,arg);102 103 return 0;104 }105 106 ssize_t chardevnode_read(struct file *file, char __user *buf, size_t count, loff_t *f_ops){107 return 0;108 }109 110 ssize_t chardevnode_write(struct file *file, const char __user *buf, size_t count, loff_t *f_ops){111 return 0;112 }113 114 loff_t chardevnode_llseek(struct file *file, loff_t offset, int ence){115 return 0;116 }117 struct file_operations my_fops = {118 .owner = THIS_MODULE,119 .open = chardevnode_open,120 .release = chardevnode_release,121 .unlocked_ioctl = chardevnode_ioctl,122 .read = chardevnode_read,123 .write = chardevnode_write,124 .llseek = chardevnode_llseek,125 };126 127 128 /*设备注册到系统*/129 static void reg_init_cdev(struct reg_dev *dev,int index){130 int err;131 int devno = MKDEV(numdev_major,numdev_minor+index);132 133 /*数据初始化*/134 cdev_init(&dev->cdev,&my_fops);135 dev->cdev.owner = THIS_MODULE;136 dev->cdev.ops = &my_fops;137 138 /*注册到系统*/139 err = cdev_add(&dev->cdev,devno,1);140 if(err){141 printk(KERN_EMERG "cdev_add %d is fail! %d\n",index,err);142 }143 else{144 printk(KERN_EMERG "cdev_add %d is success!\n",numdev_minor+index);145 }146 }147 148 static int gpio_init(void){149 int i=0,ret;150 151 for(i=0;i
View Code

 

以及完整的测试程序

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 8 int main(){ 9 10 int fd;11 12 if((fd = open("/dev/chardevnode0",O_RDWR|O_NDELAY)) < 0)13 printf("Hello_Jni open failed!\n");14 15 ioctl(fd,0,0);16 ioctl(fd,0,1);17 18 close(fd);19 20 return 0;21 }

 

转载于:https://www.cnblogs.com/pngcui/p/4766504.html

你可能感兴趣的文章
POJ 3067【树状数组】
查看>>
JNI和NDK编程
查看>>
spring boot+mybatis搭建项目
查看>>
PHP Cookies
查看>>
点击放大图片预览
查看>>
最大熵原理
查看>>
Maven最佳实践:划分模块
查看>>
内容样式
查看>>
JAVA从局域网共享文件夹中下载上传文件以及java访问共享文件夹
查看>>
DAY19 面向对象三大特性之多态、封装
查看>>
管理信息系统的开发与管理
查看>>
JMeter的学习笔记(一):JMeter的入门使用
查看>>
jap页面获取struts2中action中变量的值
查看>>
Notes on <<Refactoring Databases - Evolutionary Database Design>>
查看>>
使用MyBatis Generator自动创建代码( SSM框架)
查看>>
检验两个随机序列的beta系数
查看>>
node-webkit教程(10)Platform Service之File dialogs
查看>>
《计算机组成与体系结构:性能设计》读后小记 5、内部存储器
查看>>
3 基础语法
查看>>
字符串知识储备
查看>>