混雜設備、字符設備、平臺設備三者的註冊方式比較

轉載於http://blog.csdn.net/linxiaowu66/article/details/7639590

混雜設備、字符設備、平臺設備三者的註冊方式比較。

今天沒事幹,順便總結一下設備註冊的幾種方式,有的方式已經不提倡使用了,所以大家可以隨便一看,記得以後使用最新的方法就行了。

首先說已經不提倡使用的字符設備註冊方法:register_chrdev()函數。這個函數的功能是註冊字符設備,獲得主設備號,並將爲給定的主設備號註冊0~255作爲次設備號,同時爲每個設備建立一個默認的cdev結構。如果它的第一個形參你填0的話,將是系統自動分配主設備號,建議這樣做。很好理解的一個函數。不過設備文件還得自己創建,可以用mknod。

其次是2.6內核以及3.0內核使用的全新的註冊方式:cdev註冊法。這個方法的使用歸結於兩步:第一、獲得你的設備的主次設備號,可以用這個函數:register_chrdev_region(),也可以用alloc_chrdev_region()。推薦使用後者。

第二、依照下面的步驟自己依據實際情況填充:struct cdev*my_cdev=cdev_alloc();

my_cdev->ops=&my_fops;

void cdev_init();

int cdev_add();

void cdev_del();

這就是比較完整的註冊方式,設備文件也得自己創建。

再次是混雜設備。misc-device是一類特殊的字符設備。那什麼設備可以註冊爲字符設備呢?只要是那些原先不是預先確定的字符設備都可以註冊爲混雜設備,如鍵盤、LED等。註冊函數是misc_register();凡是註冊爲這一類的設備主設備都是10,然後纔是各自的次設備號。這個方法有一個很好的優點就是不用自己創建設備節點(設備文件)。它在實現函數裏會使用class_device_creat()或class_creat()。即:

  • 通過alloc_chrdev_region及相關函數分配主/次設備號
  • 使用device_create()創建/dev和/sys節點
  • 使用cdev_init()和cdev_add()將自身註冊爲字符驅動程序

混雜設備只要調用misc_register即可完成上述步驟。

最後是平臺設備。(以下摘自某位前輩的,覺得解了我的疑惑,特複製過來分享,稍微修改了一下)在linux2.6設備模型中,關心總線,設備,驅動這三個實體,總線將設備和驅動綁定,在系統每註冊一個設備的時候,會尋找與之匹配的驅動。相反,在系統每註冊一個驅動的時候,尋找與之匹配的設備,匹配是由總線來完成的。 

一個現實的Linux設備和驅動通常都需要掛接在一種總線上,對於本身依附於PCI、USB、I2C、SPI等的設備而言,這自然不是問題,但是在嵌入式系統裏面,SoC 系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設等卻不依附於此類總線。基於這一背景,Linux 發明了一種虛擬的總線,稱爲platform總線。SOC系統中集成的獨立外設單元(I2C,LCD,SPI,RTC等)都被當作平臺設備來處理,而它們本身是字符型設備。 從Linux2.6內核起,引入一套新的驅動管理和註冊機制:platform_device和 platform_driver 。Linux 中大部分的設備驅動,都可以使用這套機制,設備用 platform_device表示;驅動用platform_driver 進行註冊。

  platform_device 結構體include/linux/platform_device.h

  structplatform_device

  {

  constchar *name; //設備名

  u32id;

  structdevice dev;

  u32num_resources; //設備所使用的各類資源數量

  structresource *resource; //使用的資源

  }

所謂的resource,具體是與板級硬件密切相關的,比如控制器映射到soc內存的地址範圍,外部中斷引腳等,

當然,要把定義的這個resources[]賦值給platform_device的.resource字段,同時要設置.num_resources資源個數。

設備除了可以再bsp中定義資源以外,還可以附加一些數據信息,因爲對設備的硬件描述除了中斷,內存,DMA通道以外,可能還會有一些配置信息,而這些配置信息也依賴於BSP,不宜直接放置在設備驅動本身,因此platform也提供了platform_data的支持,platform_data可以自定義,比如DM9000驅動,platform_data中可以存放mac地址,總線寬度,板上有誤eeprom等信息。用platform_data描述它的一些屬性:

  staticstruct dm9000_plat_data s3c_dm9000_platdata = {

  .flags =DM9000_PLATF_16BITONLY,

  };

  staticstruct platform_device s3c_device_dm9000 = {

  .name ="dm9000",

  .id =0,

  .num_resources =ARRAY_SIZE(s3c_dm9000_resource),

  .resource =s3c_dm9000_resource,

  .dev ={

  .platform_data =&s3c_dm9000_platdata,

  }

  };

  在相應的驅動中使用:

  structdm9000_plat_data *pdata =pdev->dev.platform_data;

  可獲取platform_data

 

  platform_driver 結構體include/linux/platform_device.h

  structplatform_driver

  {

  int(*probe)(struct platform_device *);

  int(*remove)(struct platform_device *);

  void(*shutdown)(struct platform_device *);

  int(*suspend)(struct platform_device *, pm_message_tstate);

  int(*suspend_late)(struct platform_device *, pm_message_tstate);

  int(*resume_early)(struct platform_device *);

  int(*resume)(struct platform_device *);

  structpm_ext_ops *pm;

  structdevice_driver driver;

  };

  系統爲platform總線定義一個bus_type的實例platform_bus_type,通過其成員函數match(),確定device和driver如何匹配。

  匹配platform_device和platform_driver主要看二者的name字段是否相同。(name必須要相同才能匹配)

  用platform_device_register()函數註冊單個的平臺設備。

  一般是在平臺的BSP文件中定義platform_device,通過platform_add_devices()函數將平臺設備註冊到系統中

  platform_driver 的註冊與註銷:

  platform_driver_register()

  platform_driver_unregister()

  以s3c2440LCD驅動爲例:

  在BSP文件中:

  structplatform_device s3c_device_lcd = {

  .name ="s3c2410-lcd",

  .id =-1,

  .num_resources =ARRAY_SIZE(s3c_lcd_resource),

  .resource =s3c_lcd_resource,

  .dev ={

  .dma_mask =&s3c_device_lcd_dmamask,

  .coherent_dma_mask =0xffffffffUL

  }

  };

  爲了完成LCD設備的註冊,將其放進/arch/arm/mach-s3c2440/mach-smdk2440.c中定義的smdk2440_devices數組中:

  staticstruct platform_device *smdk2440_devices[] __initdata ={

  ........

  ........

  &s3c_device_lcd,

  };

  由platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));註冊到系統中

struct platform_drives3c_driver_lcd=

{

    .remove  = s3c2410fb_remove, //驅動移除

  .suspend= s3c2410fb_suspend,

  .resume= s3c2410fb_resume,

  .driver= {

  .name ="s3c2410-lcd", //和platform_device中的name相同

  .owner =THIS_MODULE,

  },

 };

  staticint __devinit s3c2410_fb_init(void)

  {

  returnplatform_driver_register(&s3c2410fb_driver);

  }

  staticvoid __exit s3c2410fb_cleanup(void)

  {

  platform_driver_unregister(&s3c2410fb_driver);

  }

  註冊成功後會在下面兩個目錄下看到設備節點:

  /sys/bus/platform/devices/

  /sys/devices/platform/

  平臺設備資源和數據:

  resource結構體:

  structresource

  {

  resource_size_t start;

  resource_size_t end;

  constchar *name;

  unsignedlong flags;

  structresource *parent, *sibling, *child;

  };

  我們通常關心start、end 和flags 這3個字段,分別標明資源的開始值、結束值和類型,flags

  可以爲IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。

  如LCD資源:

  staticstruct resource s3c_lcd_resource[] = {

  [0] ={

  .start =S3C24XX_PA_LCD, //LCD的IO資源起始地始(LCD控制器寄存器地址)

  .end =S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1, //結束地址

  .flags =IORESOURCE_MEM,

  },

  [1] ={

  .start =IRQ_LCD, //LCD中斷號

  .end =IRQ_LCD,

  .flags =IORESOURCE_IRQ,

  }

  };

  在driver中用platform_get_resource()或platform_get_irq()等函數獲取設備資源

  structresource *platform_get_resource(struct platform_device *, unsignedint, unsigned int);

  intplatform_get_irq(struct platform_device *dev, unsigned intnum);

  獲取到的內存或IO資源,需要ioremap後才能使用

  獲取到的IRQ資源,需要request_irq

    找一個和平臺相關的驅動程序,從BSP文件開始分析它的結構,一直分析到它的最底層的硬件操作,這樣很快就能熟悉platform的工作原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章