miscdevice混杂设备驱动 2



Linux里面的misc杂项设备是主设备号为10的驱动设备,它的注册跟使用比较的简单,所以比较适用于功能简单的设备。正因为简单,所以它通常嵌套在platform 总线驱动中,配合总线驱动达到更复杂,多功能的效果

一.杂项设备数据结构分析

杂项设备驱动结构还是很简单的,他可以夹杂到其它结构体当中,以丰富驱动的血肉。一般情况下,我们是将它嵌套在其它结构当中的。

[objc]  view plain  copy
  1. struct miscdevice  {  
  2.     int minor; //次设备号,主设备号已经敲定是10了,后面我们跟进代码看一下。  
  3.     const charchar *name;  //驱动名字,最终会反映在设备节点名字上。  
  4.     const struct file_operations *fops; //设备操作方法集合  
  5.     struct list_head list; //链接到所有杂项设备链表当中。  
  6.     struct device *parent; //父设备,这个一般为NULL  
  7.     struct device *this_device;//当前设备的devices结构。  
  8.     const charchar *nodename;  
  9.     umode_t mode;  
  10. };  

上面:

parent:这个指针决定了在/sys文件系统里面,它是创建在哪个目录下。如果为空就在/sys/class根目录下创建,如果不为空都是在/sys/class/misc 文件下面创建的一些属性文件。
this_device:这个就代表当前设备的设备结构体,这个在查找扩充数据结构时,非常有用。

[objc]  view plain  copy
  1. typedef struct led{  
  2.     struct miscdevice *led_dev;  
  3.     struct mutex led_lock;  
  4.     spinlock_t io_lock;  
  5.     int flset;  
  6.     int flen;  
  7.     uint8_t regs[4+1];  
  8. }rt8547_dev_t;  
上面就是我定义的数据结构,我就是把struct miscdevice 嵌套在我自己定义的头文件中。

二、杂项设备的注册过程

在介绍注册过程时,我们先来了解一些非常重要的全局变量。

  1. static LIST_HEAD(misc_list);  
  2. /************************************************ 
  3. *#define LIST_HEAD_INIT(name) { &(name), &(name) } 
  4. * 
  5. *#define LIST_HEAD(name) \ 
  6. *   struct list_head name = LIST_HEAD_INIT(name) //可以看到这里偷偷的定义了一个为name的struct list_head结构 
  7. *************************************************/  
  8.  static DEFINE_MUTEX(misc_mtx);  
  9. /*********************************************************** 
  10. * #define DEFINE_MUTEX(mutexname) \ 
  11. *    struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) //同样这里也定义了一个互斥锁。 
  12. ************************************************************/  
  13.  /* 
  14.  * Assigned numbers, used for dynamic minors 
  15.  */  
  16. #define DYNAMIC_MINORS 64 /* like dynamic majors */  
  17. static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);  
  18. /*********************************************** 
  19. *#define DECLARE_BITMAP(name,bits) \ 
  20. *    unsigned long name[BITS_TO_LONGS(bits)] //这里这个宏BITS_TO_LONGS(64) 我在机器上验证结果是2 
  21. *所以,上面相当与直接定义了 unsigned long misc_minors[2];//而我所验证的平台上unsigned long大小为4个字节,这样的话可以表示64字符设备。 
  22. ************************************************/  

misc_list:这是所有misc设备的头指针,打个比方说,所有misc设备结构体都挂在它上面。


注册的过程是用下面这个函数来实现的

  1. int misc_register(struct miscdevice * misc)  
  2. {  
  3.     dev_t dev;  
  4.     int err = 0;  
  5.   
  6.     INIT_LIST_HEAD(&misc->list); //struct list_head 初始化,这个当我们有这个结构的话,要记着用这个宏初始化一下。  
  7.   
  8.     mutex_lock(&misc_mtx); //同样互斥锁也要初始化,当然我们如果用到的话,也要这样初始化。  
  9.   
  10.     if (misc->minor == MISC_DYNAMIC_MINOR) { //我们在驱动中配置的就是MISC_DYNAMIC_MINOR(动态分配)  
  11.         int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); //其中的原理我们不深究,这里就是找一个没有使用的次设备号给我们用。  
  12.         if (i >= DYNAMIC_MINORS) {  
  13.             mutex_unlock(&misc_mtx);  
  14.             return -EBUSY;  
  15.         }  
  16.         misc->minor = DYNAMIC_MINORS - i - 1;  
  17.         set_bit(i, misc_minors); //请看下面详细代码。  
  18.     } else {  
  19.         struct miscdevice *c;  
  20.   
  21.         list_for_each_entry(c, &misc_list, list) { //如果上面不是动态注册,就遍历整个misc_list,确定是否已经注册过了。  
  22.             if (c->minor == misc->minor) {   
  23.                 mutex_unlock(&misc_mtx);  
  24.                 return -EBUSY;  
  25.             }  
  26.         }  
  27.     }  
  28.   
  29.     dev = MKDEV(MISC_MAJOR, misc->minor);//主设备好和次设备号,加工成真正的设备号。  
  30.   
  31.     misc->this_device = device_create(misc_class, misc->parent, dev, //这个函数是非常重要的。  
  32.                       misc, "%s", misc->name);  
  33.     if (IS_ERR(misc->this_device)) {  
  34.         int i = DYNAMIC_MINORS - misc->minor - 1;  
  35.         if (i < DYNAMIC_MINORS && i >= 0)  
  36.             clear_bit(i, misc_minors);  
  37.         err = PTR_ERR(misc->this_device);  
  38.         goto out;  
  39.     }  
  40.   
  41.     /* 
  42.      * Add it to the front, so that later devices can "override" 
  43.      * earlier defaults 
  44.      */  
  45.     list_add(&misc->list, &misc_list); //最后将该设备加入全局misc设备链表中  
  46.  out:  
  47.     mutex_unlock(&misc_mtx); //解锁  
  48.     return err;  
  49. }  
  50.   
  51. //下面一段的函数的意思就是把数组中的对应的标志位置位,表示这个次设备号已经使用了。  
  52. static inline void set_bit(int nr, volatile unsigned longlong *addr)  
  53. {  
  54.     //#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG))  
  55.     unsigned long mask = BIT_MASK(nr); //找到次设备号偏移,如上宏定义  
  56.     unsigned longlong *p = ((unsigned longlong *)addr) + BIT_WORD(nr);//这里如果nr>=32,这里就会+1,(类型是long,所以实际上这里是+4)标志位就跑到第二行了。  
  57.     unsigned long flags;  
  58.   
  59.      //#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG)) 即1<<(nr % 32)  
  60.   
  61.      _atomic_spin_lock_irqsave(p, flags); //加了保护  
  62.     *p  |= mask; //对应标志为置1.  
  63.     _atomic_spin_unlock_irqrestore(p, flags);  
  64. }  
假如现在我们创建的次设备号是 20 ,那么标志位偏移量就是 1<<20 ,那么就会有上面的 misc_minors[0]  = misc_minors[0] | 1<<20 ;同样假如现在我们创建的次设备号是 40 ,那么就会有 mask = 1<<(40%32) ,即 mask = 1<<8 ,就有 misc_minors[1] = misc_minors[1] | 1<<8 。下面这幅图是根据我所使用的内核版本,我使用的内核里面只定义了64个标志位,这里也就画了8个字节。但是不管有多少个字节吧,原理还是一样的。


可以看看/dev目录下的设备节点主设备是10,次设备号是42.这样的话,标志位就放在第二排了。

三、引人深思的misc_class

misc_class就是一个引子,创建了这样一个class之后,所有的misc设备的class设备文件都会在/sys/class/misc目录下创建。
[objc]  view plain  copy
  1. static struct classclass *misc_class; //全局的变量  
  2.   
  3. static int __init misc_init(void)  
  4. {  
  5.     int err;  
  6.   
  7. #ifdef CONFIG_PROC_FS  
  8.     proc_create("misc"0NULL, &misc_proc_fops); //如果允许proc文件系统,这里也会创建proc文件夹。  
  9. #endif  
  10.     misc_class = class_create(THIS_MODULE, "misc");//这里就看到了,创建了全局的class文件。  
  11.     err = PTR_ERR(misc_class);  
  12.     if (IS_ERR(misc_class))  
  13.         goto fail_remove;  
  14.   
  15.     err = -EIO;  
  16.     if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) //这里还是调用字符设备的注册接口,只不过这里主设备号始终就是10了。  
  17.         goto fail_printk;  
  18.     misc_class->devnode = misc_devnode;  
  19.     return 0;  
  20.   
  21. fail_printk:  
  22.     printk("unable to get major %d for misc devices\n", MISC_MAJOR);  
  23.     class_destroy(misc_class);  
  24. fail_remove:  
  25.     remove_proc_entry("misc"NULL);  
  26.     return err;  
  27. }  
  28. subsys_initcall(misc_init); //这里预示系统刚起来时,就会调用这里的初始化函数,确保在所有misc设备初始化之前,misc_class,misc_list都创建好了。  
只要有了misc_class对象,所有misc设备就有地方放了。

四.杂项设备的注销过程

杂项设备的注销过程,其实就是注册过程的逆向过程。只是说这里代码非常简单,其实主要工作都在device_destroy()中

  1. int misc_deregister(struct miscdevice *misc)  
  2. {  
  3.     int i = DYNAMIC_MINORS - misc->minor - 1;  
  4.   
  5.     if (WARN_ON(list_empty(&misc->list)))  
  6.         return -EINVAL;  
  7.   
  8.     mutex_lock(&misc_mtx);  
  9.     list_del(&misc->list);  
  10.     device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));  
  11.     if (i < DYNAMIC_MINORS && i >= 0)  
  12.         clear_bit(i, misc_minors);  
  13.     mutex_unlock(&misc_mtx);  
  14.     return 0;  
  15. }  
  16.   
  17. void device_destroy(struct classclass *class, dev_t devt)  
  18. {  
  19.     struct device *dev;  
  20.   
  21.     dev = class_find_device(classNULL, &devt, __match_devt);  
  22.     if (dev) {  
  23.         put_device(dev);  
  24.         device_unregister(dev);  
  25.     }  
  26. }  

.Rt8547闪光灯misc设备驱动的分析

下面的这个例子是我在添加camera 闪光灯rt8547设备时,编写的一个闪光灯驱动的初版文件。初步的功能我已经验证过了,是可以工作的。
1)open函数中的共享精神

下面限于篇幅问题,我不会贴出所有代码,只是加上自己的一些分析,和在编码过程中遇到的问题。

[objc]  view plain  copy
  1. rt8547_dev_t *led_dev = NULL//全局的闪光灯设备结构体  
  2.   
  3. static int led_open(struct inode *node, struct file *file){  
  4.   
  5.     struct my_platfrom_struct *my_data = kmalloc(sizeof(struct my_platfrom_struct), GFP_KERNEL);  
  6.     if (!my_data){  
  7.         RT8547_ERR("malloc my_data falied!");  
  8.     }  
  9.     my_data->grade = 1;  
  10.     my_data->age = 22;  
  11.     my_data->phone = 110;  
  12.     file->private_data = my_data;  
  13.     RT8547_INFO("open led devices success!!!\n");  
  14.     return 0;  
  15. }  
上面代码中,我最想强调的就是file->private_data = my_data.这行代码鬼使神功的将我们需要共享的变量,放到了其它函数都能看得到的地方。因为在其它的read,write,ioctl,release等函数都可以通过file->private_data拿到这个共享的变量。一定要学会共享,理解共享的精神。
2)ioctl函数中的多元化

[objc]  view plain  copy
  1. static long led_ioctl(struct file *file, unsigned int cmd,unsigned long arg){  
  2.     struct my_platfrom_struct *para = (struct my_platfrom_struct *)arg;  
  3.     struct my_platfrom_struct *my_dat = (struct my_platfrom_struct *)file->private_data; //这里我们就拿到了共享变量。  
  4.   
  5.     switch(cmd){  
  6.         case LED_IO_GET_MY_DATA:  
  7.             copy_to_user(para,my_dat,sizeof(struct my_platfrom_struct));  
  8.             break;  
  9.         case LED_IO_OPEN_TORCH:  
  10.             {  
  11.                 int level;  
  12.                 copy_from_user(&level,(void*)arg,sizeof(int));  
  13.                 rt8547_send_data(led_dev,RT8547_CONTROL3,RT8547_MODE_MASK,1);//Torch Mode  
  14.                 rt8547_send_data(led_dev,RT8547_CONTROL3,0x0f,level&0x0f);  
  15.                 RT8547_INFO("Open Torch, level:%d",level);  
  16.                 rt8547_set_led_on(led_dev);  
  17.             }  
  18.             break;  
  19.         case LED_IO_OPEN_FLASH:  
  20.             {  
  21.                 int level;  
  22.                 copy_from_user(&level,(void*)arg,sizeof(int));  
  23.                 rt8547_send_data(led_dev,RT8547_CONTROL3,RT8547_MODE_MASK,0);//flash Mode  
  24.                 rt8547_send_data(led_dev,RT8547_CONTROL2,0x1f,level&0x1f);  
  25.                 RT8547_INFO("Open Flash, level:%d",level);  
  26.                 rt8547_set_led_on(led_dev);  
  27.             }  
  28.             break;  
  29.         case LED_IO_CLOSE_LED:  
  30.             rt8547_set_led_off(led_dev);  
  31.             RT8547_INFO("rt8547 close device!\n");  
  32.             break;  
  33.         }  
  34.     return 0;  
  35. }  
ioctl函数在驱动中用到最多,在实际开发中一般都是通过ioctl和kernel交互的。

3)Rt8547 gpio 初始化和释放
gpio 初始化相当重要,不初始化就不能用了。gpio的初始化和释放所有kernel都是一样的过程,大家要熟记这些方法。

[objc]  view plain  copy
  1. static void rt8547_gpio_deinit(const rt8547_dev_t *dev)  
  2. {  
  3.     gpio_direction_input(dev->flen);  
  4.     gpio_free(dev->flen);  
  5.   
  6.     gpio_direction_input(dev->flset);  
  7.     gpio_free(dev->flset);  
  8. }  
  9.   
  10. static int rt8547_gpio_init(const rt8547_dev_t *dev)  
  11. {  
  12.     int ret;  
  13.     gpio_request(dev->flen,NULL); //请求gpio  
  14.     if (ret) {  
  15.         RT8547_INFO("sensor: flen already request %d.\n",dev->flen);  
  16.         goto request_flen_fail;  
  17.     }  
  18.     gpio_direction_output(dev->flen, 1); //设置gpio的输出输出方向  
  19.     gpio_set_value(dev->flen,0); //设置为高电平还是低电平  
  20.   
  21.     gpio_request(dev->flset,NULL); //以下同理  
  22.     if (ret) {  
  23.         printk("sensor: flset already request %d.\n",dev->flen);  
  24.         goto request_flset_fail;  
  25.     }  
  26.     gpio_direction_output(dev->flset, 1);//out  
  27.     gpio_set_value(dev->flset,0);  
  28.     return 0;  
  29.       
  30. request_flset_fail:  
  31.     gpio_free(dev->flset);  
  32. request_flen_fail:  
  33.     gpio_free(dev->flen);  
  34. request_fail:  
  35.     return ret;  
  36. }  

注意:大家有没有注意到上面函数gpio口的释放,有什么异样没有。也许你已经发现,谁先注册的,就放到最后在释放,这也是为了kernel的稳定性才这样写的。当有一个gpio口请求失败,那么之前注册成功的gpio资源都要释放。要不然也许有其它模块在请求这个gpio口资源呢。记住注册过程和释放过程是相对的。如下所示。


4)led_fops和misdevice设备填充

[objc]  view plain  copy
  1. static const struct file_operations led_fops = {  
  2.     .owner = THIS_MODULE, //一般都是THIS_MODULE  
  3.     .open = led_open,  
  4.     .read = led_read,  
  5.     .write = led_write,  
  6.     .unlocked_ioctl = led_ioctl,  
  7.     .release = led_release,  
  8. };//上面都是填充一些关键的函数指针,只要知道怎么用就行了。  
  9.   
  10. static struct miscdevice led_device = {  
  11.     .minor = LED_MINOR, //次设备号  
  12.     .name = LED_DEVICE_NAME, //设备名字宏定义  
  13.     .fops = &led_fops, //文件操作指针集合,如上面的结构  
  14. };  

上面两个结构是字符设备必不可少的两个结构体。大家有没有发现这两个结构一般都放在靠近驱动模块末尾的位置。不知道大家有没有考虑过为什么会有这样的情况。这是由于驱动中一般没有声明open,read,write,ioctl等接口函数,如果你不想将file_operations安排在驱动末尾。你也可以声明这些驱动函数,这样就不会受限制了。这个要看自己了,一般不这样来,这也是一种习惯。

5)rt8547驱动初始化函数和注销函数

[objc]  view plain  copy
  1. int led_init(){  
  2.     int ret;  
  3.     RT8547_INFO("led_init!\n");  
  4.     led_dev =  kzalloc(sizeof(rt8547_dev_t), GFP_KERNEL);  
  5.     if(!led_dev){  
  6.         RT8547_ERR("alloc mem failed");  
  7.     }  
  8.   
  9.     led_dev->led_dev = &led_device;  
  10.     led_dev->flset= GPIO_CAM_FLASH_SET;  
  11.     led_dev->flen = GPIO_CAM_FLASH_EN;  
  12.     led_dev->regs[RT8547_DUMMY] = 0;  
  13.     led_dev->regs[RT8547_CONTROL1] = 0x06//这里模拟寄存器  
  14.     led_dev->regs[RT8547_CONTROL2] = 0x12;  
  15.     led_dev->regs[RT8547_CONTROL3] = 0x02;  
  16.     led_dev->regs[RT8547_CONTROL4] = 0x0f;  
  17.       
  18.     //rt8547_parse_dt(led_dev->led_dev->this_device->of_node,led_dev);  
  19.     rt8547_gpio_init(led_dev);  
  20.     mutex_init(&led_dev->led_lock);  
  21.     ret = misc_register(led_dev->led_dev);  
  22.     if (unlikely(ret)) {  
  23.         RT8547_ERR("failed to register misc device'%s'!\n",  
  24.                 led_dev->led_dev->name);  
  25.         goto out_free_buffer;  
  26.     }  
  27.   
  28.     ret = device_create_file(led_dev->led_dev->this_device, &dev_attr_flash); //创建属性文件  
  29.     if (ret < 0) {  
  30.         printk(KERN_ALERT"Failed to create attribute flash.");  
  31.         goto err_register_flash;  
  32.     }  
  33.   
  34.     RT8547_INFO("create device attribute file flash!\n");  
  35.     return 0;  
  36. err_register_flash:  
  37.   
  38. out_free_buffer:  
  39.     kfree(led_dev);  
  40.     return ret;  
  41. }  
  42.   
  43. void led_exit(void)  
  44. {  
  45.     RT8547_INFO("rt8547 exit!\n");  
  46.     misc_deregister(led_dev->led_dev);  
  47.     kfree(led_dev);  
  48. }  
驱动初始化函数中一般进行全局设备结构题的初始化,资源的请求,设备树的解析。当然如果是platform设备,这些也可以在probe函数中做。大家有没有发现我在上面的初始化函数中使用device_create_file()创建了一个设备属性文件。后面我会专门写一篇博文专门来分析这些和创建设备文件相关的函数。

6)应用程序

[objc]  view plain  copy
  1. #include <fcntl.h>  
  2. #include <errno.h>  
  3. #include <sys/ioctl.h>  
  4. #include <stdio.h>  
  5. #include "led_rt8547.h"  
  6.   
  7. #define FILE_PATH "/dev/rt8547_led"  
  8.   
  9. int main(){  
  10.     int ret;  
  11.     int mode,level;  
  12.     my_data my_dat;   
  13.   
  14.     int fd = open(FILE_PATH,O_RDWR);  
  15.     if ( -1 == fd){  
  16.         printf("open led_flash node failed!\n");  
  17.         return -1;  
  18.     }  
  19.     printf("unsigned size:%d\n",sizeof(unsigned long));  
  20.   
  21.     printf("\n1:GET_MY_DATA\n \  
  22.         2:LED_OPEN_TORCH\n \  
  23.         3:LED_OPEN_FLASH\n \  
  24.         4:LED_CLOSE\n");  
  25.     printf("mode:");  
  26.     scanf("%d",&mode);  
  27.     if(2 == mode || 3 == mode){  
  28.         printf("level:");  
  29.         scanf("%d",&level);  
  30.     }  
  31.     printf("Rt8547 mode:%d,level:%d\n",mode,level);  
  32.     switch(mode){  
  33.         case 1:  
  34.             ret = ioctl(fd,LED_IO_GET_MY_DATA, &my_dat);  
  35.             printf("Rt8547 Get my data success!\n");  
  36.             printf("Rt8547 grade:%d,age:%d,phone:%d",my_dat.grade,my_dat.age,my_dat.phone);  
  37.             if(0 != ret){  
  38.                 printf("Rt8547 Get my data failed!\n");  
  39.                 ret = -1;  
  40.             }  
  41.             break;  
  42.         case 2:  
  43.             ret = ioctl(fd,LED_IO_OPEN_TORCH, &level);  
  44.             printf("Rt8547 open torch success!\n");  
  45.             if(0 != ret){  
  46.                 printf("Rt8547 open torch failed!\n");  
  47.                 ret = -1;  
  48.             }  
  49.             break;  
  50.         case 3:  
  51.             ret = ioctl(fd,LED_IO_OPEN_FLASH, &level);  
  52.             printf("Rt8547 open flash success!\n");  
  53.             if(0 != ret){  
  54.                 printf("Rt8547 open flash failed!\n");  
  55.                 ret = -1;  
  56.             }  
  57.             break;  
  58.         case 4:  
  59.             ret = ioctl(fd,LED_IO_CLOSE_LED, &level);  
  60.             printf("Rt8547 close led success!\n");  
  61.             if(0 != ret){  
  62.                 printf("Rt8547 close led failed!\n");  
  63.                 ret = -1;  
  64.             }  
  65.             break;  
  66.     }  
  67.     return ret;  
  68. }  
上面调用系统调用,一切都在代码中,read the fucking code!!!!!!

五.调试过程中遇到的问题及解决办法和思路

1.Android.mk编写问题

在Anrdroid大环境下,最主要的问题是找不到头文件的问题。一开始的编译的时候,找不到头文件,后来参考其它工程的Android.mk发现他们一般都包含了$(TARGET_OUT_INTERMEDIATES)/KERNEL/source/include/video(如下所示),大家一看都明白,这里就是存放头文件的地方。其实在编译的过程中,kernel的头文件都会拷贝到out目录对应工程的/KERNEL/source/include目录下,这些头文件和kernel源码中是一样的。大家一定要牢记。我在编译led_flash测试程序时,用的就是下面这个Android.mk

其中LOCAL_MODULE_TAGS :=  optional意思就是所有版本都编译该模块。

知识扩展:
 user: 指该模块只在user版本下才编译
 eng: 指该模块只在eng版本下才编译
 tests: 指该模块只在tests版本下才编译
 optional:指该模块在 所有版本下都编译,默认是optional

[objc]  view plain  copy
  1. LOCAL_PATH:= $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)//清除环境变量  
  4. #LOCAL_ADDITIONAL_DEPENDENCIES := $(common_deps)  
  5.   
  6. #头文件路径  
  7. LOCAL_C_INCLUDES := \  
  8.         $(TARGET_OUT_INTERMEDIATES)/KERNEL/source/include/misc \   
  9.         $(TARGET_OUT_INTERMEDIATES)/KERNEL/source/include/video   
  10.   
  11. LOCAL_SRC_FILES := led_flash.c  #源文件  
  12. LOCAL_MODULE := led_flash #应用程序,编译成可执行程序时的名字  
  13. LOCAL_MODULE_TAGS := optional   
  14.   
  15. include $(BUILD_EXECUTABLE)  

2)Kernel头文件编写问题

在上面Android.mk中包含了头文件路径时,有时候还是会提示找不到一些头文件。比如我在led_rt8547.h头文件中添加了一个#include <linux/mutex.h>。编译的时候找不到mutex.h这个头文件。这是因为我们在Android.mk中根本就没有包含mutex.h头文件所在的路径。为此我有以下几点意见:

1.应用程序一般都是使用IOCTL命令和一些宏信息,所以在驱动模块的头文件中,不要添加一些和kernel相关的结构体,尽量使用我们自己定义的数据类型。

2.头文件尽量少包含与kernel紧密联系的头文件(有联系的就要将头文件路径加到android.mk中),要不然也找不到头文件(除非已经包含了头文件路径),我们自己定义的其它头文件是可以添加进来。

3.模块中和内核紧密联系的结构体,尽量放在驱动源码中,避免编译报错。应用层其实也用不到这些结构体,那么就不要放到头文件中了。

















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值