1 basic
In the device driver often see and platform related fields, distributed in several corners of the driver, which is a more important mechanism in the 2.6 kernel, the principle of understanding it, for the future analysis of the driver is very helpful, the following brief introduction: In the linux2.6 device model, concerned with the bus, device, drive these three entities, the bus will be the device and the driver binding, in the system each registration of a device, will look for the matching driver. Instead, when the system registers for each driver, it looks for the matching device, and the match is done by the bus.
A realistic Linux device and driver usually need to hook up on a bus, for itself is attached to the PCI, USB, I²c, SPI and other devices, this is not a problem, but in the embedded system, the SOC system integrated with the independent peripheral controller, hook-up on the SOC Memory space peripherals are not attached to this type of bus. Based on this background, Linux invented a virtual bus called the platform bus
Independent peripheral units (I2C,LCD,SPI,RTC, etc.) that are integrated in the SOC system are treated as platform devices, and they are character-based devices themselves.
From the Linux2.6 kernel, a new set of driver management and registration mechanisms are introduced: Platform_device and Platform_driver. Most of the device drivers in Linux can use this mechanism, the device is represented by Platform_device, and the driver is registered with Platform_driver.
Platform model-driven programming requires implementation of Platform_device (device) and platform_driver (drive) on the platform (Virtual bus) registration, matching, mutual binding, and then as a common character device for the corresponding application, In short, if you are writing a character-based device platform driver, in the case of following and implementing a specific interface to the device on the platform bus, the core of the character device is the core structure: Cdev, File_operations (he contains the Operation function interface), Dev _t (device number), device file (/dev), etc., because the character driven by the platform mechanism, its essence is character driven.
In general, the 2.6 kernel has been initialized and mounted with a platform bus in the Sysfs file system. So when we write the platform model driver, we need to do two things: 1: Implement the Platform Driver 2: Implement the Platform device, but in the process of implementing these two tasks, there are many other small jobs that need to be implemented, which are described later. The core architecture of the platform model-driven implementation process is simple, as shown below.
Platform Driver Model Programming summary (LED driver based on mini2440 platform)
Platform Drive model three objects: Platform bus, platform device, platform driver.
Platform Bus-corresponding kernel structure: struct bus_type--> It contains the most critical functions: match ()
Platform device-corresponding kernel structure: struct platform_device--> registration: Platform_device_register (unregiste)
Platform driver-corresponding kernel structure: struct platform_driver--> registration: Platform_driver_register (unregiste
)
A brief introduction to the working process of the platform driver: when the device (or driver) registers, it will trigger the bus to call its own match function to find out whether the current platform bus is mounted with a driver (or device) that matches the name of the device (or driver), and if it exists, it binds both parties If the device is registered before the device is registered on the bus, it will not match to the driver with the same name, and then when the driver is registered to the bus, the bus will immediately match the device and driver with the same name as the binding and then call the probe function in the driver. , if the driver is registered first, the same as the device driver will match the failure, the matching failure will cause its probe function is not called, but to wait until the device registration succeeds and bind itself to the binding before it is called.
The following is a detailed procedure for implementing platform drives and devices.
Detailed process for implementing the Platform drive
1: Define driver instance Mini2440_led_platform_driver
static struct Platform_driver Mini2440_led_platform_driver = {
. Probe = Mini2440_led_probe,
. remove = __devexit_p (Mini2440_led_remove),
. Driver = {
. Name = "Mini2440_led_platform_device_driver",
. Owner = This_module,
}
};
2: Implement drive instance member function: Probe Mini2440_led_probe
The functions to be implemented in the probe function include the application of the device number, the definition of the character device kernel object Cdev, the initialization, the binding file_operations, the registration of the character device kernel object Cdev, and the dynamic creation of the device file under/dev.
3:platform model-driven write-character devices still implement the core structure of the file_operations, the first step of the Cdev to use
The interface functions to be used for open, write, read, etc. in the file_operations structure are implemented.
4: Implement drive instance member function: Remove Mini2440_led_remove
The function to be implemented in the Remove function is in contrast to the probe, and the related device and resource are logged off.
The probe and remove functions actually correspond to the module loading and unloading functions that were implemented when the driver was written with a non-platform mechanism, whereas in the platform mechanism, the module's load and unload function called the device or driver's registration function "
5: Implement the driver load and unload function:
In the load function, call the driver's registration function, Platform_driver_register (...) ;
In the Unload function, call the driver unload function, Platform_driver_unregister (...) ;
Implement detailed procedures for platform devices
There are two ways to implement platform devices:
1: The most stupid kind: directly in the kernel source code to add the relevant resource code, \ARCH\ARM\MACH-S3C2440\MACH-MINI2440.C
2: Write the device module and use the INSMOD command to load the device module onto the platform bus.
Of course, the second kind, but the principle of the two methods and to write the code is the same, but the first do not have to register themselves, because the system initialization will be mach-mini2440.c in the struct Platform_device *mini2440_devices[] All devices contained in the __INITDATA device array are registered on the bus. And the second kind of manual registration, a word live.
A: Define device and resource instances
static struct resource mini2440_led_resource[] = {
[0] = {
. Start = 0x56000010,
. end = 0x56000010 + 12,
. Flags = Ioresource_mem
},
};
static struct Platform_device mini2440_platform_device_led = {
. Name = "Mini2440_led_platform_device_driver",
. id =-1,
. num_resources = Array_size (Mini2440_led_resource),
. resource = Mini2440_led_resource,
. Dev = {
. Release = Mini2440_led_platform_device_release,
},
};
B: Implement the member function of the device release
static void Mini2440_led_platform_device_release (struct device * dev)
{
return;
}
C: Implement the Load and unload functions of the device:
In the load function, call the device's registration function, Platform_device_register (...) ;
In the Unload function, call the device's unload function, Platform_device_unregister (...) ;
Simultaneous registration of multiple devices: platform_add_devices (struct platform_devices **devs,int num);
struct platform_devices **devs device array, num contains the number of devices
Whether the drive and the device are successfully registered, we can check the/sys/bus/platform/devices (drivers)
2 device.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
static void Mini2440_led_platform_device_release (struct device * dev)
{
return;
}
static struct resource mini2440_led_resource[] = {
[0] = {
. Start = 0x56000010,
. end = 0x56000010 + 12,
. Flags = Ioresource_mem
},
};
static struct Platform_device mini2440_platform_device_led = {
. Name = "Mini2440_led_platform_device_driver",
. id =-1,
. num_resources = Array_size (Mini2440_led_resource),
. resource = Mini2440_led_resource,
. Dev = {
. Release = Mini2440_led_platform_device_release,
},
};
static int __init mini2440_led_platform_device_init (void)
{
PRINTK ("Mini2440_led_platform_device add ok!\n");
Return Platform_device_register (&mini2440_platform_device_led);
}
static void __exit mini2440_led_platform_device_exit (void)
{
PRINTK ("Mini2440_led_platform_device remove ok!\n");
Platform_device_unregister (&mini2440_platform_device_led);
}
Module_author ("litingting");
Module_license ("GPL");
Module_init (Mini2440_led_platform_device_init);
Module_exit (Mini2440_led_platform_device_exit);
Makefile
Ifneq ($ (kernelrelease),)
Obj-m: =DEVICES.O
Else
Kerneldir: =/home/litingting/gec2440/linux-2.6.30.4
All
Make-c $ (Kerneldir) m=$ (PWD) modules Arch=arm cross_compile=arm-linux-
Clean
Rm-f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~
endif
3 driver.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#define GLOBAL_LED_MAJOR 250
static unsigned int global_led_major = global_led_major;
static struct Cdev *led_cdev = NULL;
static struct Class *led_class = NULL;
Static volatile unsigned long *gpfcon = NULL;
Static volatile unsigned long *gpfdat = NULL;
Static volatile unsigned long *gpfup = NULL;
static int Mini2440_led_open (struct inode * inode,struct file * file)
{
PRINTK ("mini2440_open[kernel_space]\n");
*gpfcon &=~ ((0x3<<0) | (0x3<<8) | (0X3<<10) | (0x3<<12) | (0x3<<14));
*gpfcon |= (0x1<<0) | (0x1<<8) | (0X1<<10) | (0x1<<12) | (0X1<<14);
return 0;
}
Static ssize_t mini2440_led_read (struct file * file,const Char __user * in,size_t size,loff_t * off)
{
PRINTK ("mini2440_read[kernel_space]\n");
return 0;
}
Static ssize_t mini2440_led_write (struct file * file,const Char __user * in,size_t size,loff_t * off)
{
int ret;
Char ker_buf;
PRINTK ("mini2440_write[kernel_space]\n");
ret = Copy_from_user (&ker_buf,in,size);
PRINTK ("Ker_buf =%d\n", ker_buf);
if (KER_BUF)
{
*gpfdat |= (1 << 5) | (1 << 6) | (1 << 8) | (1 << 7);
}
Else
{
*gpfdat |= (0x1<<4) | (0x1<<5) | (0x1<<6) | (0X1<<7);
*gpfdat &= ~ (0x1<<0);
}
return 0;
}
struct File_operations led_fops = {
. Owner = This_module,
. open = Mini2440_led_open,
. Read = Mini2440_led_read,
. write = Mini2440_led_write,
};
static int __devinit mini2440_led_probe (struct platform_device *pdev)
{
int ret;
int err;
dev_t Devno;
struct resource *pioresource_mem;
Devno = MKDEV (global_led_major,0);
PRINTK (Kern_alert "mini2440_led_probe!\n");
if (DEVNO)
{
ret = register_chrdev_region (devno,1, "mini2440_led_platfor_driver");
}
Else
{
ret = alloc_chrdev_region (&devno,0,1, "mini2440_led_platfor_driver");
Global_led_major = Major (Devno);
}
if (Ret < 0)
{
return ret;
}
Led_cdev = Cdev_alloc ();
Cdev_init (Led_cdev,&led_fops);
Led_cdev->owner = This_module;
Err = Cdev_add (led_cdev,devno,1);
Led_class = Class_create (This_module, "mini2440_led_platfor_driver");
Device_create (Led_class,null,mkdev (global_led_major,0), NULL, "platfor_driver_for_mini2440_led");
Pioresource_mem = Platform_get_resource (pdev,ioresource_mem,0); Access to resources
Gpfcon = Ioremap (Pioresource_mem->start,pioresource_mem->end-pioresource_mem->start);//Address
Gpfdat = Gpfcon + 1;
Gpfup = Gpfcon + 2;
*gpfdat |= (0x1<<5) | (0X1<<7);
if (ERR)
{
PRINTK (kern_notice "Error%d adding Led_cdev", err);
return-1;
}
Else
{
PRINTK (kern_notice "platform_driver_for_mini2440_led init ok!\n");
return 0;
}
}
static int __devexit mini2440_led_remove (struct platform_device *pdev)
{
PRINTK ("mini2440_led_remove!\n");
Cdev_del (Led_cdev);
Iounmap (Gpfcon);
Unregister_chrdev_region (MKDEV (global_led_major,0), 1);
Device_destroy (Led_class, MKDEV (global_led_major,0));
Class_destroy (Led_class);
return 0;
}
static struct Platform_driver Mini2440_led_platform_driver = {
. Probe = Mini2440_led_probe,
. remove = __devexit_p (Mini2440_led_remove),
. Driver = {
. Name = "Mini2440_led_platform_device_driver",
. Owner = This_module,
}
};
static int __init mini2440_led_platform_driver_init (void)
{
PRINTK ("platform_driver_for_mini2440_led init\n");
Return Platform_driver_register (&mini2440_led_platform_driver);
}
static void __exit mini2440_led_platform_driver_exit (void)
{
PRINTK ("platform_driver_for_mini2440_led exit\n");
Platform_driver_unregister (&mini2440_led_platform_driver);
}
Module_author ("litingting");
Module_license ("GPL");
Module_param (Global_led_major,int,s_irugo);
Module_init (Mini2440_led_platform_driver_init);
Module_exit (Mini2440_led_platform_driver_exit);
Makefile
Ifneq ($ (kernelrelease),)
Obj-m: =DRIVER.O
Else
Kerneldir: =/home/litingting/gec2440/linux-2.6.30.4
All
Make-c $ (Kerneldir) m=$ (PWD) modules Arch=arm cross_compile=arm-linux-
Clean
Rm-f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~
endif
4 Test Procedure TEST.c
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/input.h>
#include <unistd.h>
int main (int argc, char *argv[])
{
int FD, no;
FD = open ("/dev/platfor_driver_for_mini2440_led", O_RDWR);
if (fd<0)
{
printf ("Can not open file.\n");
return-1;
}
Write (FD, "1", 1); Write (fd,buff,sizeof (buff));//Writing data
Close (FD);
return 0;
}
Makefile
All:test
Arm-linux-gcc-static-o Test test.c
Clean
RM Test
linux2.6.30.4 s3c2440 platform Bus led driver