總線分成三部分:
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)