[Linux driver] common character device driver framework

Posted by abitshort on Sun, 23 Jan 2022 15:03:42 +0100

1, Ordinary character device driver design process
------------------------Define a normal character device---------------------------
1) Define a normal character device
2) Defines the file operation set corresponding to the normal character device
3) Apply for a device number for a normal character device
4) Initialize normal character device
5) Adding a normal character device to the kernel
--------------------------Given a device file, provide access to the application---------
6) Create a class
7) Create a device, and the device should belong to class ------. Specify a device file name, such as led_dev/gec6818led
------------------------Apply for physical memory and get the virtual address corresponding to the physical address---------
8) Request physical memory area
9) Complete io dynamic mapping - > get the relationship between physical address and virtual address
10) In the driver, the hardware is accessed through a virtual address
2, Analysis

1. Define a normal character device
A driver can be embodied by the kernel module, so its carrier is a kernel module in the driver design
Header file:
kernel\include\linux\cdev.h

1 struct cdev {
2 struct kobject kobj;
3 struct module *owner;
4 const struct file_operations *ops;
5 struct list_head list;
6 dev_t dev;
7 unsigned int count;
8 };

Each character device can be regarded as a struct cdev variable, such as:
struct cdev gec6818led_cdev;
Describe the key members of the structure:
1)struct kobject kobj; —> The kernel object manager uses object, which is implemented by the kernel itself
2)struct module *owner; ----> Kernel module object, indicating which kernel module the current driver belongs to. Fixed writing method: THIS_MODULE
3) const struct file_ operations *ops;-----> File operation set and character device have a corresponding file operation set interface
struct file_operations gec6818led_fops;
gec6818led_ cdev. ops = &gec6818led_ fops;----> Set a file operation for a character device
4)struct list_head list; —> Manage the linked list of character devices, which is implemented by the kernel itself
5)dev_ t dev; ----> Device number ----- > is essentially an unsigned integer number, which is completed by the programmer
6)unsigned int count; -----> The number of secondary device numbers of a character device, completed by the programmer

2. Defines the file operation set corresponding to the normal character device
kernel\include\linux\fs.h

1 struct file_operations {
2 struct module *owner;
3 loff_t (*llseek) (struct file *, loff_t, int);
4 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
5 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
6 ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
7 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
8 int (*readdir) (struct file *, void *, filldir_t);
9 unsigned int (*poll) (struct file *, struct poll_table_struct *);
10 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
11 long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
12 int (*mmap) (struct file *, struct vm_area_struct *);
13 int (*open) (struct inode *, struct file *);
14 int (*flush) (struct file *, fl_owner_t id);
15 int (*release) (struct inode *, struct file *);
16 int (*fsync) (struct file *, loff_t, loff_t, int datasync);
17 int (*aio_fsync) (struct kiocb *, int datasync);
18 int (*fasync) (int, struct file *, int);
19 int (*lock) (struct file *, int, struct file_lock *);
20 ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
21 unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned l
ong);
22 int (*check_flags)(int);
23 int (*flock) (struct file *, int, struct file_lock *);
24 ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
25 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
26 int (*setlease)(struct file *, long, struct file_lock **);
27 long (*fallocate)(struct file *file, int mode, loff_t offset,
28 loff_t len);
29 };

For example:
//The defined interface should correspond to the interface in the application

int gec6818led_open (struct inode *node, struct file *file)
{
return 0;
}
static struct file_operations gec6818led_fops = {
.owner = THIS_MODULE,
.open = gec6818led_open,
};

3. Apply for a device number for a normal character device
Device No.: dev_t dev
The equipment number is composed of primary equipment and secondary equipment
Equipment number = main equipment < < 20, and then the equipment number is obtained by bit operation with the secondary equipment number

3.1 produce an equipment number - > realized by macro function
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
1) The equipment number is obtained from the main equipment number and secondary equipment number
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
dev_t gec6818led_dev = MKDEV(major,minor);
2) The main equipment number and secondary equipment number are obtained from the equipment number
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) —>
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
static unsigned int major = MAJOR(gec6818led_dev);
static unsigned int minor = MINOR(gec6818led_dev);

3.2 register the device number into the kernel
1) Static registration

1 /**
2 * register_chrdev_region() ‐ register a range of device numbers
3 * @from: the first in the desired range of device numbers; must include
4 * the major number.
5 * @count: the number of consecutive device numbers required
6 * @name: the name of the device or driver.
7 *
8 * Return value is zero on success, a negative error code on failure.
9 */
10 int register_chrdev_region(dev_t from, unsigned count, const char *name)

Function: register a device number in the kernel, provide a device number by the programmer, and apply to the kernel. It may not be successful
Parameter Description:
Parameter 1: dev_t from - > the device number to be applied to the kernel as an incoming parameter
Parameter 2: unsigned count - > number of successively numbered secondary equipment numbers
Parameter 3: const char *name - > device name
Return value:
Success: 0
Failure: negative number
2) Dynamic registration

1 /**
2 * alloc_chrdev_region() ‐ register a range of char device numbers
3 * @dev: output parameter for first assigned number
4 * @baseminor: first of the requested range of minor numbers
5 * @count: the number of minor numbers required
6 * @name: the name of the associated device or driver
7 *
8 * Allocates a range of char device numbers. The major number will be
9 * chosen dynamically, and returned (along with the first minor number)
10 * in @dev. Returns zero or a negative error code.
11 */
12 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
13 const char *name)

Function: dynamically assign a device number by the kernel
Parameter Description:
Parameter 1: dev_ T * dev ----- > device number dynamically allocated by kernel, ---- output parameter
Parameter 2: unsigned baseminor ---- > first secondary equipment number
Parameter 3: unsigned count - > number of successively numbered secondary equipment numbers
Parameter 4: const char *name - > device name
Return value:
Success: 0
Failure: negative number
Note: for the resources of the kernel, when the applied device number is used up, it should be returned to the system in time, so the resource application and release should be used in pairs in the kernel
3) Release device number

1 /**
2 * unregister_chrdev_region() ‐ return a range of device numbers
3 * @from: the first in the range of numbers to unregister
4 * @count: the number of device numbers to unregister
5 *
6 * This function will unregister a range of @count device numbers,
7 * starting with @from. The caller should normally be the one who
8 * allocated those numbers in the first place...
9 */
10 void unregister_chrdev_region(dev_t from, unsigned count)

Function: return the device number to the kernel
Parameter Description:
Parameter 1: dev_t from ---- equipment number to be released
Parameter 2: unsigned count -- the number of secondary device numbers to be released
For programming, static or dynamic registration can be provided in a compatible way

/*
* Register a single major with a specified minor range.
*
* If major == 0 this functions will dynamically allocate a major and return
* its number.
*
* If major > 0 this function will attempt to reserve the passed range of
* minors and will return zero on success.
*
* Returns a -ve errno on failure.
*/

If the master device number is 0 ------, it means dynamic registration; otherwise, it means static registration

4. Initialize normal character device
Function prototype:

1 void cdev_init(struct cdev *, const struct file_operations *);

Parameter Description:
Parameter 1: struct CDEV * --- > character device to be initialized
Parameter 2: const struct file_ Operations * ------ > the set of file operations to initialize the character device

5. Adding character devices to the kernel
5.1 add character device to kernel
Function prototype:

1 /**
2 * cdev_add() ‐ add a char device to the system
3 * @p: the cdev structure for the device
4 * @dev: the first device number for which this device is responsible
5 * @count: the number of consecutive minor numbers corresponding to this
6 * device
7 *
8 * cdev_add() adds the device represented by @p to the system, making it
9 * live immediately. A negative error code is returned on failure.
10 */
11 int cdev_add(struct cdev *p, dev_t dev, unsigned count)

Parameter Description:
Parameter 1: struct CDEV * P ----- > character device to be added
Parameter 2: dev_ T dev ----- > character device number
Parameter 3: unsigned count ----- > number of secondary equipment numbers
Return value:
Success: 0
Failure: negative number
5.2 removing character devices from the kernel
Function prototype

1 /**
2 * cdev_del() ‐ remove a cdev from the system
3 * @p: the cdev structure to be removed
4 *
5 * cdev_del() removes @p from the system, possibly freeing the structure
6 * itself.
7 */
8 void cdev_del(struct cdev *p)

Parameter Description:
Parameter 1: struct CDEV * P ----- > character device to delete

6. Create a class
Header file: kernel \ include \ Linux \ device h
#include <linux/device.h>
6.1 create class
Function prototype:

1 /* This is a #define to keep the compiler from merging different
2 * instances of the __key variable */
3 #define class_create(owner, name) \
4 ({ \
5 static struct lock_class_key __key; \
6 __class_create(owner, name, &__key); \
7 })
8 owner, name The type of parameter should be based on__class_create,besides,__class_create Return value of function
9 Also class_create Function return value
10 class_create The function prototype is as follows:
11 /**
12 * class_create ‐ create a struct class structure
13 * @owner: pointer to the module that is to "own" this struct class
14 * @name: pointer to a string for the name of this class.
15 * @key: the lock_class_key for this class; used by mutex lock debugging
16 *
17 * This is used to create a struct class pointer that can then be used
18 * in calls to device_create().
19 *
20 * Returns &struct class pointer on success, or ERR_PTR() on error.
21 *
22 * Note, the pointer created here is to be destroyed when finished by
23 * making a call to class_destroy().
24 */
25 struct class *__class_create(struct module *owner, const char *name,
26 struct lock_class_key *key)

To sum up, the most available interface prototypes are as follows:
1 struct class * class_create(struct module *owner, const char *name)
Function: create a class
Parameter Description:
Parameter 1: struct module * owner ---- > describes which module the created class belongs to ---- > fixed writing method: THIS_MODULE
Parameter 2: const char * name ----- > class name ----- > can be viewed in the development board system
Return value:
Success: struct class*
Failed: NULL
6.2 destruction
Function prototype:

1 /**
2 * class_destroy ‐ destroys a struct class structure
3 * @cls: pointer to the struct class that is to be destroyed
4 *
5 * Note, the pointer to be destroyed must have been created with a call
6 * to class_create().
7 */
8 void class_destroy(struct class *cls)

Parameter Description:
Struct class * CLS ----- > class to destroy

7. Create device
----The purpose of this step is to get the name of a device file, which can be viewed in the / dev directory on the development board
7.1 create a device
Function prototype:

1 /**
2 * device_create ‐ creates a device and registers it with sysfs
3 * @class: pointer to the struct class that this device should be registered to
4 * @parent: pointer to the parent struct device of this new device, if any
5 * @devt: the dev_t for the char device to be added
6 * @drvdata: the data to be added to the device for callbacks
7 * @fmt: string for the device's name
8 *
9 * This function can be used by char device classes. A struct device
10 * will be created in sysfs, registered to the specified class.
11 *
12 * A "dev" file will be created, showing the dev_t for the device, if
13 * the dev_t is not 0,0.
14 * If a pointer to a parent struct device is passed in, the newly created
15 * struct device will be a child of that device in sysfs.
16 * The pointer to the struct device will be returned from the call.
17 * Any further sysfs files that might be required can be created using this
18 * pointer.
19 *
20 * Returns &struct device pointer on success, or ERR_PTR() on error.
21 *
22 * Note: the struct class passed to this function must have previously
23 * been created with a call to class_create().
24 */
25 struct device *device_create(struct class *class, struct device *parent,
26 dev_t devt, void *drvdata, const char *fmt, ...)

Function: create a device and register it in the file system of the system
Parameter Description:
Parameter 1: struct class * class ----- > specifies which class the device belongs to, class_ Return value of create function
Parameter 2: struct device *parent - > the parent device of the currently created device, which is generally set to NULL
Parameter 3: dev_t devt - > the device number of the device. When viewing the device file, view the device number
Parameter 4: void *drvdata - > when creating a device, the data called back by the system is NULL when not needed
Parameter 5: const char * FMT ----- > the name of the device file, which can be viewed in the / dev directory
Return value:
Success: struct device*
Failed: NULL
7.2 destroy device
Function prototype:

1 /**
2 * device_destroy ‐ removes a device that was created with device_create()
3 * @class: pointer to the struct class that this device was registered with
4 * @devt: the dev_t of the device that was previously registered
5 *
6 * This call unregisters and cleans up a device that was created with a
7 * call to device_create().
8 */
9 void device_destroy(struct class *class, dev_t devt)

Parameter Description:
Parameter 1: struct class * class ----- class of device
Parameter 2: dev_t devt -- device number of the device

8. Request physical memory area
Header file: #include < Linux / ioport h>
8.1 applying for physical memory area
The macro prototype is as follows:

1 #define request_mem_region(start,n,name)
2 __request_region(&iomem_resource, (start), (n), (name), 0)
3
4 start,n,name The types of the three parameters are derived from__request_region function
5 /**
6 * __request_region ‐ create a new busy resource region
7 * @parent: parent resource descriptor
8 * @start: resource start address
9 * @n: resource region size
10 * @name: reserving caller's ID string
11 * @flags: IO resource flags
12 */
13 struct resource * __request_region(struct resource *parent,
14 resource_size_t start, resource_size_t n,
15 const char *name, int flags)

To sum up, you can get request_ mem_ Prototype of region:

1 struct resource *request_mem_region(resource_size_t start, resource_size_t n,
2 const char *name)

Parameter Description:
Parameter 1: Resource_ size_ T ----- > the first address of the physical memory to be applied for
#ifdef CONFIG_PHYS_ADDR_T_64BIT
typedef u64 phys_addr_t;
#else
typedef u32 phys_addr_t;
#endif
typedef phys_addr_t resource_size_t;
Parameter 2: resource_size_t n - > the size of the physical memory area to be requested
Parameter 3: const char * name ----- > after the application is successful, the name of the physical memory area can be viewed in / proc/iomem of the development board
Return value:
Success: struct resource*
Failed: NULL
8.2 releasing physical memory area
Prototype of macro:

1 #define release_mem_region(start,n)
2 __release_region(&iomem_resource, (start), (n))
3 /**
4 * __release_region ‐ release a previously reserved resource region
5 * @parent: parent resource descriptor
6 * @start: resource start address
7 * @n: resource region size
8 *
9 * The described resource region must match a currently busy region.
10 */
11 void __release_region(struct resource *parent, resource_size_t start,
12 resource_size_t n)

To sum up, the prototype of macro function is as follows:

1 void release_mem_region(resource_size_t start,
2 resource_size_t n)

Parameter Description:
Parameter 1: Resource_ size_ T ----- > the first address of the physical memory to be applied for
Parameter 2: resource_size_t n - > the size of the physical memory area to be requested

9. IO mapping ---- the purpose is to establish the mapping relationship between physical address and virtual address
Header file: #include < Linux / Io h>
9.1 IO dynamic mapping

1 #define ioremap(cookie,size)
2 __arm_ioremap((cookie), (size), MT_DEVICE)
3 cookie,size The type of the parameter is derived from the function__arm_ioremap
4 void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size,
5 unsigned int mtype)

To sum up, the prototype of macro function is as follows:

1 void __iomem *ioremap(unsigned long phys_addr, size_t size)

Parameter Description:
Parameter 1: unsigned long phys_ Addr ---- > physical header address to be mapped (chip manual)
Parameter 2: size_ T Size ----- > the size of the address to be mapped
Return value:
Success: void__ IOMEM * -- the first address of the virtual address
Failed: NULL
9.2 demapping

1 #define iounmap __arm_iounmap
2 void __arm_iounmap(volatile void __iomem *addr)

3, Driver code

#include <linux/module. h> / / kernel module interface header file
#include <linux/printk. h> / / printk function header file
#include <linux/cdev.h> // struct cdev
#include <linux/fs.h>  //struct file_operations
#include <linux/device.h> //struct class
#include <linux/err.h> //#define	ENOMEM		12
#include <linux/io.h> //ioremap
#include <linux/ioport.h> //request_mem_region
#include <linux/uaccess.h> //copy_from_user

static dev_t gec6818led_dev;//Define an equipment number
static unsigned int major = 0; //The main equipment number indicates what kind of equipment the equipment is
static unsigned int minor = 0; //The secondary equipment number indicates which of these equipment

static struct class *gec6818led_class = NULL;
static struct device *gec6818led_device = NULL;
static struct resource *gec6818led_res = NULL;

//Virtual address:
static void __iomem *gec6818led_va_base = NULL;
//GPIOEOUT ----0xC001E000
static void __iomem *gec6818led_va_eout = NULL;
//GPIOEOUTENB --- 0xC001E004
static void __iomem *gec6818led_va_eoutenb = NULL;
//GPIOEALTFN0 ---- 0xC001E020
static void __iomem *gec6818led_va_ealtfn0 = NULL;

int gec6818led_open (struct inode *node, struct file *file)
{

	return 0;
}

/*
Data transfer between driver and application:
Question 1:
  Data protocol: system call (open write) - -- > struct file_ operations --- write
 Question 2:
    Data format: control lamp: a lamp defines an array: char kbuf [1] - kbuf [0] - > indicates the command of control lamp: 1 --- on lamp 0 --- off lamp 
	                 Multiple lamps define an array: char kbuf [2] - kbuf [0] - > indicates the command to control the lamp, and kbuf[1] - indicates the number of the lamp
  The data protocol + data format between driver and application shall be consistent
  
  For the kernel space used by the driver and the user space used by the application, the two spaces cannot be accessed directly to each other, but only through a specific interface:
  copy_to_user
  copy_from_user
*/
ssize_t gec6818led_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	 printk(KERN_WARNING "gec6818led_write\n");
	//[1] Define data format
	char kbuf[1];
	int ret;
	//int copy_from_user(void *to, const void __user *from, int n)
	ret = copy_from_user(kbuf,buf,size); //Get the data from the user space and store it in kbuf
	//[2] The control hardware is realized according to the command of data
	if(kbuf[0] == 1) //turn on the light
	{
		 *((volatile unsigned int*)gec6818led_va_eout) &= ~(1<<13);
	}else if(kbuf[0] == 0)//Turn off the lights
	{
		 *((volatile unsigned int*)gec6818led_va_eout) |= (1<<13);
	}
	return size;
}
//2. Define the file operation set object of a common character device
static struct file_operations gec6818led_fops = {
	.owner = THIS_MODULE,
	.open = gec6818led_open,
	.write = gec6818led_write,
};

//1. Define a common character device object
static struct cdev  gec6818led_cdev;





static int __init gec6818led_init(void)
{
	int ret;
    printk(KERN_WARNING "gec6818led_init\n");
	
	//3. when applying for a device number, the device number is equivalent to a person's ID number. In Linux, it is the only device, and the device number is also registered in the Linux kernel.
	//If it is not in the kernel, the device number is available. Otherwise, it is not available
	if(major == 0)//Dynamic registration
	{
		//int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
			//const char *name)
		ret = alloc_chrdev_region(&gec6818led_dev,minor,1,"gec6818led_number");
	}else //Static registration
	{
		//First, an equipment number is generated from the primary equipment number and secondary equipment number
		gec6818led_dev = MKDEV(major,minor);
		//Then register the device number in the kernel, int register_chrdev_region(dev_t from, unsigned count, const char *name)
		ret = register_chrdev_region(gec6818led_dev,1,"gec6818led_number");
	}
	
	  if(ret != 0)
	  {
		  printk(KERN_WARNING "register_device_number error\n");
		  goto register_device_number_error;
	  }
	//4. Initialize the ordinary character device void cdev_init(struct cdev *, const struct file_operations *);
	cdev_init(&gec6818led_cdev,&gec6818led_fops); //In the initialization function, the ordinary character device is associated with the file operation set
	
	//5. Add character device to kernel int cdev_add(struct cdev *p, dev_t dev, unsigned count)
	ret = cdev_add(&gec6818led_cdev,gec6818led_dev,1);
	 if(ret != 0)
	  {
		  printk(KERN_WARNING "cdev_add error\n");
		  goto cdev_add_error;
	  }
	  //6. Create a class struct class * class_create(struct module *owner, const char *name)
	  gec6818led_class = class_create(THIS_MODULE,"gec6818led_class");
	  if(gec6818led_class == NULL)
	  {
		   printk(KERN_WARNING "class_create error\n");
		   //If the return value of the function is not an integer, but the return value in the total function is an integer, so set a return value for ret again
		   ret =  -ENOMEM; // #define 	 ENOMEM 		 twelve 	/*  Out of memory * / the header file of this macro constant is referenced
		   goto class_create_error;
	  }
	  //7. Create device struct device *device_create(struct class *class, struct device *parent,
			   //  dev_t devt, void *drvdata, const char *fmt, ...)
	  gec6818led_device = device_create(gec6818led_class,NULL,gec6818led_dev,NULL,"gz1or2_led_drv");
	  if(gec6818led_device == NULL)
	  {
		   printk(KERN_WARNING "device_create error\n");
		   ret = -ENOMEM;
		   goto device_create_error;
	  }
	  
	  //8. Apply for physical memory area
	  //struct resource *request_mem_region(resource_size_t start, resource_size_t n,
		//		   const char *name)
		//The transfer of parameters of this function shall be determined according to the address of specific hardware, with D7 --- gpioe13 0xc001e000
		//To apply for the size of the physical memory area, it is best to group with the GPIO port: size of group A: 0XC001A000 B: 0xc001b000 -- > B-A -- 0x1000 -- 4KB
		gec6818led_res = request_mem_region(0XC001E000,0x1000,"GPIOE_MEM");
		if(gec6818led_res == NULL)
		{
			 printk(KERN_WARNING "request_mem_region error\n");
			 ret =  -EBUSY; //#define	EBUSY		16	/* Device or resource busy */
			 goto request_mem_region_error;
		}
		//9.Io dynamic mapping void__ iomem *ioremap(unsigned long phys_addr, size_t size)
		gec6818led_va_base = ioremap(0XC001E000,0x1000);
		
	     if(gec6818led_va_base == NULL)
		{
			 printk(KERN_WARNING "ioremap error\n");
			 ret =  -EBUSY; //#define	EBUSY		16	/* Device or resource busy */
			 goto ioremap_error;
		}
		gec6818led_va_eout = gec6818led_va_base + 0x00;
		gec6818led_va_eoutenb = gec6818led_va_base + 0x04;
		gec6818led_va_ealtfn0 = gec6818led_va_base + 0x20;
		
		//[1] Function of setting pin
		 *((volatile unsigned int*)gec6818led_va_ealtfn0) &= ~(3<<26);
		 //[2] Set the working mode of the pin: output
		  *((volatile unsigned int*)gec6818led_va_eoutenb) |= (1<<13);
		   //[3] The default setting is LED on
		   *((volatile unsigned int*)gec6818led_va_eout) &= ~(1<<13);
		 
	return 0;
	ioremap_error:
	release_mem_region(0XC001E000,0x1000);
	request_mem_region_error:
	device_destroy(gec6818led_class,gec6818led_dev);
	device_create_error:
	class_destroy(gec6818led_class);
	 gec6818led_class = NULL;
	class_create_error:
	cdev_del(&gec6818led_cdev);
	cdev_add_error:
	unregister_chrdev_region(gec6818led_dev,1);
	register_device_number_error:
	return ret;
	
}

static void __exit gec6818led_exit(void)
{
    printk(KERN_WARNING "gec6818led_exit\n");
	//Release resources 
	iounmap(gec6818led_va_base);
	gec6818led_va_base = NULL;
	release_mem_region(0XC001E000,0x1000);
	//void device_destroy(struct class *class, dev_t devt)
	device_destroy(gec6818led_class,gec6818led_dev);
	//void class_destroy(struct class *cls)
	 class_destroy(gec6818led_class);
	 gec6818led_class = NULL;
	//void cdev_del(struct cdev *p)
	cdev_del(&gec6818led_cdev);
	
	//void unregister_chrdev_region(dev_t from, unsigned count)
	
	unregister_chrdev_region(gec6818led_dev,1);
}

module_init(gec6818led_init);
module_exit(gec6818led_exit);

MODULE_AUTHOR("gec.zhang3");//Drive author
MODULE_DESCRIPTION("GEC6818 Led driver");//Description of drive
MODULE_LICENSE("GPL"); //module, GPL - > public general license

4, Application code

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>



int main(void)
{
	char buf[4096];
	//1. Open the file
	int fd = open("/udata/rfid_test.c",O_RDWR);
	if(fd == -1)
	{
		perror("open");
		return -1;
	}
	
	//2. Read data
	read(fd,buf,sizeof(buf));
	printf("data = %s\n",buf);
	
	
	//3. Close the file
	 close(fd);
	return 0;
}

Topics: Linux Operation & Maintenance Linux Driver