============================ Guide =============================
The first section is the most basic driver;
The second section is the use of the/dev Application layer interface;
The third section is the use of/sys application layer interface;
The fourth section is the operation of the hardware;
The fifth section is a simple description of the old version of Platform_driver;
The sixth section is the simple description of the equipment tree and the new platform;
=========================== Simple Driver ===========================
1. Basic framework
this is a . Ko driver is the most basic and most common framework, the most important feature of this framework is that it does not rely on any external code restrictions, can be insmod operation at any time, often used for high-real-time code, or some kind of interface support embedded into the kernel. This framework is what all people have to master.
static __init int moduleinit (void)//function called on Mount
{
......
return 0; Returns 0 if initialization is complete, otherwise the correct ERR code should be returned based on the reason
}
static __exit void Moduleexit (void)//function called at unload
{
......
}
Module_init (Moduleinit); A function that specifies that Moduleinit is called when loading
Module_exit (Moduleexit); Specifies the function that moduleexit is called when unloading
Module_author ("Your Name"); The name of the author and additional instructions for maintaining
Module_license ("GPL"); Declare open source or not open source, optional GPL and proprietary
2. Common functions
1) PRINTK:
PRINTK and printf are very similar, it is used to output the driver debugging information, but PRINTK more powerful, can specify the print level, display the system time when printing, in addition, PRINTK only for output debugging information, its output will not enter the stdio buffer stream.
Example:
PRINTK (kern_info "Kernel message%d", 1);
Output:
[s.mmmμμμ] Kernel message 1
because the debugging information is usually output through the serial port, so the PRINTK occupies a lot of time;
2) Kzalloc
Kzalloc is used to request memory space, most drivers need to open up a memory space as a temporary data storage area, the function is very high frequency, and it is basically the same as malloc;
Example:
struct Device_type *objet = NULL;
Objet = Kzalloc (sizeof (*objet), Gfp_kernel);
3) Kfree
In contrast to Kzalloc, the memory space used to release the Kzalloc application;
Example:
Kfree (object);
=========================/dev Application layer interface ==========================
This is extracted because the/dev Application layer interface is a stand-alone framework and does not depend on a particular driver framework, and any driver can use the/dev framework.
1. Prerequisite knowledge--linux File System basic operation
Many people know that Linux below is the device as a file, but the specific information is how the file from the application layer issued to the device, many people have not to understand, and even a lot of people even the standard read and write operations are not clear, so it is necessary here very basic to say a bit.
1) Open and close
The open and close functions are a way for the program to get access to the file, and the result of open is to return a file handle, and the close function is used to release the file;
int open (__const char *__file, int __oflag, ...);
int close (int __fd);
Cases:
int fd = open ("abc", O_RDWR | O_nonblock);
Close (FD);
2) Read and write
Read and write are used to pass in or out data to a file, and the return value is the size of the data actually written;
ssize_t Read (int __fd, void *__buf, size_t __nbytes);
ssize_t write (int __fd, __const void *__buf, size_t __n);
Cases:
Length = write (fd, buffer, strlen (buffer));
Length = read (fd, buffer, sizeof (buffer));
3) IOCTL
The IOCTL is used for slightly more complex control, it is originally used for TCP/IP, but many drivers also use this interface to achieve a variety of functions to share an interface, the return value of 0 means success, less than 0;
int ioctl (int fd, int cmd, void *arg);
4) Fread and fwrite
using the handle of a file opened with fopen, you need to use fread and fwrite operations, but it is important to note that the F family function uses flow control, the data is cached in the buffer, to a certain number or triggered by the Fflush function before sending. For the equipment with strict timing, it is not advisable to use F-family function;
2.struct File_operations Structural Body
The file_operations structure exists in the Fs.h file, which specifies which interfaces the driver will show to the application layer program in the/dev directory, which is strictly matched to read, write, and so on, so when designing the driver, if you want/ Using the standard file interface to transfer data to the driver in the dev directory, it is necessary to implement the corresponding attribute in the file_operations structure in the driver;
The statement is as follows (excerpt):
struct File_operations {
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
Int (*open) (struct inode *, struct file *);
Long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
......
};
Example: Implementing data that is written back to a file write
ssize_t fops_write (struct file *file_p, const char __user *buf, size_t size, loff_t *loff)
{
Char Data[page_size] = {'} '};
Copy_from_user (&data, buf, size);
Data[size] = ' + ';
PRINTK (kern_info "%s", data);
return size;
};
struct File_operations fops = {
. write = Fops_write;
};
3. Create a device interface under/dev
1) The process is more complex, first give the example code:
int dev_id = 0;
struct class *test_class = NULL;
dev_t Test_dev;
dev_id = Register_chrdev (0, "Test_dev", &fops);
Test_dev = MKDEV (dev_id, 0);
Test_class = Class_create (This_module, "Test_class");
Device_create (Test_class, NULL, Test_dev, NULL, "Test_dev");
2) So what does this code do?
This code refers to 4 objects, in simple terms: A, fops structure, B, Dev object, C,/sys/class under the object, D,/dev under the object;
This code completes the following actions: Creating a /sys/class/test_class folder, creating a/dev/test_dev device file;
The reason for this is that because Device_create creates an object under/dev, it needs to bind both the device driver and an object in the/sys/class, and eventually the file representing the device appears under/dev.
The code that creates the/dev application-tier interface is typically placed in the driver's initialization function.
=========================/sys Application Layer Interface ==========================
1./sys folder
This folder provides all the configuration options for the entire Linux, and for driver development, we need to focus on the/sys/class folder, because the objects under that folder are one of the prerequisites for creating device files under/dev. And it is typically used to store the configuration options for the driver.
The following is an example of this folder, with Gpio as an example:
/sys/class
┗gpio # class
┣export # class attribute
┣unexport # class attribute
┣ ...
┗PIOA # Device
┣active_low # Device attribute
┣direction # Device attribute
┣edge # Device attribute
┣value # Device attribute
┗ ...
2. Differences from/dev
1) Interface differences:
/sys only supports the customization of read and write operations;
2) Read and write differences:
/sys, once the open operation is performed, the show function is executed and the return value is generated, no matter how many times it is repeated, it is the same return value and must be refreshed after the close operation; write is not rigorously tested and recommended.
3. Code samples
this piece of code hierarchy is very strong, but involves a lot of variables, the code is also relatively large, so can only be used as an example of the form to illustrate, I hope you can read the analysis carefully, now to take the previous 335x EQEP drive the corresponding parts:
1) First is the implementation of class
Specifies which attribute are available for the class object, but the driver does not need it, so it is empty
static struct Class_attribute eqep_class_attrs_gs[] = {
__attr_null,
};
Constructing a Class object
static struct Class Eqep_class_gs = {
. Name = "Eqep",
. Owner = This_module,
. Class_attrs = Eqep_class_attrs_gs,
};
/* Register the class object with the system when the device is initialized */
static int __init eqep_classinit (void) {
Return Class_register (&EQEP_CLASS_GS);
}
then when this program is finished, the system will produce /SYS/CLASS/EQEP this class;
2) Device implementation
Constructing a single attribute
corresponding to the Read function
Static ssize_t eqep_showposition (struct device *dev, struct device_attribute *attr, char *buf)
{
...
Return sprintf (buf, "...");
}
Corresponding Write function
Static ssize_t eqep_storeposition (struct device *dev, struct device_attribute *attr, const char *BUF, size_t len)
{
......
return Len;
}
Constructs attribute to give read and write permissions and read and write operations
Static device_attr (position, S_irugo | S_IWUSR, Eqep_showposition, eqep_storeposition);
......
Constructs a attribute list, summarizing all properties
static const struct attribute *eqep_attrs_gs[] = {
&dev_attr_all_regs.attr,
&dev_attr_position.attr,
&dev_attr_mode.attr,
&dev_attr_run.attr,
&dev_attr_timer_period.attr,
Null
};
static const struct Attribute_group Eqep_device_attr_group_gs = {
. attrs = (struct attribute * *) Eqep_attrs_gs,
};
3) Registration of the device object, a series of properties will appear after completion
int eqep_devicecreate (struct eqep_chip_t *eqep) {
......
Create a Device object
sprintf (Device_name, "%s.%d", Eqep->pdev->name, Eqep->pdev->id); Eqep_device = device_create (&eqep_class_gs, NULL, MKDEV (0, 0), NULL, device_name);
......
Registering attributes with the device object
ret = Sysfs_create_group (&eqep_device->kobj, &eqep_device_attr_group_gs);
......
return 0;
}
============================= operation of the hardware ==========================
only the operation of the on-chip device is spoken here, as the operation of the external device can be attributed to the The CPU's on-chip device for operation. This section only speaks of API interfaces for device operations on the chip.
1. Basic Concepts-memory mapping
memory mapping, which is mainly referred to as the Ioremap function, is to map the physical address to the virtual address of the kernel, providing access to the physical device.
2. Request hardware resources, i.e. memory mapping
struct resource *r;
void __iomem *phy_addr = 0x********;
U32 size = * *;
void __iomem *virt_addr;
R = request_mem_region (phy_addr, size, "dev name");
VIRT_ADDR = Ioremap (phy_addr, size);
3. Reading and writing
Read and write virtual memory addresses need to use special API functions:
READB, READW, Readl, Writeb, Writew, Writel;
==================== the old version of Platform_driver simple description ===================
To be perfected, now this way has been gradually eliminated
==================== Equipment Tree and the new version of platform simple description ===================
1) Platform Device driver framework
The scope of this framework applies: hardware-dependent drivers.
as long as the compatible match, then the node information will be passed to the driver, if the matching device tree node cannot be found, then the driver will not be started, the benefit of this framework is that the same system image with multiple drivers can be compatible with a different device tree, There is no case that the driver could not find the hardware.
2) Example:
Device Tree Creation node:
/ {
New_dev {
compatible = "Test,new_dev";
Status = "Okay";
};
};
Write a simple drive:
static int test_probe (struct platform_device *pdev)
{
return 0;
}
static int __devexit test_remove (struct platform_device *pdev)
{
return 0;
}
static const struct OF_DEVICE_ID test_of_match[] = {
{
. Compatible = "Test,new_dev",
},
{ }
};
static struct Platform_driver Test_driver = {
. Driver = {
. Name = "New_dev",
. Owner = This_module,
. of_match_table = Of_match_ptr (Test_of_match),
},
. Probe = Test_probe,
. remove = Test_remove,
};
Linux Driver Framework Basics