Platform device driver full dialysis

Source: Internet
Author: User
Platform device driver full dialysis

Http://blog.donews.com/21cnbao/archive/2010/07/14/1581997.aspx

Th, 2010 by Song Baohua

1.1 platform bus, devices and drivers

In the device driver model of Linux 2.6, the bus binds the device and the driver with three entities: Bus, device, and driver. When the system registers a device, it looks for a matching driver. On the contrary, when the system registers a driver, it looks for a matching device, the matching is completed by the bus.

A real Linux Device and driver usually need to be mounted on a bus. This is naturally not a problem for devices that are attached to PCI, USB, I2 C, SPI, etc, however, in an embedded system, the independent peripheral controller integrated in the SoC system and peripherals attached to the SOC memory space are not attached to such a general line. Based on this background, Linux invented a virtual bus called platform bus. The corresponding device is called platform_device, and the driver becomes platform_driver.

Note that the so-called platform_device is not tied with character devices, Block devices, and network devices, but an additional means provided by the Linux system, for example, the built-in controllers such as I2 C, RTC, SPI, LCD, and watchdog are classified as platform_device, which are character devices. The platform_device struct is defined as follows:CodeList 1.

Code List 1 platform_device struct

1 struct platform_device {

2 const char * Name;/* device name */

3 u32 ID;

4 struct device dev;

5 u32 num_resources;/* Number of resources used by the device */

6 struct resource * resource;/* Resource */

7 };

The platform_driver struct contains the probe (), remove (), shutdown (), suspend (), and resume () functions, which usually need to be implemented by the driver, such as the code list 2.

Code List 2 platform_driver struct

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 };

In the system, a bus_type instance platform_bus_type is defined for the platform bus. Its definition is as follows: code list 15.3.

Code List 15.3 platform bus bus_type instance 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 );

Here we need to focus on its match () member function, which shows how the platform_device and platform_driver match each other, as shown in code listing 4.

Code list 4: The match () member function of platform_bus_type

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}

From the 6th line of code listing 4, we can see that matching platform_device and platform_driver mainly depends on whether the name fields of the two are the same.

The definition of platform_device is usually implemented in the BSP board file. In the Board file, divide platform_device into an array, and finally register the device through the platform_add_devices () function. The platform_add_devices () function can be used to add platform devices to the system. The prototype of this function is:

Int platform_add_devices (struct platform_device ** devs, int num );

The first parameter of this function is the pointer of the platform device array, and the second parameter is the number of platform devices. It internally calls the platform_device_register () function to register a single platform device.

1.2 use globalfifo as a platform device

Now we need to mount the globalfifo driver in the previous section to the platform bus. We need to complete two tasks:

1. Port globalfifo to the platform driver.

2. Add the globalfifo platform device to the Board file.

To port globalfifo to the platform driver, you need to build a platform_driver shell in the driver of the original globalfifo character device, such as listing 5. Note that after this operation, globalfifo is not changed to the nature of the character device, but is mounted to the platform bus.

Code List 5 add platform_driver to globalfifo

1 static int _ devinit global1_o_probe (struct platform_device * pdev)

2 {

3 int ret;

4 dev_t devno = mkdev (global1_o_major, 0 );

5

6/* apply for a device Number */

7 if (global1_o_major)

8 ret = register_chrdev_region (devno, 1, "globalfifo ");

9 else {/* dynamically apply for a device Number */

10 ret = alloc_chrdev_region (& devno, 0, 1, "globalfifo ");

11 global1_o_major = major (devno );

12}

13 if (Ret <0)

14 return ret;

15/* dynamically apply for memory of device struct */

16 globalbench o_devp = kmalloc (sizeof (struct globalbench o_dev), gfp_kernel );

17 if (! Global1_o_devp) {/* application failed */

18 ret =-enomem;

19 goto fail_malloc;

20}

21

22 memset (global1_o_devp, 0, sizeof (struct global1_o_dev ));

23

24 global1_o_setup_cdev (global1_o_devp, 0 );

25

26 init_mutex (& global1_o_devp-> SEM);/* initialize the semaphore */

27 init_waitqueue_head (& global1_o_devp-> r_wait);/* initialize the read wait queue header */

28 init_waitqueue_head (& global1_o_devp-> w_wait);/* initialize the write wait queue header */

29

30 return 0;

31

32 fail_malloc: unregister_chrdev_region (devno, 1 );

33 return ret;

34}

35

36 static int _ devexit global1_o_remove (struct platform_device * pdev)

37 {

38 cdev_del (& global1_o_devp-> cdev);/* log out of cdev */

39 kfree (global1_o_devp);/* release the device struct memory */

40 unregister_chrdev_region (mkdev (global1_o_major, 0), 1);/* release the device Number */

41 return 0;

42}

43

44 static struct platform_driver global1_o_device_driver = {

45. Probe = global1_o_probe,

46. Remove = _ devexit_p (global1_o_remove ),

47. Driver = {

48. Name = "globalfifo ",

49. Owner = this_module,

50}

51 };

52

53 static int _ init global1_o_init (void)

54 {

55 return platform_driver_register (& global1_o_device_driver );

56}

57

58 static void _ exit global1_o_exit (void)

59 {

60 platform_driver_unregister (& global1_o_device_driver );

61}

62

63 module_init (global1_o_init );

64 module_exit (global1_o_exit );

In code list 5, the module loading and uninstalling functions only register and deregister platform_driver using the platform_driver_register () and platform_driver_unregister () functions, the previous registration and cancellation of character devices have been handed over to the probe () and remove () member functions of platform_driver.

The parts not listed in code list 5 are the same as those of the original globalfifo driver. They are all member functions that implement file_operations as the driver core of the character device.

To add the globalfifo platform device to the Board file, you need to add code in the Board file (for ldd6410, for arch/ARM/mach-s3c6410/mach-ldd6410.c, for example, list 6 of codes.

Code List 6: platform_device corresponding to globalfifo

1 static struct platform_device global1_o_device = {

2. Name = "globalfifo ",

3. ID =-1,

4 };

For the ldd6410 Development Board, in order to complete the above global1_o_device platform_device registration, just put its address into the ARCH/ARM/mach-s3c6410/mach-ldd6410.c defined in the ldd6410_devices array, such:

Static struct platform_device * ldd6410_devices [] _ initdata = {

+ & Global1_o_device,

# Ifdef config_fb_jx_v2

& Amp; cloud_device_fb,

# Endif

& Amp; initi_device_hsmmc0,

...

}

After the ldd6410 driver is loaded, the following nodes are found in sysfs:

/Sys/bus/platform/devices/globalfifo/

/Sys/devices/platform/globalfifo/

Note that the first line of code listing 5 is the same as the second line of code listing 6. The name of platform_device is the same as that of platform_driver, which is the prerequisite for matching.

1.3 platform device resources and data

Pay attention to the 5th ~ Row 6 describes the resources of platform_device. The resource itself is described by the resource structure. Its definition is shown in listing 7.

Code List 7 resouce struct Definition

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 };

We usually care about the start, end, and flags fields, respectively indicating the start value, end value, and type of the resource. flags can be ioresource_io, ioresource_mem, ioresource_irq, ioresource_dma, etc. The meaning of start and end will change with flags. For example, when flags is ioresource_mem, start and end indicate the start address and end address of the memory occupied by the platform_device respectively; when flags is ioresource_irq, start and end indicate the start and end values of the interrupt number used by the platform_device respectively. If only one interrupt number is used, the start and end values are the same. For resources of the same type, there can be multiple copies. For example, if a device occupies two memory regions, two ioresource_mem resource sources can be defined.

The definition of resource is usually carried out in the BSP board file, and is obtained through an API such as platform_get_resource () in a specific device driver. The prototype of this API is:

Struct resource * platform_get_resource (struct platform_device *, unsigned int, unsigned INT );

For example, the following resouce is defined for the dm9000 Nic In the Board file of the ldd6410 Development Board:

Static struct resource ldd6410_dm9000_resource [] = {

[0] = {

. Start = 0 ×18000000,

. End = 0 × 18000000 + 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,

}

};

In the driver of the dm9000 Nic, the three resources are obtained through the following method:

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 );

For IRQ, platform_get_resource () has another encapsulated variant platform_get_irq (). Its prototype is:

Int platform_get_irq (struct platform_device * Dev, unsigned int num );

It actually calls "platform_get_resource (Dev, ioresource_irq, num );".

In addition to defining resources in BSP, the device can also append some data information, because the hardware description of the device may have some configuration information besides the interrupt, memory, and DMA channel, these configuration information also depends on the Board and is not suitable for direct placement in the device driver itself. Therefore, platform also provides support for platform_data. The platform_data format is customized. For example, for a dm9000 Nic, platform_data is a dm9000_plat_data struct. We can put the MAC address, the total line width, and the eeprom information into platform_data:

Static struct dm9000_plat_data ldd6410_dm9000_platdata = {

. Flags = dm9000_platf_16bitonly | dm9000_platf_no_eeprom,

. Dev_addr = {0 × 0, 0 × 16, 0xd4, 0 × 9f, 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,

}

};

In the driver of the dm9000 Nic, platform_data is obtained as follows:

Struct dm9000_plat_data * pdata = pdev-> Dev. platform_data;

Pdev is the pointer of platform_device.

The above analysis shows that the concept of platform introduced in device drivers has at least two advantages:

1. the device is mounted to a bus, so it complies with the device model of Linux 2.6. As a result, supporting sysfs nodes and device power management are all possible.

2. Isolate BSP and driver. Define the resources used by platform devices and the specific configuration information of devices in BSP. In the driver, you only need to obtain resources and data through common APIs, the separation of board-related code and driver code makes the driver more scalable and cross-platform.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.