linux驅動開發之platform平臺總線的編程(一)

總線分成三部分
bus
driver
device
什麼時候用平臺總線
1, 只要有設備的地址和中斷都可以用平臺總線
2, 如果寫的驅動需要在多個平臺中升級使用
3, 平臺總線只是一個功能代碼:將操作方法和操作資源進行了分離。
注意:平臺總線不屬於子系統,只是一個功能。
這裏寫圖片描述
圖1 platform總線模型
(1)平臺總線:struct bus_type總線對象

        struct bus_type platform_bus_type = {
            .name       = "platform",
            .dev_attrs  = platform_dev_attrs,
            .match      = platform_match, //匹配方法
            .uevent     = platform_uevent,
            .pm     = &platform_dev_pm_ops,
        };

(2)pdev

// 描述一個設備的信息
struct platform_device {
    const char  * name;         // 名字,用於匹配
    int     id;                 // 表示不同寄存器組的編號, 一般可以填-1
    struct device   dev;        //父類
    u32     num_resources;      //資源的個數
    struct resource * resource; //資源的詳細信息--描述中斷和內存資源
};
        struct resource {
            resource_size_t start;
            resource_size_t end;
            const char *name;
            unsigned long flags;
            struct resource *parent, *sibling, *child;
        };
    註冊:
        int platform_device_register(struct platform_device *);
    註銷:
        void platform_device_unregister(struct platform_device *);

(3)pdrv

struct platform_driver {
    int (*probe)(struct platform_device *);     //表示匹配之後的函數
    int (*remove)(struct platform_device *);    //解除匹配
    struct device_driver driver;                //父類
    const struct platform_device_id *id_table;  //可以匹配列表
};

註冊和註銷:
extern int platform_driver_register(struct platform_driver *);
extern void platform_driver_unregister(struct platform_driver *);

下面寫設備程序plat_led_dev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
//設備程序

#define LED_GPC0_CONF 0xE0200060
#define LED_GPC0_SIZE  8


//這裏描述多個設備
struct resource  led_resource[] = {
    [0] = {
        .start = LED_GPC0_CONF,
        .end = LED_GPC0_CONF + LED_GPC0_SIZE -1,//寄存器從0開始計算,所以要減1
        .flags = IORESOURCE_MEM,                //表示內存資源
    },
    // 以下部分爲演示部分
    [1] = {
        .start = 8888,
        .end = 8888,                //中斷是沒有連續性的
        .flags = IORESOURCE_IRQ,    //表示中斷資源
    },

    [2] = {
        .start = 0xE0200160,
        .end = 0xE0200160 + 8 -1,
        .flags = IORESOURCE_MEM, 
    },  
};

struct platform_device led_pdev = {
    .name = "s5pv210_led",                      //只要保證與pdrv一致就行
    .id = -1,
    .num_resources = ARRAY_SIZE(led_resource),  //求數組個數
    .resource = led_resource,
};

static int __init plat_led_dev_init(void)
{
    printk("-------%s-----------------\n", __FUNCTION__);

    //註冊一個pdev
    //int platform_device_register(struct platform_device *);   //註冊到總線上
    return platform_device_register(&led_pdev);                 //正常情況返回0

}


static void __exit plat_led_dev_exit(void)
{   
    printk("-------%s-----------------\n", __FUNCTION__);
    //void platform_device_unregister(struct platform_device *);//從總線上註銷出來
    platform_device_unregister(&led_pdev);                      //一般卸載類的沒有返回值

}


module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");


接下來寫驅動程序plat_led_drv.c
其中,platform_driver結構體中,
probe中要做事情:(下一個博客裏面寫)
//爲用戶提供接口
0, 實例化全局的設備對象– kzalloc
1, 申請主設備號—register_chrdev
2, 自動創建設備節點—class_create, device_create
3, 初始化硬件–ioremap
4,實現 file_operation
5,拿到pdev中的資源對硬件進行初始化

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <asm/io.h>

//驅動程序

volatile unsigned long *gpc0_conf;
volatile unsigned long *gpc0_data;

//led_drv_probe中主要任務是拿到資源
//匹配成功第一個要執行的函數
int led_drv_probe(struct platform_device *pdev)//這裏的pdev指針是總線bus傳過來的
{
    printk("-------%s-----------------\n", __FUNCTION__);
    /*
        編寫驅動的套路(下次文件裏面寫)
        0, 實例化全局的設備對象-- kzalloc
        1,  申請主設備號---register_chrdev
        2, 自動創建設備節點---class_create, device_create
        3, 初始化硬件--ioremap
        4,實現 file_operation

    */ 

    // 肯定拿到pdev中的資源對硬件進行初始化
    //獲取到內存資源  
    //參數2--拿到資源的類型
    //參數3--相同資源中第幾個,這裏是第0個,記住是同一類的累加數字
    //platform_get_resource_byname(struct platform_device *, unsigned int, const char *)
    struct resource *addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    //ioremap第二個參數等價於addr_res->end - addr_res->start + 1
    gpc0_conf = ioremap(addr_res->start,  resource_size(addr_res));
    gpc0_data = gpc0_conf + 1;


    //如果要對硬件進行初始化
    *gpc0_conf &= ~(0xff<<12);
    *gpc0_conf |= (0x11<<12);

    *gpc0_data |= (0x3<<3); //亮燈

    //如果要獲取到中斷資源(按鍵)
    //參數3--相同資源中第幾個,這裏是第0個,記住是同一類的累加數字
    //struct resource *irq_res =  platform_get_resource(pdev, IORESOURCE_IRQ, 0);

    //printk("irqno = %d\n", irq_res->start);//打印出起始地址

    int irqno = platform_get_irq(pdev, 0);
    printk("irqno = %d\n", irqno);

    return 0;

}


int led_drv_remove(struct platform_device *pdev)
{
    printk("-------%s-----------------\n", __FUNCTION__);
    return 0;
}


const struct platform_device_id led_id_table[] = {
        {"s5pv210_led", 0x5210},//一定要和pdev裏面的name一致,這裏優先匹配
        {"s3c2410_led", 0x4444},
        {"s3c6410_led", 0x4444},
};

#if 0
//用來參考的
struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
};
#endif

struct platform_driver led_pdrv = {
    .probe = led_drv_probe,         //表示匹配之後的函數
    .remove = led_drv_remove,       //解除匹配
    .driver = {
        .name = "samsung_led_drv",  //隨便寫,但是一定要有
                                    //可以用於和pdev進行匹配的,優先級較id_table低
    },
    .id_table =led_id_table ,       //一定是用於和pdev進行匹配, 優先進行匹配
};


static int __init plat_led_drv_init(void)
{
    printk("-------%s-----------------\n", __FUNCTION__);

    //註冊一個pdrv
    //extern int platform_driver_register(struct platform_driver *);
    return platform_driver_register(&led_pdrv);
}


static void __exit plat_led_drv_exit(void)
{
    printk("-------%s-----------------\n", __FUNCTION__);
    platform_driver_unregister(&led_pdrv);

}


module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");

Makefile

#指定內核源碼的絕對路徑
KERNEL_DIR = /home/ubuntu/s5pv210/kernel/linux-3.0.8
CUR_DIR = $(shell pwd)
MYMODULE = plat_led_dev
MYMODULE2 = plat_led_drv
#MYAPP = buttons_v5

all:
        #make進入到內涵源碼目錄,將當前目錄下的源程序作爲內核模塊一起編譯
        make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
        #arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c

clean:
        #將編譯生成的文件刪除
        make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
        rm -rf (MYAPP)
install:
        cp -raf $(MYAPP) *.ko /opt/rootfs/drv_module

#指定編譯哪個源文件
obj-m = $(MYMODULE).o 
obj-m += $(MYMODULE2).o        

這裏,在開發板轉載程序,可以不分先後順序。
insmod plat_led_dev.ko
insmod plat_led_drv.ko
這裏寫圖片描述
並且可以看到開發板上的兩個LED燈亮了。
可在/sys/bus/platform/中查看程序是否成功加載到總線
程序代碼:led_plat_v1.rar(鏈接:https://pan.baidu.com/s/1i5XRNHr 密碼:qbx2)

發佈了92 篇原創文章 · 獲贊 133 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章