内核版本:4.14.0,基于设备树
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#define DEVICE_CNT 1 /* Number of device id */
#define DEVICE_NAME "xxx" /* Device name */
#define COMPAT_PROPT "xxx,xxx" /* Compatible property of the device matched with this driver. */
/* Device information structure. */
struct dev_info {
dev_t devid; /* Device ID */
struct cdev cdev; /* cdev */
struct class *class;
struct device *device;
int major; /* Major device id */
int minor; /* Minor device id */
struct device_node *nd; /* Device node */
wait_queue_head_t WaitQueue; /*Wait queue head */
struct fasync_struct *async_queue;
};
static struct dev_info device_info;
/*
* @description : Open the device.
* @param – inode : The inode passed to the driver.
* @param - filp : Device file, the file structure has a member variable called 'private_data',
* which is normally pointed to the device structure when it is opened.
* @return : 0: Successful; Others: Failed.
*/
static int device_open(struct inode *inode, struct file *filp)
{
filp->private_data = &device_info; /* Set private data.*/
return 0;
}
/*
* @description : Read from the device.
* @param - filp : The device file (file descriptor) to open.
* @param - buf : The data buffer returns to the user space.
* @param - cnt : The length of the data to be read.
* @param - offt : Offset relative to the first address of the file.
* @return : The number of bytes read, negative means the read failed.
*/
static ssize_t device_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
if (filp->f_flags & O_NONBLOCK) //Read by nonblock mode.
{
}
else //Read by block mode.
{
/* Wait the wait queue is waked up */
ret = wait_event_interruptible(device_info.r_wait, xxx);
if (ret)
return ret;
}
ret = copy_to_user(buf, &xxx, xxx);
return xxx-ret ;
}
/*
* @description : Write to the device
* @param - filp : Device file, indicating the opened file descriptor.
* @param - buf : The data to write to the device.
* @param - cnt : The length of the data to be written.
* @param - offt : Offset relative to the first address of the file.
* @return : The number of bytes written, negative means the write failed.
*/
static ssize_t device_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int ret;
ret = copy_from_user(xxx, buf, cnt);
if (ret < 0)
{
printk(KERN_ERR "kernel get data failed!\n");
return -EFAULT;
}
return cnt;
}
/*
* @description : The ioctl function.
* @param - filp : Device file, indicating the opened file descriptor.
* @param - cmd : Command form app.
* @param - arg : Parameter from app.
* @return : 0: Successful; Others: Failed.
*/
static long device_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
return 0 ;
}
/*
* @description : Handle the block access.
* @param - filp : Device file, indicating the opened file descriptor.
* @param - wait : Wait table from app.
* @return : status of the device or resource.
*/
static unsigned int device_poll(struct file *filp, struct poll_table_struct *wait)
{
/* Add waiting queue header that may cause device state changes to the poll_table */
poll_wait(filp, &device_info.WaitQueue, wait);
/* Return status of the device or resource according to fact */
if (xxx)
return xxx;
else
return 0;
}
/*
* @description : Handle the async notice.Asynchronously flush the pending data
* in the buffer to the disk.
* @param - fd : File descriptor.
* @param - filp : Device file, indicating the opened file descriptor.
* @param - on : Mode.
* @return : Negative means the operation failed.
*/
static int device_fasync(int fd, struct file *filp, int on)
{
/* Initialize the fasync_struct structure pointer */
return fasync_helper(fd, filp, on, &device_info.async_queue);
}
/*
* @description : Close/Release the device.
* @param - filp : The device file (file descriptor) to close.
* @return : 0: Successful; Others: Failed.
*/
static int device_release(struct inode *inode, struct file *filp)
{
/* Release the fasync_struct structure */
return device_fasync(-1, filp, 0);
}
/* The device operation function structure. */
static struct file_operations device_fops = {
.owner = THIS_MODULE,
.open = device_open,
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_unlocked_ioctl,
.poll = device_poll,
/*
* When an application changes the fasync flag by calling "fcntl(fd, F_SETFL, flags | FASYNC)",
* fasync function will be executed.
*/
.fasync = device_fasync,
.release = device_release,
};
/*
* @description : Entry function of the driver.
* @param : None.
* @return : 0: Successful; Others: Failed.
*/
static int __init device_init(void)
{
int ret;
const char *str;
/* Get device node. */
device_info.nd = of_find_node_by_path("/"DEVICE_NAME);
if (device_info.nd == NULL)
{
printk(KERN_ERR "%s node can not be found!\n", DEVICE_NAME);
return -EINVAL;
}
/* Get the 'status' property. */
ret = of_property_read_string(device_info.nd, "status", &str);
if (!ret)
{
if (strcmp(str, "okay"))
return -EINVAL;
}
/* Get 'compatible' property and match. */
ret = of_property_read_string(device_info.nd, "compatible", &str);
if (ret < 0)
{
printk(KERN_ERR "Failed to get compatible property\n");
return -EINVAL;
}
else if (strcmp(str, COMPAT_PROPT))
{
printk(KERN_ERR "Compatible match failed\n");
return -EINVAL;
}
printk(KERN_ERR "%s device matching successful!\n", DEVICE_NAME);
/* Initialize driver. */
/* Creat device id. */
if (device_info.major)
{
device_info.devid = MKDEV(device_info.major, 0);
ret = register_chrdev_region(device_info.devid, DEVICE_CNT, DEVICE_NAME);
if (ret)
goto out1;
}
else
{
ret = alloc_chrdev_region(&device_info.devid, 0, DEVICE_CNT, DEVICE_NAME);
if (ret)
goto out1;
device_info.major = MAJOR(device_info.devid);
device_info.minor = MINOR(device_info.devid);
}
printk("%s major=%d,minor=%d\r\n",DEVICE_NAME, device_info.major, device_info.minor);
/* Initialize char device. */
device_info.cdev.owner = THIS_MODULE;
cdev_init(&device_info.cdev, &device_fops);
/* Add a char device. */
ret = cdev_add(&device_info.cdev, device_info.devid, DEVICE_CNT);
if (ret)
goto out2;
/* Creat class. */
device_info.class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(device_info.class))
{
ret = PTR_ERR(device_info.class);
goto out3;
}
/* Creat device (node) file. */
device_info.device = device_create(device_info.class, NULL, device_info.devid, NULL, DEVICE_NAME);
if (IS_ERR(device_info.device))
{
ret = PTR_ERR(device_info.device);
goto out4;
}
return 0;
out4:
class_destroy(device_info.class);
out3:
cdev_del(&device_info.cdev);
out2:
unregister_chrdev_region(device_info.devid, DEVICE_CNT);
out1:
/* Some reset code. */
return ret;
}
/*
* @description : Exit function of the driver.
* @param : None.
* @return : None.
*/
static void __exit device_exit(void)
{
/* Logoff device (node). */
device_destroy(device_info.class, device_info.devid);
/* Logoff class. */
class_destroy(device_info.class);
/* Delete char device */
cdev_del(&device_info.cdev);
/* Logoff device id. */
unregister_chrdev_region(device_info.devid, DEVICE_CNT);
/* Some reset code. */
return;
}
/*
* Register the entry and exit functions of the driver.
*/
module_init(device_init);
module_exit(device_exit);
/*
* Author, driver information and LICENSE.
*/
MODULE_AUTHOR("蒋楼丶");
MODULE_DESCRIPTION(DEVICE_NAME" Driver");
MODULE_LICENSE("GPL");