驱动层需要实现的结构体是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 #include <linux/device.h> 2 #include <linux/string.h> 3 #include <mach/hardware.h> 4 5 #include <linux/kernel.h> 6 #include <linux/module.h> 7 #include <linux/init.h> 8 #include <linux/ioctl.h> 9 #include <linux/types.h> 10 #include <linux/cdev.h> 11 #include <linux/device.h> 12 #include <linux/fs.h> 13 #include <mach/map.h> 14 #include <asm/io.h> 15 #include <linux/platform_device.h> 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<<i)&pin_bit) //pin_bit[i]是否为1,i=5,6,7,8 为1时, 64 { //tmp的2i位置1,2i+1位置0,其他位不变, 65 tmp&=~(3<<i*2); //up的i位,dat的i位置1,其他位不变 66 tmp|=(1<<i*2); 67 up|=(1<<i); 68 dat|=(1<<i); 69 } 70 } 71 72 s3c2440_leds_drv_data.vr_gpioreg->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<<i)&pin_bit) 87 { 88 ledbit[j]=i; 89 //printk("ledbit[%d]:%d\r\n",j,i) 90 j++; 91 } 92 } 93 //检查传入的led的编号是否大于led总数 94 if(arg>=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<<ledbit[arg]); 106 s3c2440_leds_drv_data.vr_gpioreg->vr_gpbdat=tmp; 107 return 0; 108 109 case LED_OFF: 110 tmp|=1<<ledbit[arg]; 111 s3c2440_leds_drv_data.vr_gpioreg->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.o 10 arm-linux-gcc leds_platform_test.c leds_platform_test 11 # 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_versions 17 18 clean: 19 rm -rf *.ko *.o *.mod.o *.mod.c *symvers. *.o. *.cmd modules.order *.bak .tmp_versions
参考文献:《嵌入式Linux高级驱动教程》 电子工业出版社 深圳信盈达电子有限公司 陈志发 周中孝 *超 编著