(The platform used in this article is tiny210sdkv2)
Linux drivers can be divided into three types:Miscdevice, platform_device, and platform_driver.
These three struct relationships:
(Base class)
Kobject --------------------
/\\
/\\
Device cdev driver
// \ (Device Driver Operation Method )\
/\\
Miscdevice platform_device platform_driver
(Device Driver Operation Method) (device resources) (Device Driver)
At this time, we will not discuss the relationship between these two items and the relationship between them. For Beginners, getting started is the most important thing!
First, let's look at the mixed Miscellaneous:
In Linux drivers, devices that cannot be categorized are defined as hybrid devices (expressed in the miscdevice structure ). Miscdevice shares the primary device number misc_major (10), but the secondary device number is different. All miscdevice devices form a linked list. During device access, the kernel searches for the corresponding miscdevice device based on the device number, and then calls the file operation interface registered in its file_operations structure to perform operations. In the kernel, use struct miscdevice to represent the miscdevice device, and then call the file operation interface registered in its file_operations structure to perform operations. The API of miscdevice is implemented in drivers/Char/Misc. C.
Second, let's look at the program architecture of the driver for mixed Miscellaneous devices:
Create a new first_led.c. Reference all header files that may be used first!
# Include <Linux/kernel. h> # include <Linux/module. h> // the header file that must be added to the driver module # include <Linux/miscdevice. h> # include <Linux/Fs. h> # include <Linux/types. h> # include <Linux/moduleparam. h> # include <Linux/slab. h> # include <Linux/IOCTL. h> # include <Linux/cdev. h> # include <Linux/delay. h>. // corresponds to the header file of the corresponding machine platform # include <Mach/gpio. h> # include <Mach/regs-gpio.h> # include <plat/gpio-cfg.h> // define a name for your device driver # define device_name "first_led"
But what does the name look like? Now let's start to define a "look "!
If a character device driver needs to drive multiple devices, it should not use
Misc device.
Generally, a character device has to perform the following steps during initialization:
Assign the Primary and Secondary device numbers through alloc_chrdev_region.
Use cdev_init () and cdev_add () to register yourself with a single character device.
However, a misc driver can only call misc_register ()
To complete all the steps. (Miscdevice is a special chrdev character device driver)
All miscdevice devices form a linked list. when accessing devices, the kernel searches
The corresponding miscdevice device, and then call the file operation method registered in its file_operations to perform the operation.
In the Linux kernel, struct miscdevice is used to represent miscdevice. The struct is defined:
Struct miscdevice
{
Int minor;
Const char * Name;
Const struct file_operations * fops;
Struct list_head list;
Struct device * parent;
Struct device * this_device;
Const char * nodename;
Mode_t mode;
};
Minor is the sub-device number of this hybrid device. If it is automatically configured by the system, it can be set
Misc_dynanic_minor, whose name is the device name
To make it easy to understand, let's make a rough picture first. Only use minor, name, and FOPS;
Define a myfirst_led_dev device:
static struct miscdevice myfirst_led_dev = {.minor= MISC_DYNAMIC_MINOR,.name= DEVICE_NAME,.fops= &myfirst_led_dev_fops,};
Minor names have been defined. Then implement the myfirst_led_dev_fops method.
The structure of file_operations in the kernel is as follows:
Struct file_operations {
Struct module * owner;
Loff_t (* llseek) (struct file *, loff_t, INT );
Ssize_t (* read) (struct file *, char _ User *, size_t, loff_t *);
Ssize_t (* write) (struct file *, const char _ User *, size_t, loff_t *);
Ssize_t (* aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t );
Ssize_t (* aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t );
INT (* readdir) (struct file *, void *, filldir_t );
Unsigned int (* poll) (struct file *, struct poll_table_struct *);
Long (* unlocked_ioctl) (struct file *, unsigned int, unsigned long );
Long (* compat_ioctl) (struct file *, unsigned int, unsigned long );
INT (* MMAP) (struct file *, struct vm_area_struct *);
INT (* open) (struct inode *, struct file *);
INT (* flush) (struct file *, fl_owner_t ID );
INT (* release) (struct inode *, struct file *);
INT (* fsync) (struct file *, int datasync );
INT (* aio_fsync) (struct kiocb *, int datasync );
INT (* fasync) (INT, struct file *, INT );
INT (* Lock) (struct file *, Int, struct file_lock *);
Ssize_t (* sendpage) (struct file *, struct page *, Int, size_t, loff_t *, INT );
Unsigned long (* get_unmapped_area) (struct file *, unsigned long, unsigned long );
INT (* check_flags) (INT );
INT (* flock) (struct file *, Int, struct file_lock *);
Ssize_t (* splice_write) (struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned INT );
Ssize_t (* splice_read) (struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned INT );
INT (* setlease) (struct file *, long, struct file_lock **);
Long (* fallocate) (struct file * file, int mode, loff_t offset,
Loff_t Len );
};
For led operations, you only need to simply implement Io operations, so only
Long (* unlocked_ioctl) (struct file *, unsigned int, unsigned long );
(This function appears in the operation method of the device after linux2.6.5 .)
Function parameters include file nodes, commands, and parameters.
static struct file_operations myfirst_led_dev_fops = {.owner= THIS_MODULE,.unlocked_ioctl= myfirst_led_ioctl,};
Here, we will consider how the physical layer of the LED is implemented. Through the pins of the Development Board, we can know that the four LEDs are connected to the 0 ~ of gpj2 respectively ~ Pin 3. Therefore, we define an array to reference these pins (of course, I/O physical addresses cannot be operated like hosts, and I/O memory operations need to be achieved through memory ing of the kernel! The kernel orchestrates the arm I/O pin address according to a linear address)
Static int led_gpios [] = {s5pv210_gpj2 (0), s5pv210_gpj2 (1), s5pv210_gpj2 (2), s5pv210_gpj2 (3), };# define led_numarray_size (led_gpios) // determine the number of led_gpio instances
S5pv210_gpj2 (*) is defined as follows:
# Define s5pv210_gpj2 (_ nR) (s5pv210_gpio_j2_start + (_ nR ))
|
|
|
Enum s5p_gpio_number {
S5pv210_gpio_a0_start = 0,
...................................
S5pv210_gpio_j2_start = s5pv210_gpio_next (s5pv210_gpio_j1 ),
.....................................
}
|
|
|
# Define s5pv210_gpio_next (_ gpio )\
(_ Gpio ##_ start) + (_ gpio ##_ nr) + config_cloud_gpio_space + 1)
(Note: # It refers to the paste operation. For specific usage, please contact du Niang or Gu Ge)
Interface operation for user space:
Static long myfirst_led_ioctl (struct file * filp, unsigned int cmd, unsigned long Arg) {Switch (CMD) {Case 0: Case 1: If (Arg> led_num) {return-einval; // check whether user parameters are correct} gpio_set_value (led_gpios [Arg],! CMD); // select the LED and set the value // printk (device_name ": % d \ n", ARG, CMD); break; default: return-einval;} return 0 ;}
For gpio_set_value (unsigned int gpio, int value), the kernel has the following definitions:
Static inline void gpio_set_value (unsigned int gpio, int value)
{
_ Gpio_set_value (gpio, value );
}
|
|
|
Void _ gpio_set_value (unsigned gpio, int value)
{
Struct gpio_chip * chip;
Chip = gpio_to_chip (gpio );
Warn_on (chip-> can_sleep );
Trace_gpio_value (gpio, 0, value );
Chip-> set (chip, gpio-chip-> base, value );
} // Here we will not analyze it any more. It is nothing more than determining which chip it is.
The program writes it here. For the user space, there is a complete operation method interface, but for the kernel module, there is still a lack of access and exit of the driver module. Next, write the initialization (entry) and exit of the driver module.
Static int _ init myfirst_led_dev_init (void ){;}
Static void _ exit myfirst_led_dev_exit (void ){;}
Function. Dual-underline indicates that the module runs and exits automatically when the kernel is started or shut down.
What should I write about the initialization function of the driver module? We consider the following:
For the user space interface, our implementation function only provides the IO value settings, but the arm Io pin still needs to be configured, pulled up and down ..... all hardware resources are subject to the kernel. the driver must apply for hardware resources from the kernel to operate the hardware. In addition, you also need to register the device so that the kernel knows what your device is and what it uses. These operations are implemented in init!
Static int _ init myfirst_led_dev_init (void) {int ret; int I; for (I = 0; I <led_num; I ++) {ret = gpio_request (led_gpios [I], "Led"); // request Io pin if (RET) {printk ("% s: Request gpio % d for LED failed, ret = % d \ n", device_name, led_gpios [I], RET); return ret;} initi_gpio_cfgpin (led_gpios [I], initi_gpio_output); gpio_set_value (led_gpios [I], 1 );} ret = misc_register (& myfirst_led_dev); printk (device_name "\ tinitialized \ n"); return ret ;}
Pio_request (unsigned gpio, const char * label)
Which pin does gpio apply for? The label is its name.
Int 89c_gpio_cfgpin (unsigned int pin, unsigned int config );
Judge the chip and set the direction of the pin.
Ret = misc_register (& myfirst_led_dev );.
In this function, the kernel automatically creates a device node for your device.
Register a device
At this point, the device initialization and registration have been completed!
When the user no longer needs this driver resource, we must actively release the resources that occupy the kernel in the driver module!
Therefore, when the driver module exits, complete these tasks!
static void __exit myfirst_led_dev_exit(void) {int i;for (i = 0; i < LED_NUM; i++) {gpio_free(led_gpios[i]);}misc_deregister(&myfirst_led_dev);}
Gpio_free (led_gpios [I]);
Release Io Resources
Misc_deregister (& myfirst_led_dev );
Cancel a device
The module initialization and exit function Declaration are also required.
Module_init (myfirst_led_dev_init );
Module_exit (myfirst_led_dev_init );
Finally, in order to maintain the style of the kernel driver module, we need to add the corresponding license and author
Module_license ("GPL ");
Module_author ("jjvip136@163.com ");
Okay, the program has already been compiled (the yellow Code). Let's sort it out and compile it to try the effect (the effect will be completed later ).