内核版本:4.14.0
基于设备树
以i2c触摸屏为例
#include
#include
#include
#include
#include
#include
#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 = ®
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");