platform設備驅動簡介

簡介:

目的:說白了就是爲了將設備與驅動分離,通過platform總線進行連接





廢話不多說:

相關結構介紹:

1.platform設備

結構體structplatform_device{

const charname;/*設備名*/

u32 id;/*設備id*/

struct device dev;/*設備*/

u32 num_resource;/*設備所使用各類資源數量*/

struct resource resource;/*資源*/

}

這我現在只想說一個成員constcharname;它表示設備的名稱,一會我們介紹platform_driver的時候你會看到它的成員driver也有這個字段,platform總線檢查這兩個字段如果匹配就將platform設備和驅動關聯起來





2.platform驅動

structplatform_driver{

int(*probe)(struct platform_device *);

int(*remove)(struct platform_device *);

structdevice_driver driver;

};

這裏主要介紹上述3個成員

probe函數:正如字面意思(探針),platform設備與驅動匹配後會調用此函數,我們對字符設備的註冊的工作可以在這裏完成


remove函數:對字符設備的註銷工作在這裏完成

driver:包含兩個字段

.name:需要與前面的platform_device中的name段保持一致,才能完成匹配

.owner:一般設置爲THIS_MODULE



思考:系統如何完成platform設備和驅動的匹配?

系統爲platform總線定義了一個bus_type(總線類型)的實例platform_bus_type,在此結構體中有一個成員函數:

.match,系統就是靠這個函數完成匹配的



驅動編寫:

編寫platform驅動要完成三方面的工作:

1.platform_device的編寫

2.platform_driver的編寫

3.設備資源和數據的定義



對於platform_device的編寫又有兩方法:

(1).BSP板文件中實現定義,在板文件中將platform_device被歸納爲一個數組,最終通過platform_add_devices()函數統一註冊,這個函數內部其實調用了platform_device_register()單個註冊平臺設備



例如這裏我們要實現一個名爲“globalfifo”的設備,我們可以找板文件,對於ok6410來說位於arch/arm/match-s3c64XX/match-smdk6410中,添加如下代碼:



編譯好內核後在/sysfs中會出現如下節點:

/sys/bus/platform/devices/globalfifo/

/sys/devices/platform/globalfifo/



然後你只需要編寫對應的platform_driver驅動程序就可以了。

但是這種方法存在缺點,如果你想要改寫platform_device就需要重新修改板文件,換句話說需要重新編譯內核





(2)第二種是爲platform_device單獨編寫內核模塊然後加載到內核中,在本文最後會以此方式給出一個例子程序,這裏就不在多做介紹




 



platform_device設備資源的定義:

platform驅動習慣上將設備資源單獨定義,需要的時候再將其加載到內存中去。

Platform設備資源和數據:

structresource{

resource_size_tstart;

resource_size_tend;

constchar *name;

unsignedlong flags;

structresourceparent,*sibling,*child;

};


對於此結構體通常只關心三個字段

start:資源開始值

end:資源結束值

flags:資源類型


資源類型的定義:

flagsIORESOURCE_MEM的時候,startend表示該platform_device佔據的內存的開始地址和結束地址


flagsIORESOURCE_IRQ時,startend表示該platform使用的中斷號的開始值和結束值


當然對於resource的定義也有兩種方法:

1.在板文件中定義

2.platform_device所處的內核模塊代碼中編寫





(1).在板文件中進行定義:

例如DM9000網卡資源的定義



 


獲取資源:

無論是在板文件中定義還是直接在設備代碼中定義,我們在platform_driver中要獲取這些資源的方都是一樣的:

resouce*platform_get_resouce(

structplatform_device *pdev,

intflags;//資源類型

intnum;//資源數組索引

)



(2)platform_device模塊文件中定義,

在本文末尾將給出一個例子程序,這裏就不在多做介紹






補充:

我們除了可以定義資源以外還可以定義數據

platform_device成員dev裏有一個成員叫做platform_data

這個成員就可以存放數據信息

這裏還是以DM9000網卡爲例:





同樣對於它的定義你仍然可以放到platform_device模塊中去




數據的獲得:

這個很容以在platform驅動模塊中直接從pdev(platform_device變量)中去得

:

struct dm9000_plat_data *pdata = pdev->dev.platform_data;









在本文的最後我們以ok6410下的led程序爲例,以第二種方法編寫一個platform_led驅動

 

 

platform_led_dev.c

#include <linux/module.h> 
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/fs.h> 
#include <asm/uaccess.h> /* copy_to_user,copy_from_user */   
#include <linux/pci.h>   
#include <mach/map.h>   
#include <linux/sched.h> 
#include <linux/gpio.h>     

 

 
struct platform_device *my_led_dev; 
 
static int __init platform_dev_init(void) 

    int ret; 
     
 //分配一個 platform_device結構體
    my_led_dev = platform_device_alloc("platform_led", -1);   
     
    ret = platform_device_add(my_led_dev);//將自定義的設備添加到內核設備架構中
     
    if(ret) 
        platform_device_put(my_led_dev);//銷燬platform設備結構 
     
    return ret; 

 
static void __exit platform_dev_exit(void) 

    platform_device_unregister(my_led_dev);//註銷platform_device

 
module_init(platform_dev_init); 
module_exit(platform_dev_exit); 
 
MODULE_AUTHOR("Sola"); 
MODULE_LICENSE("GPL"); 

 

 

 


 



platform_led_drv.c
//platform driver 
#include <linux/module.h> 
#include <linux/types.h> 
#include <linux/uaccess.h>
#include <linux/miscdevice.h> 
#include <linux/fs.h> 
#include <linux/init.h> 
#include <linux/platform_device.h>
#include <linux/pci.h>   
#include <mach/map.h>   
#include <mach/regs-gpio.h>   
#include <mach/gpio-bank-m.h>   
#include <plat/gpio-cfg.h>    
#define LED_MAJOR 240

 
static int s3c6410_led_open(struct inode *inode, struct file *file) 

   unsigned tmp;    
          tmp = readl(S3C64XX_GPMCON);    
     tmp = (tmp & ~(0xFFFF))|(0x1111U);      
          writel(tmp, S3C64XX_GPMCON);  
     printk("#########open######\n"); 
     return 0; 

 
 
static int s3c6410_led_close(struct inode *inode, struct file *file) 

    printk("#########release######\n"); 
    return 0; 

 
 
static int s3c6410_led_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 

     printk("#########read######\n"); 
     return count; 


static int s3c6410_led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos) 

     char wbuf[10]; 
     unsigned tmp;    
     printk("#########write######\n"); 
     copy_from_user(wbuf,buf,count);
  if(wbuf[0]==1)//1號燈亮
     switch(wbuf[1]) 
     { 
         case 0:  //off 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp |= (0x1U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         case 1:  //on 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp &= ~(0x1U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         default : 
             break; 
     }      
 
  if(wbuf[0]==2)//2號燈亮
     switch(wbuf[1]) 
     { 
         case 0:  //off 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp |= (0x2U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         case 1:  //on 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp &= ~(0x2U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         default : 
             break; 
     } 
 
  if(wbuf[0]==3)//3號燈亮
     switch(wbuf[1]) 
     { 
         case 0:  //off 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp |= (0x4U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         case 1:  //on 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp &= ~(0x4U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         default : 
             break; 
     } 
 
  if(wbuf[0]==4)//4號燈亮
     switch(wbuf[1]) 
     { 
         case 0:  //off 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp |= (0x8U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         case 1:  //on 
             tmp = readl(S3C64XX_GPMDAT);    
                           tmp &= ~(0x8U);    
                           writel(tmp, S3C64XX_GPMDAT); 
             break; 
         default : 
             break; 
     } 
     return count; 

 
 
static struct file_operations led_fops = { 
    .owner   =   THIS_MODULE, 
    .open    =   s3c6410_led_open, 
    .release =   s3c6410_led_close,  
    .read    =   s3c6410_led_read,
 .write   =   s3c6410_led_write,
}; 

 
 
static int my_plat_probe(struct platform_device *dev) 

    int rc;
 printk("Test platform_led dev\n");
 //註冊設備
 rc = register_chrdev(LED_MAJOR,"platform_led",&led_fops);

  if (rc <0) 
     { 
         printk ("register %s char dev error\n","led"); 
         return -1; 
     } 
     printk ("ok!\n"); 
     return 0;   

 
static int my_plat_remove(struct platform_device *dev) 

    printk("my platfrom device has removed.\n");  
    return 0; 

 
struct platform_driver my_led_drv = {  
    .probe = my_plat_probe, 
    .remove = my_plat_remove, 
    .driver = {  
        .owner = THIS_MODULE, 
        .name = "platform_led", 
    }, 
}; 
 
static int __init platform_drv_init(void) 

    int ret; 
 
    ret = platform_driver_register(&my_led_drv); 
     
    return ret; 

 
static void __exit platform_drv_exit(void) 

    platform_driver_unregister(&my_led_drv); 

 
module_init(platform_drv_init); 
module_exit(platform_drv_exit); 
 
MODULE_AUTHOR("Sola"); 
MODULE_LICENSE("GPL"); 
發佈了2 篇原創文章 · 獲贊 4 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章