platform裝置驅動簡介
簡介:
目的:說白了就是為了將裝置與驅動分離,通過platform匯流排進行連線
廢話不多說:
相關結構介紹:
1.platform裝置
結構體structplatform_device{
const char name;/*裝置名*/
u32 id;/*裝置id*/
struct device dev;/*裝置*/
u32 num_resource;/*裝置所使用各類資源數量*/
struct resource resource;/*資源*/
}
這我現在只想說一個成員constcharname;它表示裝置的名稱,一會我們介紹platform_driver的時候你會看到它的成員driver也有這個欄位,
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;
structresource parent,*sibling,*child;
};
對於此結構體通常只關心三個欄位
start:資源開始值
end:資源結束值
flags:資源型別
資源型別的定義:
當flags為IORESOURCE_MEM的時候,start、end表示該platform_device佔據的記憶體的開始地址和結束地址
當flags為IORESOURCE_IRQ時,start、end表示該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");