IIC总线框架+通用字符设备驱动模板

内核版本:4.14.0,基于设备树
以读写实时时钟(RTC)为例

#include <linux/module.h> 
#include <linux/i2c.h> 
#include <linux/cdev.h> 
#include <linux/bcd.h> 
#include <linux/uaccess.h> 

#define DEVICE_CNT		1
#define DEVICE_NAME		"pcf8563"			/* Device name */
#define COMPAT_PROPT	"navigator,pcf8563"	/* Compatible property of the device matched with this driver. */

/* 
 * pcf8563 register define
 */ 
#define PCF8563_CTL_STATUS_1 	0x00 /* 控制寄存器1 */ 
#define PCF8563_CTL_STATUS_2 	0x01 /* 控制寄存器2 */ 
#define PCF8563_VL_SECONDS 		0x02 /* 时间: 秒 */ 
#define PCF8563_MINUTES 		0x03 /* 时间: 分 */ 
#define PCF8563_HOURS 			0x04 /* 时间: 小时 */ 
#define PCF8563_DAYS 			0x05 /* 日期: 天 */ 
#define PCF8563_WEEKDAYS 		0x06 /* 日期: 星期 */ 
#define PCF8563_CENTURY_MONTHS 	0x07 /* 日期: 月 */ 
#define PCF8563_YEARS 			0x08 /* 日期: 年 */ 

#define YEAR_BASE 				2000 /* 20xx 年 */ 

struct pcf8563_time { 
	int sec; // 秒 
	int min; // 分 
	int hour; // 小时 
	int day; // 日 
	int wday; // 星期 
	int mon; // 月份 
	int year; // 年 
}; 

/* Device information structure. */
struct dev_info { 
	struct i2c_client *client;
	dev_t devid;						/* Device ID */ 
	int major;							/* Major device id */ 
	int minor;							/* Minor device id */ 
	struct cdev cdev;
	struct class *class;
	struct device *device;
};

struct dev_info device_info;

/* 
 * @description :		Writing data to multiple consecutive registers of the I2C slave device. 
 * @param – client :	I2C slave device.
 * @param – reg : 		The first address of register to write. 
 * @param – buf : 		The buffer of data to write.
 * @param – len : 		The lenth of data to write.
 * @return : 			0 means successfully, negative means the write failed.
 */ 
static int device_write_reg(struct i2c_client *client, u8 reg, u8 *buf, u8 len) 
{ 
	struct i2c_msg msg; 
	u8 send_buf[17] = {0}; 
	int ret; 

	if (16 < len) 
	{ 
		dev_err(&client->dev, "%s: error: Invalid transfer byte length %d\n", __func__, len); 
		return -EINVAL; 
	} 

	send_buf[0] = reg;
	memcpy(&send_buf[1], buf, len);

	msg.addr = client->addr;
	/* Label as "Write Data" */
	msg.flags = 0; 
	msg.buf = send_buf;  
	msg.len = len + 1; 

	ret = i2c_transfer(client->adapter, &msg, 1); 
	if (1 != ret) 
	{ 
		dev_err(&client->dev, "%s: error: reg=0x%x, len=0x%x\n", __func__, reg, len); 
		return -EIO; 
	} 

	return 0; 
}

/* 
* @description :	Reading data from multiple consecutive registers of the I2C slave device.
* @param – client :	I2C slave device.
* @param – reg : 	The first address of register to read. 
* @param – buf : 	The buffer of data to read.
* @param – len : 	The lenth of data to read.
* @return : 		0 means successfully, negative means the read failed.
*/ 
static int device_read_reg(struct i2c_client *client, u8 reg, u8 *buf, u8 len) 
{ 
	struct i2c_msg msg[2]; 
	int ret; 

	/* msg[0]: write data */ 
	msg[0].addr = client->addr;
	/* Label as "Write Data" */
	msg[0].flags = 0; 
	msg[0].buf = &reg; 
	msg[0].len = 1;

	/* msg[1]: read data */ 
	msg[1].addr = client->addr; 
	/* Label as "Read Data" */
	msg[1].flags = I2C_M_RD; 
	msg[1].buf = buf; 
	msg[1].len = len; 

	ret = i2c_transfer(client->adapter, msg, 2); 
	if (2 != ret) 
	{ 
		dev_err(&client->dev, "%s: error: reg=0x%x, len=0x%x\n", __func__, reg, len); 
		return -EIO; 
	} 

	return 0; 
} 

/* 
 * @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)  
{ 
	struct dev_info *p_dev_info = filp->private_data; 
	struct i2c_client *client = p_dev_info->client; 
	struct pcf8563_time time = {0}; 
	u8 read_buf[9] = {0}; 
	int ret; 

	/* 读寄存器数据 */ 
	ret = device_read_reg(client, PCF8563_CTL_STATUS_1, read_buf, 9); 
	if (ret) 
		return ret; 

	/* 校验时钟完整性 */ 
	if (read_buf[PCF8563_VL_SECONDS] & 0x80) 
	{ 
		dev_err(&client->dev, "low voltage detected, date/time is not reliable.\n"); 
		return -EINVAL;
	} 

	/* 将BCD 码转换为数据得到时间、日期 */ 
	time.sec = bcd2bin(read_buf[PCF8563_VL_SECONDS] & 0x7F); // 秒 
	time.min = bcd2bin(read_buf[PCF8563_MINUTES] & 0x7F); // 分 
	time.hour = bcd2bin(read_buf[PCF8563_HOURS] & 0x3F); // 小时 
	time.day = bcd2bin(read_buf[PCF8563_DAYS] & 0x3F); // 日 
	time.wday = read_buf[PCF8563_WEEKDAYS] & 0x07; // 星期 
	time.mon = bcd2bin(read_buf[PCF8563_CENTURY_MONTHS] & 0x1F); // 月 
	time.year = bcd2bin(read_buf[PCF8563_YEARS]) + YEAR_BASE; // 年 

	/* 将数据拷贝到用户空间 */ 
	return copy_to_user(buf, &time, sizeof(struct pcf8563_time)); 
} 

/* 
 * @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) 
{ 
	struct dev_info *p_dev_info = filp->private_data; 
	struct pcf8563_time time = {0}; 
	u8 write_buf[9] = {0}; 
	int ret; 

	ret = copy_from_user(&time, buf, cnt); // 得到应用层传递过来的数据 
	if(0 > ret) 
		return -EFAULT; 

	/* 将数据转换为 BCD 码 */ 
	write_buf[PCF8563_VL_SECONDS] = bin2bcd(time.sec); // 秒 
	write_buf[PCF8563_MINUTES] = bin2bcd(time.min); // 分 
	write_buf[PCF8563_HOURS] = bin2bcd(time.hour); // 小时 
	write_buf[PCF8563_DAYS] = bin2bcd(time.day); // 日 
	write_buf[PCF8563_WEEKDAYS] = time.wday & 0x07; // 星期 
	write_buf[PCF8563_CENTURY_MONTHS] = bin2bcd(time.mon); // 月 
	write_buf[PCF8563_YEARS] = bin2bcd(time.year % 100); // 年 

	/* 将数据写入寄存器 */ 
	ret = device_write_reg(p_dev_info->client, PCF8563_VL_SECONDS, &write_buf[PCF8563_VL_SECONDS], 7); 
	if (ret) 
		return ret; 

	return cnt;
} 

/* 
 * @description :	Close/Release the device.
 * @param – inode :	The inode passed to the driver.
 * @param - filp :	The device file (file descriptor) to close.
 * @return :		0: Successful; Others: Failed.
 */ 
static int device_release(struct inode *inode, struct file *filp) 
{ 

	return 0; 
} 

/* The device operation function structure. */
static const struct file_operations device_fops = {
	.owner = THIS_MODULE,
	.open = device_open,
	.read = device_read,
	.write = device_write,
	.release = device_release,
}; 

/* 
 * @description :	Initialize the device.
 * @param -pdev:	Pointer to client struct.
 * @return :		0: Successful; Others: Failed.
 */
static int device_init(struct i2c_client *client) 
{ 
	int ret;
	u8 val;
	
	ret = device_read_reg(client, PCF8563_VL_SECONDS, &val, 1);
	if (ret)
		return ret;
	
	val &= 0x7F; // 将寄存器最高一位清零,也就是将 VL 位清零 
	
	return device_write_reg(client, PCF8563_VL_SECONDS, &val, 1); // 写入 VL_SECONDS 寄存器 
}

/* 
 * @description :	Probe function of the platform, it will be executed when the 
 * 					platform driver and platform device matching successfully.
 * @param -client :	Pointer to i2c_client struct, means i2c client/device.
 * @param -id :		Pointer to i2c_device_id struct, include name and private data.
 * @return :		0: Successful; Others: Failed.
 */
static int device_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret;
	
	dev_info(&client->dev, "Driver and device matched successfully!\n");
	
	device_info.client = client;
	
	/* Store the dev_info pointer in client->dev.driver_data for later use */
	i2c_set_clientdata(client, &device_info);
	
	/* Device init */
	ret = device_init(client);
	if (ret)
		return ret;

	/* 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: device id: 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, &client->dev, 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 code to release resources */
	
	return ret; 
} 

/* 
 * @description :	Release some resources. This function will be executed when the platform
 *					driver module is unloaded.
 * @param -client :	Pointer to i2c_client struct, means i2c client/device.
 * @return :		0: Successful; Others: Failed.
 */
static int device_remove(struct i2c_client *client) 
{ 
	/* 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 code to release resources */

	
	printk(KERN_INFO "%s: Driver removed!\n", DEVICE_NAME);

	return 0;
} 

/* Match table */
static const struct of_device_id device_of_match[] = {
	{.compatible = COMPAT_PROPT},
	{/* Sentinel */}
};

/* 
 * Declare device matching table. Note that this macro is generally used to dynamically 
 * load and unload drivers for hot-pluggable devices such as USB devices.
 */
MODULE_DEVICE_TABLE(of, device_of_match); 

/* i2c driver struct */
static struct i2c_driver device_driver = {
	.driver = {
		.name = DEVICE_NAME,				//Drive name, used to match device who has the same name.
		.of_match_table = device_of_match,	//Used to match the device tree who has the same compatible property.
	},
	.probe = device_probe,					//probe function
	.remove = device_remove,				//remove function
};

/*
 * Register or unregister i2c driver,
 * and Register the entry and exit functions of the Module.
 */
module_i2c_driver(device_driver);

/* 
 *  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、付费专栏及课程。

余额充值