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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动

發布時間:2024/1/17 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
linux設備驅動歸納總結(九):1.platform總線的設備和驅動


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

這一節可以理解是第八章的延伸,從這節開始介紹platform設備驅動。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


一、什么是paltform總線


一個現實的linux設備和驅動通常都需要掛接在一種總線上,比較常見的總線有USBPCI總線等。但是,在嵌入式系統里面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設卻不依附與此類總線。基于這樣的背景下,2.6內核加入了platform虛擬總線。platform機制將設備本身的資源注冊進內核,有內核統一管理,在驅動程序使用這些資源時使用統一的接口,這樣提高了程序的可移植性。

如果簡單的說,就像我在第八章第三節模擬的usb總線一樣(源代碼路徑:8th_devModule_3/2nd),platform總線對加入到該總線的設備和驅動分別封裝了兩個結構體——platform_deviceplatform_driver。并且提供了對應的注冊函數。當然,前提是要包含頭文件

來個圖:

由上面兩個的關系我們可以看出來,需要在platform總線上注冊設備和驅動,只要定義指定的結構體后調用platform給出的注冊函數就可以了。


下面就介紹一下platform總線、設備和驅動。


1platform總線:


和我之前虛擬的usb總線一樣,linux在系統啟動時就注冊了platform總線,看內核代碼:

/*drivers/base/platform.c*/

604 static int?platform_match(struct device *dev, struct device_driver *drv)

605 {

606???? struct platform_device *pdev;

607

608???? pdev = container_of(dev, struct platform_device, dev);

609???? return (strcmp(pdev->name, drv->name) == 0);?//配對函數檢驗名字是否一致

610 }

。。。。。

949 struct bus_type platform_bus_type = {

950???? .name = "platform", //定義了總線名字為platform,總線注冊后新建目錄sys/bus/platform

951???? .dev_attrs = platform_dev_attrs,

952???? .match = platform_match, //指定配對函數

953???? .uevent = platform_uevent,

954???? .pm = PLATFORM_PM_OPS_PTR,

955 };

可以看到,和我的usb虛擬總線一樣,總線中定義了成員名字和match函數,當有總線或者設備注冊到platform總線時,內核自動調用match函數,判斷設備和驅動的name是否一致


2platform設備:


同樣的,先看一下platform設備對應的結構體paltform_device

/*linux/platform_device.h*/

16 struct platform_device {

17???? const char * name; //設備的名字,這將代替device->dev_id,用作sys/device下顯示的目錄名

18???? int id; //設備id,用于給插入給該總線并且具有相同name的設備編號,如果只有一個設備的話填-1

19???? struct device dev; //結構體中內嵌的device結構體。

20???? u32 num_resources; //資源數。

21?????struct resource * resource; //用于存放資源的數組。

22 };

上面的結構體中先不介紹idnum_resourcesresource。可以看到,platform_device的封裝就是指定了一個目錄的名字name,并且內嵌device

platform_device的注冊和注銷使用以下函數:

/*drivers/base/platform.c*/

322 int platform_device_register(struct platform_device *pdev) //同樣的,需要判斷返回值

。。。

337 void platform_device_unregister(struct platform_device *pdev)

注冊后,同樣會在/sys/device/目錄下創建一個以name命名的目錄,并且創建軟連接到/sys/bus/platform/device下。


3platform驅動:


先看一下platform驅動對應的結構體paltform_driver

/*linux/platform_device.h*/

50 struct platform_driver {

51?????int (*probe)(struct platform_device *);

52?????int (*remove)(struct platform_device *);

53?????void (*shutdown)(struct platform_device *);

54?????int (*suspend)(struct platform_device *, pm_message_t state);

55?????int (*suspend_late)(struct platform_device *, pm_message_t state);

56?????int (*resume_early)(struct platform_device *);

57?????int (*resume)(struct platform_device *);

58?????struct device_driver driver;

59 };

可以看到,platform_driver結構體內嵌了device_driver,并且實現了probremove等操作。其實,當內核需要調用probe函數時,它會調用driver->probe,在dricer->probe中再調用platform_driver->probe。如果想了解清楚的話建議查看內核源代碼。

platform_driver的注冊和注銷使用以下函數:

/*drivers/base/platform.c*/

492 int platform_driver_register(struct platform_driver *drv)

。。。。。

513 void platform_driver_unregister(struct platform_driver *drv)

注冊成功后內核會在/sys/bus/platform/driver/目錄下創建一個名字為driver->name的目錄。


介紹完后,那我就根據第八章第三節(linux設備驅動歸納總結(八):3.設備管理的分層與面向對象思想)的源程序(8th_devModule_3/2nd),將我想象出來的usb鼠標設備和驅動添加到platform總線上:

/*9th_platform_1/1st/device.c*/

4 #include

5

6 void usb_dev_release(struct device *dev)

7 {

8?????printk("?release\n");

9 }

10 struct?platform_device?mouse_dev = {

11?????.name = "plat_usb_mouse", //將以這個名字創建目錄

12?????.dev = {

13?????????.bus_id = "usb_mouse", //不會用這個名字創建目錄了,這里不設置bus_id也行的。

14?????????.release = usb_dev_release,

15?????},

16 };

17

18 static int __init usb_device_init(void)

19 {

20?????int ret;

21

22?????ret =?platform_device_register(&mouse_dev);

23?????if(ret){

24?????????printk("device register failed!\n");

25?????????return ret;

26?????}

27

28?????printk("usb device init\n");

29?????return 0;

30 }

31

32 static void __exit usb_device_exit(void)

33 {

34?????platform_device_unregister(&mouse_dev);

35?????printk("usb device bye!\n");

36 }

一看就很簡單,將之前usb結構體和注冊函數更改為platform類型就可以了。dirver.c也是一樣:

/*9th_platform_1/1st/driver.c */

25 struct platform_driver mouse_drv = {

26?????.probe = usb_driver_probe,

27?????.remove = usb_driver_remove,

28?????.driver = {

29?????????.name = "plat_usb_mouse", ///sys/中的驅動目錄名字

30?????},

31 };

32

33 static int __init usb_driver_init(void)

34 {

35?????int ret;

36?????/*驅動注冊,注冊成功后在/sys/platform/usb/driver目錄下創建目錄

37?????* plat_usb_mouse*/

38?????ret = platform_driver_register(&mouse_drv);

39?????if(ret){

40?????????printk("driver register failed!\n");

41?????????return ret;

42?????}

43?????printk("usb driver init\n");

44?????return 0;

45 }

46

47 static void __exit usb_driver_exit(void)

48 {

49?????platform_driver_unregister(&mouse_drv);

50?????printk("usb driver bye!\n");

51 }


由上面的程序看到,設備和驅動都以”plat_usb_mouse”命名,這樣的話match函數也就能配對成功。

看效果:

[root: 1st]# insmod device.ko

usb device init

[root: 1st]# insmod driver.ko

init usb mouse

usb driver init

[root: 1st]# lsmod

driver 1604 0 - Live 0xbf006000

device 1584 0 - Live 0xbf000000

[root: 1st]# cd /

[root: /]# find -name "*usb_mouse*"

./sys/devices/platform/plat_usb_mouse.0

./sys/bus/platform/devices/plat_usb_mouse.0

./sys/bus/platform/drivers/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse/plat_usb_mouse.0

當我查找usb_mouse時出現了四個目錄,但是為什么后面有個“0”?這個0代表設備的編號,是由paltform_device->id指定的。我的程序沒有設備,所以默認為0。如果你不想的你的目錄名字沒有后綴,那你就設置platform_device->id = -1;

platform_device->id = 3的效果:

[root: /]# find -name "*usb_mouse*"

./sys/devices/platform/plat_usb_mouse.3

./sys/bus/platform/devices/plat_usb_mouse.3

./sys/bus/platform/drivers/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse/plat_usb_mouse.3

platform_device->id = -1的效果:

[root: /]# find -name "*usb_mouse*"

./sys/devices/platform/plat_usb_mouse

./sys/bus/platform/devices/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse/plat_usb_mouse


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


二、platform設備的資源和數據


上面講的usb鼠標都是假的,接下來簡單實現一個led驅動,實現的功能很簡單,加載后燈亮,卸載后燈滅,我的led燈對應管腳GPE12。順便介紹一些platform_device中的resourceplatform_data

再看一下platform_device

16 struct platform_device {

17?????const char * name;

18?????int id;

19?????struct device dev;

20?????u32 num_resources;

21?????struct resource * resource;

22 };

前三個已經介紹了,現在來介紹一下最后兩個。

resource是一個指向platform資源數組的指針,該數組中有num_resource個資源,看一下資源結構體:

/*linux/ioport.h"*/

18 struct resource {

19?????resource_size_t start;

20?????resource_size_t end;

21?????const char *name;

22?????unsigned long flags;

23?????struct resource *parent, *sibling, *child;

24 };

常用的就是紅色標記的三個,分別是資源的開始值,結束值和類型。

常見的flagsIORESOURCE_MEMIORESOURCE_IRQ。其他的可以自己查看include/linux/ioport.h

如果fiagsIORESOURCE_MEMstartend分別是該設備的連續的開始和結束地址,如果不連續你可以定義兩個或者更多的資源結構體。

如果flagsIORESOURCE_IRQstartend分別是該設備連續的開始和結束的連續中斷號,如果不連續可以分開定義。

當然,如果地址或者中斷只有一個,你可以將startend定義成一樣。

device.c定義了led的資源:

/*9th_platform_1/2nd/device.c*/

10 struct resource s3c_led_res[1] = {

11?????[0] = {

12?????????.start = 0x56000000,

13?????????.end = 0x560000ff,

14?????????.flags = IORESOURCE_MEM,

15?????},

16 };

17

18 struct platform_device s3c_led_dev = {

19?????.name = "plat_led",

20?????.id = -1,

21?????.dev = {

22?????????.release = led_dev_release,

23?????},

24?????.num_resources = ARRAY_SIZE(s3c_led_res), //platform資源的數量,為1

25?????.resource = s3c_led_res,

26 };

同時,還要修改一下driver.c中的的proberemoveprobe函數中點亮ledremove滅掉led

/*9th_platform_1/2nd/driver.c*/

9 struct _plat_led_t {

10?????unsigned long phys, virt;

11?????unsigned long gpecon, gpedat, gpeup;

12?????unsigned long reg;

13 };

14

15 struct _plat_led_t pled;

16

17 int led_driver_probe(struct platform_device *pdev)

18 {

19?????pled.phys = pdev->resource[0].start;

20?????pled.virt = ioremap(pled.phys, SZ_4K);

21?????pled.gpecon = pled.virt + 0x40;

22?????pled.gpedat = pled.virt + 0x44;

23?????pled.gpeup = pled.virt + 0x48;

24

25?????//config

26?????pled.reg = ioread32(pled.gpecon);

27?????pled.reg &= ~(3 << 24);

28?????pled.reg |= (1 << 24);

29?????iowrite32(pled.reg, pled.gpecon);

30

31?????//up

32?????pled.reg = ioread32(pled.gpeup);

33?????pled.reg |= (1 << 12);

34?????iowrite32(pled.reg, pled.gpeup);

35

36?????//dat

37?????pled.reg = ioread32(pled.gpedat);

38?????pled.reg &= ~(1 << 12);

39?????iowrite32(pled.reg, pled.gpedat);

40

41?????printk("led on\n");

42?????return 0;

43 }

上面的probe只要看紅色標記就可以了,在platform_device的資源中獲取資源的start,而其他的都是之前介紹過的led操作。

45 int led_driver_remove(struct platform_device *pdev)

46 {

47?????pled.reg = ioread32(pled.gpedat);

48?????pled.reg |= (1 << 12);

49?????iowrite32(pled.reg, pled.gpedat);

50

51?????printk("led off\n");

52?????return 0;

53 }

接下來驗證一下:

[root: 2nd]# insmod device.ko

led device init

[root: 2nd]# insmod driver.ko

led on

led driver init

[root: 2nd]# rmmod driver

led off

led driver bye!

[root: 2nd]# rmmod device

release

led device bye!


最后在介紹一下paltform設備的數據:

device結構體下有一個paltform_data

390 void *platform_data; /* Platform specific data, device

391 core doesn't touch it */

它也說明了,這是用與platformdevice的代碼不會使用該結構體。

這是一個void指針類型,用于存放platform的數據地址,類似字符設備時介紹的private_data。這里就不寫代碼了。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


三、platform設備的靜態注冊


在上面的程序中,設備和驅動都是通過手動加載的,但有些情況下,內核已經為設備注冊到platform總線上,只需要我們注冊驅動就可以了。接下來介紹一下設備的靜態注冊。


先看一下內核是從哪里獲取靜態注冊的platform_device


內核是從?arch/arm/mach-s3c2440/mach-mini2440.c文件中獲取到platform_device的信息:

/*arch/arm/mach-s3c2440/mach-mini2440.c*/

250 static struct platform_device *mini2440_devices[] __initdata = {

251?????&s3c_device_usb,

252?????&s3c_device_rtc,

253?????&s3c_device_lcd,

254?????&s3c_device_wdt,

255?????&s3c_device_led, //這是我新加的

257?????&s3c_device_i2c0,

258?????&s3c_device_iis,

259?????&s3c_device_dm9k,

260?????&net_device_cs8900,

261?????&s3c24xx_uda134x,

262 };

可以看到,這個數組存放著所有靜態注冊的platform_device信息,我按照它們的格式,也添加了一個s3c_device_led結構體指針在這個數組中。接下來就要看看是在哪里定義了。

我全局搜索內核代碼中含有s3c_device_wdt的文件,然后在這結構體后面依樣畫葫蘆。

讓我搜到兩個相關的文件:

1arch/arm/plat-s3c24xx/devs.c


可以看到,內核在在這個文件中聲明并定義s3c_device_wdtplatform_device結構體,所以,我也在這里定義的個strucr platform_device s3c_device_led

/*arch/arm/plat-s3c24xx/devs.c*/

359 /*test by xiaobai*/

360 /* led_test */

361

362 static struct resource s3c_led_resource[] = {

363?????[0] = {

364?????????.start = 0x56000000,

365?????????.end = 0x560000ff,

366?????????.flags = IORESOURCE_MEM,

367?????}

368 };

369

370 struct platform_device s3c_device_led = {

371?????.name = "plat_led",

372?????.id = -1,

373?????.num_resources = ARRAY_SIZE(s3c_led_resource),

374?????.resource = s3c_led_resource,

375 };

376

377 EXPORT_SYMBOL(s3c_device_led);

會發現,上面的內容跟我前面卸載device.c的代碼一模一樣。


2)第二個文件是arch/arm/plat-s3c/include/plat/devs.h


platform_match函數是通過包含該文件后讀取里面的platform_device信息來跟platform_driver匹配。所以,必須在這里加上一行代碼:

/*arch/arm/plat-s3c/include/plat/devs.h*/

32 extern struct platform_device s3c_device_wdt;

33 extern struct platform_device s3c_device_led; //這是我新添加的


修改完上面的3個文件后,重新編譯內核后就實現了靜態注冊platform設備,在內核啟動時會自動注冊s3c_device_led。所以,我們只需要注冊platform驅動就可以了,代碼在driver.c中,和上一個程序一模一樣(2nd/driver.c),我就不貼出來了,可以自己看3th/driver.c。接下來看效果:

Please press Enter to activate this console.

[root: /]# find -name "*plat_led*" //開機后查找plat_led

./sys/devices/platform/plat_led //發現led設備已經被靜態注冊上

./sys/bus/platform/devices/plat_led

[root: /]# cd review_driver/9th_platform/9th_platform_1/3rd/

[root: 3rd]# insmod driver.ko //加載led驅動

led on //燈亮

led driver init

[root: 3rd]# rmmod driver

led off

led driver bye!


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


四、總結


這節由第八章的usb虛擬總線的延伸開始介紹platform的設備和驅動使用和注冊。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

源代碼:?9th_platform_1.rar?

轉載于:https://www.cnblogs.com/huty/p/8518552.html

總結

以上是生活随笔為你收集整理的【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动的全部內容,希望文章能夠幫你解決所遇到的問題。

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