驱动层需要实现的结构体是struct platform_driver,它用来描述一个设备的驱动信息。
结构如下 (include\linux\platform_device.h)
1 struct platform_driver {2 int (*probe)(struct platform_device *);//探测3 int (*remove)(struct platform_device *);//移除4 void (*shutdown)(struct platform_device *);//关闭5 int (*suspend)(struct platform_device *, pm_message_t state);//挂起6 int (*resume)(struct platform_device *);//唤醒7 struct device_driver driver;8 struct platform_device_id *id_table;9 };
其中probe和remove是必需实现的,跟在设备层提到的注册和注销函数有关。其他函数根据需要,自行实现。
struct device_driver中的name成员很重要,必须要和设备层struct platform_device中的name成员相同,这样才能实现驱动层和设备层的绑定。
结构如下 (include\linux\device.h)
struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p;};
平台驱动层核心API
int platform_driver_register(struct platform_driver *drv) ( drivers\base\platform.c)
void platform_driver_unregister(struct platform_driver *drv) ( drivers\base\platform.c)
struct resource *platform_get_resource( struct platform_device *dev, unsigned int type, unsigned int num) ( drivers\base\platform.c)
int platform_get_irq( struct platform_device *dev, unsigned int num) ( drivers\base\platform.c)
平台驱动的实现步骤:
1.编写probe函数
2.编写remove函数
3.定义struct platform_drv结构,并填充这个结构(注,此结构是自己定义的,结构中包含的内容也是自己定义的;这个结构其实是将驱动函数中用到的变量,写到了一起,定义成此结构,方便了其它函数的调用)
4.在模块的初始化函数中调用platform_driver_register(struct platform_driver *drv), 以注册
5.在模块卸载函数中调用platform_driver_unregister(struct platform_driver *drv), 以注销
实现代码如下:leds_platform_driver.c
1 #include2 #include 3 #include 4 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 17 18 #define uint unsigned int 19 #define uchar unsigned char 20 21 struct led_info 22 { 23 unsigned int pin_bit; 24 }; 25 26 struct gpioreg 27 { 28 volatile unsigned long vr_gpbcon; 29 volatile unsigned long vr_gpbdat; 30 volatile unsigned long vr_gpbup; 31 }; 32 33 //自己定义的struct platform_drv结构 34 struct s3c2440_leds_drv{ 35 dev_t dev_nr; 36 struct cdev *cdev_led; 37 struct class *led_class; 38 struct resource *ledres; 39 struct led_info *led_pdata; 40 struct device *this_device; 41 struct gpioreg *vr_gpioreg; 42 struct file_operations leds_ops; 43 }; 44 45 #define DEVICE_NAME "s3c2440leds" 46 #define LED_ON 1 47 #define LED_OFF 0 48 49 static struct s3c2440_leds_drv s3c2440_leds_drv_data; 50 static int leds_open(struct inode *inode, struct file *file) 51 { 52 unsigned int tmp=0, up=0, dat=0; 53 unsigned int i=0; 54 uint pin_bit=s3c2440_leds_drv_data.led_pdata->pin_bit; 55 56 tmp=s3c2440_leds_drv_data.vr_gpioreg->vr_gpbcon; 57 up=s3c2440_leds_drv_data.vr_gpioreg->vr_gpbcon; 58 dat=s3c2440_leds_drv_data.vr_gpioreg->vr_gpbup; 59 printk("leds_open:1111\r\n"); 60 61 for(;i<16;i++) 62 { 63 if((1< vr_gpbcon=tmp; 73 s3c2440_leds_drv_data.vr_gpioreg->vr_gpbup=up; 74 s3c2440_leds_drv_data.vr_gpioreg->vr_gpbdat=dat; 75 return 0; 76 } 77 78 static long leds_ioctl(struct file *filp, uint cmd, unsigned long arg) 79 { 80 uint tmp=0,i=0,j=0; 81 uint ledbit[16]={ 0}; 82 uint pin_bit=s3c2440_leds_drv_data.led_pdata->pin_bit; 83 84 for(i=0;i<16;i++) 85 { 86 if((1< =j) 95 return -EINVAL; 96 mb();//防止编译器对下面的代码进行优化 97 tmp=s3c2440_leds_drv_data.vr_gpioreg->vr_gpbdat; 98 mb(); 99 100 printk("cmd:%d tmp:%d\r\n",cmd,tmp);101 102 switch(cmd)103 {104 case LED_ON:105 tmp &=~(1< vr_gpbdat=tmp;107 return 0;108 109 case LED_OFF:110 tmp|=1< vr_gpbdat=tmp;112 return 0;113 114 default:115 printk("ioctl() cmd is err\r\n");116 return -EINVAL;117 }118 }119 120 static int led_probe(struct platform_device *pdev)121 {122 int msize,val,err;123 // printk("%s is call\r\n",_FUNCTION_);124 printk("led_probe is call\r\n");125 126 //获取平台数据127 s3c2440_leds_drv_data.led_pdata=pdev->dev.platform_data;128 129 if(!s3c2440_leds_drv_data.led_pdata)130 {131 printk("get platform_data error\n");132 return -1;133 }134 135 s3c2440_leds_drv_data.ledres=platform_get_resource(pdev,IORESOURCE_MEM,0);136 if(!s3c2440_leds_drv_data.ledres)137 {138 printk("get resource error\n");139 return -1;140 }141 //142 msize=s3c2440_leds_drv_data.ledres->end-s3c2440_leds_drv_data.ledres->start+1;143 //向内核申请资源144 s3c2440_leds_drv_data.ledres=request_mem_region(145 s3c2440_leds_drv_data.ledres->start,msize,DEVICE_NAME);146 if(!s3c2440_leds_drv_data.ledres)147 {148 printk("request resource error\n");149 return -1;150 }151 152 s3c2440_leds_drv_data.vr_gpioreg=ioremap_nocache(153 s3c2440_leds_drv_data.ledres->start,msize);154 if(!s3c2440_leds_drv_data.vr_gpioreg)155 {156 printk("ioremap_nocache error\n");157 release_region( s3c2440_leds_drv_data.ledres->start,msize);158 return -1;159 }160 memset( s3c2440_leds_drv_data.vr_gpioreg,0,sizeof(struct gpioreg));161 //申请设备号162 val=alloc_chrdev_region(&s3c2440_leds_drv_data.dev_nr,0,1,DEVICE_NAME);163 if(val)164 {165 printk("request dev_nr error\n");166 iounmap(s3c2440_leds_drv_data.vr_gpioreg);167 release_region(s3c2440_leds_drv_data.ledres->start,msize);168 return -1;169 }170 //开辟cdev结构空间,并且初始化cdev结构171 s3c2440_leds_drv_data.cdev_led=cdev_alloc();172 cdev_init(s3c2440_leds_drv_data.cdev_led,&s3c2440_leds_drv_data.leds_ops);173 s3c2440_leds_drv_data.leds_ops.owner=THIS_MODULE,174 s3c2440_leds_drv_data.leds_ops.open=leds_open,175 s3c2440_leds_drv_data.leds_ops.unlocked_ioctl=leds_ioctl,176 //添加字符设备到内核177 val=cdev_add(s3c2440_leds_drv_data.cdev_led,178 s3c2440_leds_drv_data.dev_nr,1);179 if(val)180 {181 printk("cdev_add error\n");182 kfree(s3c2440_leds_drv_data.cdev_led);183 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);184 iounmap(s3c2440_leds_drv_data.vr_gpioreg);185 release_region(s3c2440_leds_drv_data.ledres->start,msize);186 printk(KERN_INFO "Add device led error!\n");187 return -1;188 }189 190 s3c2440_leds_drv_data.led_class=class_create(THIS_MODULE,DEVICE_NAME);191 if(IS_ERR(s3c2440_leds_drv_data.led_class))192 {193 printk("Err:failed in creating class.\n");194 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);195 cdev_del(s3c2440_leds_drv_data.cdev_led);196 kfree(s3c2440_leds_drv_data.cdev_led);197 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);198 iounmap(s3c2440_leds_drv_data.vr_gpioreg);199 release_region(s3c2440_leds_drv_data.ledres->start,msize);200 return PTR_ERR(s3c2440_leds_drv_data.led_class);201 }202 203 s3c2440_leds_drv_data.this_device=device_create(s3c2440_leds_drv_data.led_class,NULL,s3c2440_leds_drv_data.dev_nr,NULL,"%s",DEVICE_NAME);204 if(IS_ERR(s3c2440_leds_drv_data.this_device)){205 printk("Err:failed in creating device_create.\n");206 class_destroy(s3c2440_leds_drv_data.led_class);207 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);208 cdev_del(s3c2440_leds_drv_data.cdev_led);209 kfree(s3c2440_leds_drv_data.cdev_led);210 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);211 iounmap(s3c2440_leds_drv_data.vr_gpioreg);212 release_region(s3c2440_leds_drv_data.ledres->start,msize);213 err=PTR_ERR(s3c2440_leds_drv_data.this_device);214 return err;215 }216 217 printk(KERN_INFO "LED Initilized I'm in!! ^_^\n");218 return 0;219 220 }221 222 static int led_remove(struct platform_device *pdev)223 {224 device_destroy(s3c2440_leds_drv_data.led_class,s3c2440_leds_drv_data.dev_nr);225 226 class_destroy(s3c2440_leds_drv_data.led_class);227 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);228 cdev_del(s3c2440_leds_drv_data.cdev_led);229 kfree(s3c2440_leds_drv_data.cdev_led);230 unregister_chrdev_region(s3c2440_leds_drv_data.dev_nr,1);231 iounmap(s3c2440_leds_drv_data.vr_gpioreg);232 release_region(s3c2440_leds_drv_data.ledres->start,233 s3c2440_leds_drv_data.ledres->end-s3c2440_leds_drv_data.ledres->start+1);234 kfree(s3c2440_leds_drv_data.ledres);235 236 printk("led driver remove\n");237 return 0;238 }239 240 static struct platform_driver led_driver={241 .probe=led_probe,242 .remove=led_remove,243 .driver={244 .owner=THIS_MODULE,245 .name="s3c2440leds",246 },247 };248 249 static int __init leddrv_init(void)250 {251 platform_driver_register(&led_driver);252 return 0;253 }254 255 static void __init leddrv_exit(void)256 {257 platform_driver_unregister(&led_driver);258 }259 260 module_init(leddrv_init);261 module_exit(leddrv_exit);262 263 MODULE_LICENSE("GPL");
注:代码中红色标注部分是字符设备cdev驱动中函数,而定义的struct s3c2440_leds_drv结构中很多是cdev中用到的变量。
代码中pin_bit[i]中的注释部分,结合led的控制寄存器,可以理解。
ps.结合上一节leds_platform_device.c中提到的platform_data来理解本代码中probe函数下的s3c2440_leds_drv_data.led_pdata = pdev->dev.platform_data, 这样就可以理解为什么struct platform_data可以赋值给struct led_pdata(不同的结构体 之间是不能赋值的)。
makefile文件
1 #Makefile 2 obj-m:=leds_platform_drivers.o leds_platform_device.o 3 # leds_platform_driver.o 4 5 KDIR:=/opt/FriendlyARM/mini2440/linux-2.6.32.2 6 PWD:=$(shell pwd) 7 modules: 8 $(MAKE) -C $(KDIR) M=$(PWD) modules 9 # arm-linux-gcc leds_platform_drivers.c -o leds.o10 arm-linux-gcc leds_platform_test.c leds_platform_test11 # cp leds_platform_device.ko leds_platform_driver.ko leds_platform_test /opt/FriendlyARM/12 13 modules_install:14 $(MAKE) -C $(KDIR) M=$(PWD)15 16 # rm -rf *.o *.mod.o *mod.c *symvers. *.o. *.cmd modules.order *.bak.tmp_versions17 18 clean: 19 rm -rf *.ko *.o *.mod.o *.mod.c *symvers. *.o. *.cmd modules.order *.bak .tmp_versions
参考文献:《嵌入式Linux高级驱动教程》 电子工业出版社 深圳信盈达电子有限公司 陈志发 周中孝 李志超 编著