平台设备驱动之平台驱动

时间:2021-01-20 19:05:16

驱动层需要实现的结构体是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高级驱动教程》    电子工业出版社      深圳信盈达电子有限公司   陈志发  周中孝  *超  编著