IIC总线框架+Input子系统驱动模板

内核版本:4.14.0
基于设备树
以i2c触摸屏为例

#include <linux/module.h> 
#include <linux/i2c.h> 
#include <linux/input/mt.h> 
#include <linux/of_gpio.h> 
#include <linux/delay.h> 
#include <linux/interrupt.h> 

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

/* 
 * FT5426 register define
 */ 
#define FT5426_DEVIDE_MODE_REG	0x00 // 模式寄存器 
#define FT5426_TD_STATUS_REG	0x02 // 状态寄存器 
#define FT5426_TOUCH_DATA_REG	0x03 // 触摸数据读取的起始寄存器 
#define FT5426_ID_G_MODE_REG	0xA4 // 中断模式寄存器 

#define MAX_SUPPORT_POINTS		5 	// ft5426 最大支持 5 点触摸 
#define RESOLUTION_WIDTH		800
#define RESOLUTION_HEIGHT		480

#define TOUCH_EVENT_DOWN		0x00 // 按下 
#define TOUCH_EVENT_UP			0x01 // 抬起 
#define TOUCH_EVENT_ON			0x02 // 接触 
#define TOUCH_EVENT_RESERVED	0x03 // 保留 

/* Device information structure. */
struct dev_info { 
	struct i2c_client *client;
	struct input_dev *inputdev;
	int reset_gpio;
	int irq_gpio;
};

/* 
 * @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; 
} 

static int ft5426_reset(struct i2c_client *client)
{
	struct dev_info *device_info;
	int ret;
	
	device_info = i2c_get_clientdata(client);
	
	/* get reset gpio from device tree */
	device_info->reset_gpio = of_get_named_gpio(client->dev.of_node, "reset-gpio", 0);
	if (!gpio_is_valid(device_info->reset_gpio))
	{
		dev_err(&client->dev, "Failed to get ft5426's reset gpio!\n");
		return device_info->reset_gpio;
	}
	
	/* request reset gpio */
	ret = devm_gpio_request_one(&client->dev, device_info->reset_gpio, GPIOF_OUT_INIT_HIGH, "ft5426 reset");
	if (ret < 0)
		return ret;
	
	msleep(20);
	gpio_set_value_cansleep(device_info->reset_gpio, 0);
	msleep(5);
	gpio_set_value_cansleep(device_info->reset_gpio, 1);
	
	return 0;
}

static void ft5426_init(struct i2c_client *client)
{
	u8 data;
	
	data = 0;
	device_write_reg(client, FT5426_DEVIDE_MODE_REG, &data, 1);
	data = 1;
	device_write_reg(client, FT5426_ID_G_MODE_REG, &data, 1);
}

static irqreturn_t ft5426_isr(int irq, void *dev_id)
{
	int ret, i, type, x, y, id;
	u8 read_buf[30] = {0};
	struct dev_info *device_info;
	u8 *buf;
	bool down;
	
	/* 读取FT5426 触摸点坐标从 0x02 寄存器开始,连续读取 29 个寄存器 */ 
	ret = device_read_reg(device_info->client, FT5426_TD_STATUS_REG, read_buf, 29);
	if (ret)
		goto out;
	
	for (i=0; i<MAX_SUPPORT_POINTS; i++)
	{
		buf = &read_buf[6*i+1];
		
		/* 以第一个触摸点为例,寄存器 TOUCH1_XH(地址0x03),各 bit 位描述如下: 
		* bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件 
		* bit5:4 保留 
		* bit3:0 X 轴触摸点的 11~8 位 
		* 以第一个触摸点为例,寄存器 TOUCH1_YH(地址0x05),各 bit 位描述如下: 
		* bit7:4 Touch ID 触摸 ID,表示是哪个触摸点 
		* bit3:0 Y 轴触摸点的 11~8 位。 
		*/ 
		type = buf[0]>>6;				//获取触摸点的Event Flag
		if (type == TOUCH_EVENT_RESERVED)
			continue;
		
		x = ((buf[0] << 8) | buf[1]) & 0x0FFF;
		y = ((buf[2] << 8) | buf[3]) & 0x0FFF; 
		
		id = (buf[2] >> 4) & 0x0F;
		down = type != TOUCH_EVENT_UP;
		
		input_mt_slot(device_info->inputdev, id);
		input_mt_report_slot_state(device_info->inputdev, MT_TOOL_FINGER, down);
		
		if (!down)
			continue;
		
		input_report_abs(device_info->inputdev, ABS_MT_POSITION_X, x); 
		input_report_abs(device_info->inputdev, ABS_MT_POSITION_Y, y); 	
	}
	
	input_mt_report_pointer_emulation(device_info->inputdev, true);
	input_sync(device_info->inputdev);
	
out:
	return IRQ_HANDLED;
}

static int ft5426_register_irq(struct dev_info *device_info)
{
	struct i2c_client *client = device_info->client;
	int ret;
	
	/* get intr gpio from device tree */
	device_info->irq_gpio = of_get_named_gpio(client->dev.of_node, "interrupt-gpio", 0);
	if (!gpio_is_valid(device_info->irq_gpio))
	{
		dev_err(&client->dev, "Failed to get interrupt gpio!\n");
		return device_info->irq_gpio;
	}
	
	/* request gpio */
	ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, ft5426_isr, 
			IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, device_info);
	if (ret)
	{
		dev_err(&client->dev, "Failed to request touchscreen IRQ!\n");
		return ret;
	}
	
	return 0;
}

/* 
 * @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 = ft5426_reset(client);
	if (ret)
		return ret;
	
	msleep(5);
	
	ft5426_init(client);
	
	return 0;
}

/* 
 * @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)
{
	struct dev_info *device_info;
	struct input_dev *inputdev;
	int ret;
	
	dev_info(&client->dev, "Driver and device matched successfully!\n");
	
	device_info = devm_kzalloc(&client->dev, sizeof(struct dev_info), GFP_KERNEL);
	if (!device_info)
	{
		dev_err(&client->dev, "Failed to allocate for struct dev_info!\n");
		return -ENOMEM;
	}
	
	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;
	
	/* Request and register interrupt service functions */
	ret = ft5426_register_irq(device_info);
	
	/* register input device */
	inputdev = devm_input_allocate_device(&client->dev);
	if (!inputdev)
	{
		dev_err(&client->dev, "Failed to allocate input device!\n");
		return -ENOMEM;
	}
	
	device_info->inputdev = inputdev;
	inputdev->name = DEVICE_NAME;
	inputdev->id.bustype = BUS_I2C;
	
	input_set_abs_params(inputdev, ABS_MT_POSITION_X, 0, RESOLUTION_WIDTH, 0, 0); 
	input_set_abs_params(inputdev, ABS_MT_POSITION_Y, 0, RESOLUTION_HEIGHT, 0, 0);
	
	ret = input_mt_init_slots(inputdev, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT); 
	if (ret)
	{
		dev_err(&client->dev, "Failed to init MT slots!\n"); 
		return ret;
	}
	
	ret = input_register_device(inputdev);
	if (ret)
		return ret;
	
	return 0;
} 

/* 
 * @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) 
{ 
	struct dev_info *device_info = i2c_get_clientdata(client); 

	input_unregister_device(device_info->inputdev); 

	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 = {
		.owner = THIS_MODULE,
		.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、付费专栏及课程。

余额充值