DeviceDriver(十二):I2C驱动

一:Linux I2C驱动框架

Linux内核将I2C驱动分为两部分:

(1)I2C总线驱动,I2C总线驱动就是SOC的I2C控制器驱动,也叫做I2C适配器驱动

(2)I2C设备驱动,I2C设备驱动就是针对具体的I2C设备而编写的驱动。

作为SOC开发者来讲无需关心i2c总线驱动,只需关注i2c设备驱动即可。

1、I2C总线驱动

       I2C设备与platform设备不同的是,I2C拥有自己的总线即I2C总线而不需要虚拟出一条来。I2C总线驱动重点是I2C适配器(SOC的I2C接口控制器)驱动,其中有两个重要的数据结构:i2c_adapter和i2c_algorithm,Linux内核将I2C适配器抽象成i2c_adapter结构体:

include/linux/i2c.h

struct i2c_adapter {
	struct module *owner;
	unsigned int class;		  /* classes to allow probing for */
	const struct i2c_algorithm *algo; /* the algorithm to access the bus */
	void *algo_data;

	/* data fields that are valid for all devices	*/
	struct rt_mutex bus_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */

	int nr;
	char name[48];
	struct completion dev_released;

	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;

	struct i2c_bus_recovery_info *bus_recovery_info;
	const struct i2c_adapter_quirks *quirks;
};

对于一个i2c适配器,需要对外提供读写API函数,设备驱动程序就可以使用这些API函数来完成读写操作,其中i2c_algorithm类型的指针变量algo就是i2c适配器与i2c设备进行通信的方法。

struct i2c_algorithm {
	/* If an adapter algorithm can't do I2C-level access, set master_xfer
	   to NULL. If an adapter algorithm can do SMBus access, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	/* To determine what the adapter supports */
	u32 (*functionality) (struct i2c_adapter *);

#if IS_ENABLED(CONFIG_I2C_SLAVE)
	int (*reg_slave)(struct i2c_client *client);
	int (*unreg_slave)(struct i2c_client *client);
#endif
};

master_xfer:i2c适配器的传输函数,可以通过此函数来完成与i2c设备之间的通信。

smbus_xfer:SMBUS总线的传输函数。

i2c总线驱动的主要工作任务就是初始化i2c_adapter结构体变量,然后设置i2c_algorithm中的master_xfer函数。完成后通过i2c_add_numberd_adapter和i2c_add_adapter这两个函数向系统注册设置好的i2c_adapter,原型如下:

int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)

其中adapter 和 adap:要添加到 Linux 内核中的 i2c_adapter,也就是 I2C 适配器。

2、I2C设备驱动

        i2c设备驱动重点关注两个数据结构:i2c_client和i2c_driver。根据总线,设备,驱动模型划分,i2c_client描述设备信息,i2c_driver描述驱动内容,类似于platform_driver。

(1)i2c_client:一个设备对应一个i2c_client,每检测到一个i2c设备就会分配一个该数据

struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;	/* callback for slave mode	*/
#endif
};

(2)i2c_driver:类似于platform_driver,当i2c设备与驱动匹配成功后probe函数就会执行。

struct i2c_driver {
	unsigned int class;

	int (*attach_adapter)(struct i2c_adapter *) __deprecated;

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);

	void (*shutdown)(struct i2c_client *);

	void (*alert)(struct i2c_client *, unsigned int data);

	int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

	struct device_driver driver;
	const struct i2c_device_id *id_table;

	/* Device detection callback for automatic device creation */
	int (*detect)(struct i2c_client *, struct i2c_board_info *);
	const unsigned short *address_list;
	struct list_head clients;
};

如果使用设备树的话,需要设置device_driver的of_match_table成员变量,也就是驱动的兼容属性compatible。

对于i2c设备驱动来讲,重点工作就是构建i2c_driver,构建完成以后需要向linux内核注册这个i2c_driver。注册函数为i2c_register_driver:

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

或者使用注册宏定义:i2c_add_driver

#define i2c_add_driver(driver) \
	i2c_register_driver(THIS_MODULE, driver)

注销函数:i2c_del_driver

void i2c_del_driver(struct i2c_driver *driver)

i2c_driver注册示例:

/* i2c 驱动的 probe 函数 */
static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	/* 函数具体程序 */
	return 0;
}

/* i2c 驱动的 remove 函数 */
static int ap3216c_remove(struct i2c_client *client)
{
	/* 函数具体程序 */
	return 0;
}

/* 传统匹配方式 ID 列表 */
static const struct i2c_device_id xxx_id[] = {
	{"xxx", 0},
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
	{ .compatible = "xxx" },
	{ /* Sentinel */ }
};

/* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = {
	.probe = xxx_probe,
	.remove = xxx_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.id_table = xxx_id,
};

/* 驱动入口函数 */
static int __init xxx_init(void)
{
	int ret = 0;
	ret = i2c_add_driver(&xxx_driver);
	return ret;
}

/* 驱动出口函数 */
static void __exit xxx_exit(void)
{
	i2c_del_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);

3、I2C设备和驱动匹配

设备和驱动的匹配过程是由I2C总线完成的,I2C总线的数据结构为i2c_bus_type:

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

成员函数match就是i2c总线的设备和驱动的匹配函数: i2c_device_match

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

二:I2C适配器驱动分析

一般情况i2c适配器驱动不需要我们编写,做个简单分析。

在对应SOC设备树共用文件(imx6ull.dtsi)中找到i2c1控制器节点:i2c1

i2c1: i2c@021a0000 {
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
	reg = <0x021a0000 0x4000>;
	interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_I2C1>;
	status = "disabled";
};

搜寻compatible属性即可找到i2c适配器驱动文件:

static struct platform_device_id imx_i2c_devtype[] = {
	{
		.name = "imx1-i2c",
		.driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
	}, {
		.name = "imx21-i2c",
		.driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,
	}, {
		/* sentinel */
	}
};
MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);

static const struct of_device_id i2c_imx_dt_ids[] = {
	{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
	{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
	{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);


static struct platform_driver i2c_imx_driver = {
	.probe = i2c_imx_probe,
	.remove = i2c_imx_remove,
	.driver	= {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table = i2c_imx_dt_ids,
		.pm = IMX_I2C_PM,
	},
	.id_table	= imx_i2c_devtype,
};

static int __init i2c_adap_imx_init(void)
{
	return platform_driver_register(&i2c_imx_driver);
}
subsys_initcall(i2c_adap_imx_init);

static void __exit i2c_adap_imx_exit(void)
{
	platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit);

分析适配器驱动可以看出i2c适配器驱动是个标准的platform驱动框架,而i2c设备驱动用的是i2c总线驱动框架。

当设备和驱动匹配成功后,i2c_imx_probe函数就会执行:

static int i2c_imx_probe(struct platform_device *pdev)

i2c_imx_probe函数主要的工作有两点:

(1)初始化i2c_adapter,设置i2c_algorithm 为 i2c_imx_algo,最后想Linux内核注册i2c_adapter

(2)初始化i2c1控制器的相关寄存器

i2c_imx->adapter.algo		= &i2c_imx_algo;

static struct i2c_algorithm i2c_imx_algo = {
	.master_xfer	= i2c_imx_xfer,
	.functionality	= i2c_imx_func,
};

.functionality用于返回i2c适配器支持什么样的通讯协议:

static u32 i2c_imx_func(struct i2c_adapter *adapter)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
		| I2C_FUNC_SMBUS_READ_BLOCK_DATA;
}

.master_xfer用来完成与i2c设备通信

static int i2c_imx_xfer(struct i2c_adapter *adapter,
						struct i2c_msg *msgs, int num)
{
	unsigned int i, temp;
	int result;
	bool is_lastmsg = false;
	struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);

	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

	/* Start I2C transfer */
	result = i2c_imx_start(i2c_imx);
	if (result)
		goto fail0;

	/* read/write data */
	for (i = 0; i < num; i++) {
		if (i == num - 1)
			is_lastmsg = true;

		if (i) {
			dev_dbg(&i2c_imx->adapter.dev,
				"<%s> repeated start\n", __func__);
			temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
			temp |= I2CR_RSTA;
			imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
			result =  i2c_imx_bus_busy(i2c_imx, 1);
			if (result)
				goto fail0;
		}
		dev_dbg(&i2c_imx->adapter.dev,
			"<%s> transfer message: %d\n", __func__, i);
		/* write/read data */
... ...
		if (msgs[i].flags & I2C_M_RD)
			result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
		else {
			if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
				result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
			else
				result = i2c_imx_write(i2c_imx, &msgs[i]);
		}
		if (result)
			goto fail0;
	}

fail0:
	/* Stop I2C transfer */
	i2c_imx_stop(i2c_imx);

	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
		(result < 0) ? "error" : "success msg",
			(result < 0) ? result : num);
	return (result < 0) ? result : num;
}

其中i2c_imx_start,i2c_imx_read,i2c_imx_write,i2c_imx_stop这些函数就是i2c寄存器的具体操作函数。

三:I2C设备驱动流程

1、未使用设备树

未使用设备树的时候需要在BSP里面使用i2c_board_info结构体来描述一个具体的I2C设备。

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	struct fwnode_handle *fwnode;
	int		irq;
};

type和addr这两个成员变量是必须要设置的,一个是I2C设备的名字,一个是I2C设备的器件地址。举例OV2640设备

static struct i2c_board_info mx27_3ds_i2c_camera = {
	I2C_BOARD_INFO("ov2640", 0x30),
};

#define I2C_BOARD_INFO(dev_type, dev_addr) \
	.type = dev_type, .addr = (dev_addr)

2、使用设备树

使用设备树时i2c设备信息通过创建相应的节点就可以,例如 imx6ull-14x14-evk.dts

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	mag3110@0e {
		compatible = "fsl,mag3110";
		reg = <0x0e>;
		position = <2>;
	};
        ... ...
};

"mag3110@0e"为子节点名称,“@”后面的“0e”为mag3110的i2c设备地址,与"reg = <0x0e>"相同。

compatible = "fsl,mag3110";用于设置i2c设备属性用于匹配驱动。

3、设备数据收发处理

       第一章节中已经讲过,i2c设备驱动首先要做的就是初始化i2c_driver并想Linux内核注册。当设备和驱动匹配以后i2c_driver里面的probe函数就会执行,probe函数里面所做的就是字符设备驱动的那一套,一般需要初始化i2c设备,初始化i2c设备就需要对i2c设备寄存器进行读写操作,就需要要用到i2c_transfer函数,该函数最终会调用i2c适配器中i2c_algorithm里面的master_xfer函数,对于imx6u来说就是i2c_imx_xfer函数。

int i2c_transfer(struct i2c_adapter *adap,
    struct i2c_msg *msgs,
    int num)

其中msgs这个参数是一个i2c_msg类型的指针参数,Linux内核中i2c设备的数据收发消息就用i2c_msg结构体来描述:

struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

使用i2c_transfer函数发送数据之前要先构建好i2c_msg。

除过i2c_transfer函数还有两个API可以使用:i2c_master_send和i2c_master_recv,最终还是会调用到i2c_transfer

int i2c_master_send(const struct i2c_client *client,
    const char *buf,
    int count)

int i2c_master_recv(const struct i2c_client *client,
    char *buf,
    int count)

4、举例说明:mag3110设备

根据设备节点compatible属性搜寻:

#define MAG3110_DRV_NAME       "mag3110"


static const struct i2c_device_id mag3110_id[] = {
	{MAG3110_DRV_NAME, 0},
	{}
};

MODULE_DEVICE_TABLE(i2c, mag3110_id);
static struct i2c_driver mag3110_driver = {
	.driver = {
		.name	= MAG3110_DRV_NAME,
		.owner	= THIS_MODULE,
		.pm	= MAG3110_DEV_PM_OPS,
	},
	.probe = mag3110_probe,
	.remove = mag3110_remove,
	.id_table = mag3110_id,
};

static int __init mag3110_init(void)
{
	return i2c_add_driver(&mag3110_driver);
}

static void __exit mag3110_exit(void)
{
	i2c_del_driver(&mag3110_driver);
}

分析probe函数结构:mag3110_probe

static int mag3110_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
{
    ... ...
	/* Initialize mag3110 chip */
	mag3110_init_client(client);
}

-->>mag3110_init_client(client);
    -->> mag3110_write_reg(client, MAG3110_CTRL_REG2, val);
        -->> i2c_smbus_write_byte_data(client, reg, value);
            -->> i2c_smbus_xfer(client->adapter, client->addr, client->flags,
			          I2C_SMBUS_WRITE, command,
			          I2C_SMBUS_BYTE_DATA, &data);
                -->> i2c_smbus_xfer_emulated(adapter, addr, flags, read_write,
				          command, protocol, data);
                    -->> i2c_transfer(adapter, msg, num);

接着第三小节举例说明i2c_transfer函数中msg参数的构建过程:具体含义参考第三小节i2c_msg结构体

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
				   unsigned short flags,
				   char read_write, u8 command, int size,
				   union i2c_smbus_data *data)
{
	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];

	struct i2c_msg msg[2] = {
		{
			.addr = addr,
			.flags = flags,
			.len = 1,
			.buf = msgbuf0,
		}, {
			.addr = addr,
			.flags = flags | I2C_M_RD,
			.len = 0,
			.buf = msgbuf1,
		},
	};

    ... ...
	case I2C_SMBUS_PROC_CALL:
		num = 2; /* Special case */
		read_write = I2C_SMBUS_READ;
		msg[0].len = 3;
		msg[1].len = 2;
		msgbuf0[1] = data->word & 0xff;
		msgbuf0[2] = data->word >> 8;
		break;
    ... ...

	status = i2c_transfer(adapter, msg, num);
}

四:示例代码

1、修改设备树

pinctrl_i2c1: i2c1grp {
    fsl,pins = <
        MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
        MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
    >;
};

&i2c1 {
	clock-frequency = <100000>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_i2c1>;
	status = "okay";

	ap3216c@1e {
		compatible = "alientek,ap3216c";
		reg = <0x1e>;
	};
};

2、驱动

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "ap3216creg.h"


#define AP3216C_CNT     1
#define AP3216C_NAME    "ap3216c"

struct ap3216c_dev {
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int major;
    void *private_data;
    unsigned short ir, als, ps;
};

struct ap3216c_dev ap3216cdev;

static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
    int ret;
    struct i2c_msg msg[2];
    struct i2c_client *client = (struct i2c_client *)dev->private_data;

    /* msg[0]为发送要读取的首地址 */
    msg[0].addr = client->addr; /* ap3216c 地址 */
    msg[0].flags = 0; /* 标记为发送数据 */
    msg[0].buf = &reg; /* 读取的首地址 */
    msg[0].len = 1; /* reg 长度 */

    /* msg[1]读取数据 */
    msg[1].addr = client->addr; /* ap3216c 地址 */
    msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
    msg[1].buf = val; /* 读取数据缓冲区 */
    msg[1].len = len; /* 要读取的数据长度 */

    ret = i2c_transfer(client->adapter, msg, 2);
    if(ret == 2) {
        ret = 0;
    } else {
        printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
        ret = -EREMOTEIO;
    }
    return ret;
}


static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
    u8 b[256];
    struct i2c_msg msg;
    struct i2c_client *client = (struct i2c_client *)dev->private_data;

    b[0] = reg;                         /* 寄存器首地址 */
    memcpy(&b[1], buf, len);            /* 将要写入的数据拷贝到数组 b 里面 */
    
    msg.addr = client->addr;            /* ap3216c 地址 */
    msg.flags = 0;                      /* 标记为写数据 */
    msg.buf = b;                        /* 要写入的数据缓冲区 */
    msg.len = len + 1;                  /* 要写入的数据长度 */

    return i2c_transfer(client->adapter, &msg, 1);
}

static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
    u8 data = 0;
    ap3216c_read_regs(dev, reg, &data, 1);

    return data;
}

static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
    u8 buf = 0;
    buf = data;
    ap3216c_write_regs(dev, reg, &buf, 1);
}

void ap3216c_readdata(struct ap3216c_dev *dev)
{
    unsigned char i = 0;
    unsigned char buf[6];

    for(i = 0; i < 6; i++)
    {
        buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i);
    }

    if(buf[0] & 0x80)
        dev->ir = 0;
    else
        dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03);

    dev->als = ((unsigned short)buf[3] << 8) | buf[2];/* ALS 数据 */
    if(buf[4] & 0x40) /* IR_OF 位为 1,则数据无效 */
        dev->ps = 0;
    else
        dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}

static int ap3216c_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &ap3216cdev;
    ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04);
    mdelay(50);
    ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x03);
    return 0;
}

static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    short data[3];
    long err = 0;

    struct ap3216c_dev *dev = (struct ap3216c_dev *)filp->private_data;

    ap3216c_readdata(dev);

    data[0] = dev->ir;
    data[1] = dev->als;
    data[2] = dev->ps;
    err = copy_to_user(buf, data, sizeof(data));
    return 0;
}

static int ap3216c_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations ap3216c_fops = {
    .owner = THIS_MODULE,
    .open  = ap3216c_open,
    .read  = ap3216c_read,
    .release = ap3216c_release,
};

static int ap3216c_probe(struct i2c_client *client,
    const struct i2c_device_id *id)
{
    if(ap3216cdev.major)
    {
        ap3216cdev.devid = MKDEV(ap3216cdev.major, 0);
        register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME);
    }
    else
    {
        alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME);
        ap3216cdev.major = MAJOR(ap3216cdev.devid);
    }

    cdev_init(&ap3216cdev.cdev, &ap3216c_fops);
    cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT);

    ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME);
    ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME);

    ap3216cdev.private_data = client;

    return 0;
}

static int ap3216c_remove(struct i2c_client *client)
{
    device_destroy(ap3216cdev.class, ap3216cdev.devid);
    class_destroy(ap3216cdev.class);

    cdev_del(&ap3216cdev.cdev);
    unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT);

    return 0;
}

static const struct i2c_device_id ap3216c_id[] = {
    {"ap3216c", 0},
    {}
};

static const struct of_device_id ap3216c_of_match[] = {
    {.compatible = "alientek, ap3216c"},
    {}
};


static struct i2c_driver ap3216c_driver = {
    .probe = ap3216c_probe,
    .remove = ap3216c_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "ap3216c",
        .of_match_table = ap3216c_of_match,
    },
    .id_table = ap3216c_id,
};

static int ap3216c_init(void)
{
    int ret = 0;
    
    ret = i2c_add_driver(&ap3216c_driver);
    return ret;
}

static void ap3216c_exit(void)
{
    i2c_del_driver(&ap3216c_driver);
}

// module_i2c_driver(ap3216c_driver);
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");

猜你喜欢

转载自blog.csdn.net/qq_34968572/article/details/104819330