混杂设备、字符设备、平台设备三者的注册方式比较

来源:互联网 发布:space x 知乎 编辑:程序博客网 时间:2024/05/16 06:28

今天没事干,顺便总结一下设备注册的几种方式,有的方式已经不提倡使用了,所以大家可以随便一看,记得以后使用最新的方法就行了。

首先说已经不提倡使用的字符设备注册方法: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的工作原理。

0 0
原创粉丝点击