1 basic
In the device driver often see and platform related fields, distributed in many corners of the driver, this is also the 2.6 kernel of a more important mechanism, the principle of its understanding, for later analysis of the driver is very helpful, the following brief introduction: In the linux2.6 device model, care about the bus, the device, drive the three entities, the bus will be device and drive binding, in the system each register a device, will look for a matching driver. Instead, when the system registers a driver, the matching device is found, and the match is done by the bus.
A realistic Linux device and driver usually need to be hooked up on a bus, which is naturally not a problem for devices that are attached to PCI, USB, I2C, SPI, and so on, but in embedded systems, the independent peripheral controller integrated in the SOC system Peripherals such as memory space are not attached to such a bus. Based on this background, Linux invented a virtual bus, called the platform bus
The integrated independent peripheral units (I2C,LCD,SPI,RTC, etc.) in the SOC system are treated as platform devices, and they are character-type 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 set of mechanisms, the device is represented by Platform_device, and the driver is registered with Platform_driver.
Platform Model driver programming, the need to implement 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 platform driver based on a character device, the core of the character device is the core structure of the platform-Cdev, file_operations (the operator interface he contains), Dev _t (device number), device file (/dev), etc., because of the character driven by the platform mechanism, its essence is character driven.
In general, a platform bus is already initialized and mounted in the 2.6 kernel in the Sysfs file system. So when we write the platform model driver, we need to do two things: 1: Realize platform Drive 2: Implement platform device, however, in the process of implementing these two tasks, many other small tasks 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 drive.
Platform bus corresponding to the kernel structure: struct bus_type--> It contains the most critical function: Match ()
Platform device corresponding to the 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 platform-driven work process: device (or driver) registration, will cause the bus to call its own match function to find whether the current platform bus is mounted with the device (or driver) name matching the driver (or device), if the existence of the two sides bound If the device is registered first, the driver has not yet registered, then when the device is registered on the bus, it will not match the driver with the same name, and then when the driver registers on the bus, because the device is registered, then the bus will immediately match the device and the driver with the same name as the binding, and then call the probe function in the driver. If the driver is first registered, the same device driver will match the failure, the matching failure will cause its probe function is not called, but to wait until the device registration is successful and matching the binding will not be invoked.
Next, we'll explain the detailed process of implementing platform drive and device as follows.
Detailed process to implement platform drive
1: Define Drive 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 driver instance member function: Probe Mini2440_led_probe
The functions to be implemented in the probe function include: device number application, character device kernel object Cdev definition, initialization, binding file_operations, registering character device kernel object Cdev, dynamically creating device files under/dev.
3:platform model drives the character device to implement the core structure file_operations, the first step of the Cdev to use
The interface functions to be used in the file_operations structure are realized by open, write, read and so on.
4: Implement driver instance member function: Remove Mini2440_led_remove
The function to be implemented in the Remove function is to cancel the related equipment and resources in contrast with the probe.
"Probe and remove functions actually correspond to the module loading and unloading functions that are implemented when the platform mechanism is written to drive, and in the platform mechanism, the load and unload functions of the module call the device or driver registration function."
5: Implementation of the driver load and unload functions:
In the load function, call the driver's registration function, Platform_driver_register (...) ;
In the Unload function, call the driven unload function, Platform_driver_unregister (...) ;
Implement the detailed process of the platform device
There are two ways to implement a platform device:
1: The most stupid one: directly in the kernel source code to add the relevant resource code, \ARCH\ARM\MACH-S3C2440\MACH-MINI2440.C
2: Write the device module, use the INSMOD command to load the device module to the platform bus.
Of course, the second one, but the principle of these two methods and to write the code are the same, but the first do not 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: Defining 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 release of the device
static void Mini2440_led_platform_device_release (struct device * dev)
{
return;
}
C: Implement the load and unload function of the device:
In the load function, call the device's registration function, Platform_device_register (...) ;
In the Unload function, call the Unload function of the device, 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
Drive and equipment successfully registered, we can all in/sys/bus/platform/devices (drivers)/below view
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
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
Clean
Rm-f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~
endif
4 Test Program 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