Linux字符设备之IO简单读写驱动模板

内核版本: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");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值