日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

platform设备驱动全透析

發布時間:2025/4/16 编程问答 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 platform设备驱动全透析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章?原始出處?、作者信息和本聲明。否則將追究法律責任。http://21cnbao.blog.51cto.com/109393/337609

1.1 platform總線、設備與驅動
在Linux 2.6的設備驅動模型中,關心總線、設備和驅動這3個實體,總線將設備和驅動綁定。在系統每注冊一個設備的時候,會尋找與之匹配的驅動;相反的,在系統每注冊一個驅動的時候,會尋找與之匹配的設備,而匹配由總線完成。 一個現實的Linux設備和驅動通常都需要掛接在一種總線上,對于本身依附于PCI、USB、I2?C、SPI等的設備而言,這自然不是問題,但是在嵌入式系統里面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設等確不依附于此類總 線。基于這一背景,Linux發明了一種虛擬的總線,稱為platform總線,相應的設備稱為platform_device,而驅動成為 platform_driver。 注意,所謂的platform_device并不是與字符設備、塊設備和網絡設備并列的概念,而是Linux系統提供的一種附加手段,例如,在S3C6410處理器中,把內部集成的I2?C、RTC、SPI、LCD、看門狗等控制器都歸納為platform_device,而它們本身就是字符設備。platform_device結構體的定義如代碼清單1所示。 代碼清單1 platform_device結構體 1 struct platform_device { 2 const char * name;/* 設備名 */ 3 u32 id; 4 struct device dev; 5 u32 num_resources;/* 設備所使用各類資源數量 */ 6 struct resource * resource;/* 資源 */ 7 }; platform_driver這個結構體中包含probe()、remove()、shutdown()、suspend()、resume()函數,通常也需要由驅動實現,如代碼清單2。 代碼清單2 platform_driver結構體 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 (*suspend_late)(struct platform_device *, pm_message_t state); 7 int (*resume_early)(struct platform_device *); 8 int (*resume)(struct platform_device *); 9 struct pm_ext_ops *pm; 10 struct device_driver driver; 11}; 系統中為platform總線定義了一個bus_type的實例platform_bus_type,其定義如代碼清單15.3。 代碼清單15.3 platform總線的bus_type 實例platform_bus_type 1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_attrs = platform_dev_attrs, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .pm = PLATFORM_PM_OPS_PTR, 7 }; 8 EXPORT_SYMBOL_GPL(platform_bus_type); 這里要重點關注其match()成員函數,正是此成員表明了platform_device和platform_driver之間如何匹配,如代碼清單4所示。 代碼清單4 platform_bus_type的match()成員函數 1 static int platform_match(struct device *dev, struct device_driver *drv) 2 { 3 struct platform_device *pdev; 4 5 pdev = container_of(dev, struct platform_device, dev); 6 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); 7 } 從代碼清單4的第6行可以看出,匹配platform_device和platform_driver主要看二者的name字段是否相同。 對platform_device的定義通常在BSP的板文件中實現,在板文件中,將platform_device歸納為一個數組,最終通過 platform_add_devices()函數統一注冊。platform_add_devices()函數可以將平臺設備添加到系統中,這個函數的 原型為: int platform_add_devices(struct platform_device **devs, int num); 該函數的第一個參數為平臺設備數組的指針,第二個參數為平臺設備的數量,它內部調用了platform_device_register()函數用于注冊單個的平臺設備。
1.2 將globalfifo作為platform設備
現在我們將前面章節的globalfifo驅動掛接到platform總線上,要完成2個工作: 1. 將globalfifo移植為platform驅動。 2. 在板文件中添加globalfifo這個platform設備。 為完成將globalfifo移植到platform驅動的工作,需要在原始的globalfifo字符設備驅動中套一層 platform_driver的外殼,如代碼清單5。注意進行這一工作后,并沒有改變globalfifo是字符設備的本質,只是將其掛接到了 platform總線。 代碼清單5 為globalfifo添加platform_driver 1 static int __devinit globalfifo_probe(struct platform_device *pdev) 2 { 3 int ret; 4 dev_t devno = MKDEV(globalfifo_major, 0); 5 6 /* 申請設備號*/ 7 if (globalfifo_major) 8 ret = register_chrdev_region(devno, 1, "globalfifo"); 9 else { /* 動態申請設備號 */ 10 ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo"); 11 globalfifo_major = MAJOR(devno); 12 } 13 if (ret < 0) 14 return ret; 15 /* 動態申請設備結構體的內存*/ 16 globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL); 17 if (!globalfifo_devp) { /*申請失敗*/ 18 ret = - ENOMEM; 19 goto fail_malloc; 20 } 21 22 memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev)); 23 24 globalfifo_setup_cdev(globalfifo_devp, 0); 25 26 init_MUTEX(&globalfifo_devp->sem); /*初始化信號量*/ 27 init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化讀等待隊列頭*/ 28 init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化寫等待隊列頭*/ 29 30 return 0; 31 32 fail_malloc: unregister_chrdev_region(devno, 1); 33 return ret; 34 } 35 36 static int __devexit globalfifo_remove(struct platform_device *pdev) 37 { 38 cdev_del(&globalfifo_devp->cdev); /*注銷cdev*/ 39 kfree(globalfifo_devp); /*釋放設備結構體內存*/ 40 unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*釋放設備號*/ 41 return 0; 42 } 43 44 static struct platform_driver globalfifo_device_driver = { 45 .probe = globalfifo_probe, 46 .remove = __devexit_p(globalfifo_remove), 47 .driver = { 48 .name = "globalfifo", 49 .owner = THIS_MODULE, 50 } 51 }; 52 53 static int __init globalfifo_init(void) 54 { 55 return platform_driver_register(&globalfifo_device_driver); 56 } 57 58 static void __exit globalfifo_exit(void) 59 { 60 platform_driver_unregister(&globalfifo_device_driver); 61 } 62 63 module_init(globalfifo_init); 64 module_exit(globalfifo_exit); 在代碼清單5中,模塊加載和卸載函數僅僅通過platform_driver_register()、 platform_driver_unregister()函數進行platform_driver的注冊與注銷,而原先注冊和注銷字符設備的工作已經被 移交到platform_driver的probe()和remove()成員函數中。 代碼清單5未列出的部分與原始的globalfifo驅動相同,都是實現作為字符設備驅動核心的file_operations的成員函數。 為了完成在板文件中添加globalfifo這個platform設備的工作,需要在板文件(對于LDD6410而言,為arch/arm/mach-s3c6410/ mach-ldd6410.c)中添加相應的代碼,如代碼清單6。 代碼清單6 globalfifo對應的platform_device 1 static struct platform_device globalfifo_device = { 2 .name = "globalfifo", 3 .id = -1, 4 }; 對于LDD6410開發板而言,為了完成上述globalfifo_device這一platform_device的注冊,只需要將其地址放入 arch/arm/mach-s3c6410/ mach-ldd6410.c中定義的ldd6410_devices數組,如: static struct platform_device *ldd6410_devices[] __initdata = { + & globalfifo_device, #ifdef CONFIG_FB_S3C_V2 &s3c_device_fb, #endif &s3c_device_hsmmc0, ... } 在加載LDD6410驅動后,在sysfs中會發現如下結點: /sys/bus/platform/devices/globalfifo/ /sys/devices/platform/globalfifo/ 留意一下代碼清單5的第48行和代碼清單6的第2行,platform_device和platform_driver的name一致,這是二者得以匹配的前提。
1.3 platform設備資源和數據
留意一下代碼清單1中platform_device結構體定義的第5~6行,描述了platform_device的資源,資源本身由resource結構體描述,其定義如代碼清單7。 代碼清單7 resouce結構體定義 1 struct resource { 2 resource_size_t start; 3 resource_size_t end; 4 const char *name; 5 unsigned long flags; 6 struct resource *parent, *sibling, *child; 7 }; 我們通常關心start、end和flags這3個字段,分別標明資源的開始值、結束值和類型,flags可以為IORESOURCE_IO、 IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。start、end的含義會隨著flags而變更,如當 flags為IORESOURCE_MEM時,start、end分別表示該platform_device占據的內存的開始地址和結束地址;當 flags為IORESOURCE_IRQ時,start、end分別表示該platform_device使用的中斷號的開始值和結束值,如果只使用了 1個中斷號,開始和結束值相同。對于同種類型的資源而言,可以有多份,譬如說某設備占據了2個內存區域,則可以定義2個IORESOURCE_MEM資 源。 對resource的定義也通常在BSP的板文件中進行,而在具體的設備驅動中透過platform_get_resource()這樣的API來獲取,此API的原型為: struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int); 譬如在LDD6410開發板的板文件中為DM9000網卡定義了如下resouce: static struct resource ldd6410_dm9000_resource[] = { [0] = { .start = 0x18000000, .end = 0x18000000 + 3, .flags = IORESOURCE_MEM }, [1] = { .start = 0x18000000 + 0x4, .end = 0x18000000 + 0x7, .flags = IORESOURCE_MEM }, [2] = { .start = IRQ_EINT(7), .end = IRQ_EINT(7), .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, } }; 在DM9000網卡的驅動中則是通過如下辦法拿到這3份資源: db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 對于IRQ而言,platform_get_resource()還有一個進行了封裝的變體platform_get_irq(),其原型為: int platform_get_irq(struct platform_device *dev, unsigned int num); 它實際上調用了“platform_get_resource(dev, IORESOURCE_IRQ, num);”。 設備除了可以在BSP中定義資源以外,還可以附加一些數據信息,因為對設備的硬件描述除了中斷、內存、DMA通道以外,可能還會有一些配置信息,而 這些配置信息也依賴于板,不適宜直接放置在設備驅動本身,因此,platform也提供了platform_data的支持。platform_data 的形式是自定義的,如對于DM9000網卡而言,platform_data為一個dm9000_plat_data結構體,我們就可以將MAC地址、總 線寬度、有無EEPROM信息放入platform_data: static struct dm9000_plat_data ldd6410_dm9000_platdata = { .flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM, .dev_addr = { 0x0, 0x16, 0xd4, 0x9f, 0xed, 0xa4 }, }; static struct platform_device ldd6410_dm9000 = { .name = "dm9000", .id = 0, .num_resources = ARRAY_SIZE(ldd6410_dm9000_resource), .resource = ldd6410_dm9000_resource, .dev = { .platform_data = &ldd6410_dm9000_platdata, } }; 而在DM9000網卡的驅動中,通過如下方式就拿到了platform_data: struct dm9000_plat_data *pdata = pdev->dev.platform_data; 其中,pdev為platform_device的指針。 由以上分析可知,設備驅動中引入platform的概念至少有如下2大好處: 1. 使得設備被掛接在一個總線上,因此,符合Linux 2.6的設備模型。其結果是,配套的sysfs結點、設備電源管理都成為可能。 2. 隔離BSP和驅動。在BSP中定義platform設備和設備使用的資源、設備的具體配置信息,而在驅動中,只需要通過通用API去獲取資源和數據,做到了板相關代碼和驅動代碼的分離,使得驅動具有更好的可擴展性和跨平臺性。

本文出自 “宋寶華的博客” 博客,請務必保留此出處http://21cnbao.blog.51cto.com/109393/337609

總結

以上是生活随笔為你收集整理的platform设备驱动全透析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。