I2C
子系统:作用是完成驱动和设备的匹配。
MISC
子系统:作用是简化字符设备注册过程。
I2C
控制器驱动使用 platform
总线驱动框架。
I2C
设备驱动使用 I2C
总线驱动框架。
I²C(IIC)总线协议详解—完整版 - 知乎 (zhihu.com)
I2C
驱动分为 2
部分:I2C
控制器驱动(也叫 I2C
适配器驱动)和 I2C
设备驱动。
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);
I2C
总线是对 platform
总线进行封装,主要作用是匹配 I2C
设备和 I2C
驱动。
/** i2c_adapter is the structure used to identify a physical i2c bus along* with the access algorithms necessary to access it.*/
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;
};
struct i2c_adapter
表示 I2C
控制器驱动,用于驱动 SOC
集成的 I2C
控制器。
/*** struct i2c_algorithm - represent I2C transfer method* @master_xfer: Issue a set of i2c transactions to the given I2C adapter* defined by the msgs array, with num messages available to transfer via* the adapter specified by adap.* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this* is not present, then the bus layer will try and convert the SMBus calls* into I2C transfers instead.* @functionality: Return the flags that this algorithm/adapter pair supports* from the I2C_FUNC_* flags.* @reg_slave: Register given client to I2C slave mode of this adapter* @unreg_slave: Unregister given client from I2C slave mode of this adapter** The following structs are for those who like to implement new bus drivers:* i2c_algorithm is the interface to a class of hardware solutions which can* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584* to name two of the most common.** The return codes from the @master_xfer field should indicate the type of* error code that occurred during the transfer, as documented in the kernel* Documentation file Documentation/i2c/fault-codes.*/
struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, 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
};
struct i2c_algorithm
表示 I2C
时序规则。
/*** struct i2c_driver - represent an I2C device driver* @class: What kind of i2c device we instantiate (for detect)* @attach_adapter: Callback for bus addition (deprecated)* @probe: Callback for device binding* @remove: Callback for device unbinding* @shutdown: Callback for device shutdown* @alert: Alert callback, for example for the SMBus alert protocol* @command: Callback for bus-wide signaling (optional)* @driver: Device driver model driver* @id_table: List of I2C devices supported by this driver* @detect: Callback for device detection* @address_list: The I2C addresses to probe (for detect)* @clients: List of detected clients we created (for i2c-core use only)** The driver.owner field should be set to the module owner of this driver.* The driver.name field should be set to the name of this driver.** For automatic device detection, both @detect and @address_list must* be defined. @class should also be set, otherwise only devices forced* with module parameters will be created. The detect function must* fill at least the name field of the i2c_board_info structure it is* handed upon successful detection, and possibly also the flags field.** If @detect is missing, the driver will still work fine for enumerated* devices. Detected devices simply won't be supported. This is expected* for the many I2C/SMBus devices which can't be detected reliably, and* the ones which can always be enumerated in practice.** The i2c_client structure which is handed to the @detect callback is* not a real i2c_client. It is initialized just enough so that you can* call i2c_smbus_read_byte_data and friends on it. Don't do anything* else with it. In particular, calling dev_dbg and friends on it is* not allowed.*/
struct i2c_driver {unsigned int class;/* Notifies the driver that a new bus has appeared. You should avoid* using this, it will be removed in a near future.*/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 *);/* driver model interfaces that don't relate to enumeration */void (*shutdown)(struct i2c_client *);/* Alert callback, for example for the SMBus alert protocol.* The format and meaning of the data value depends on the protocol.* For the SMBus alert protocol, there is a single bit of data passed* as the alert response's low bit ("event flag").*/void (*alert)(struct i2c_client *, unsigned int data);/* a ioctl like command that can be used to perform specific functions* with the device.*/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;
};
struct i2c_driver
表示 I2C
控制器外接的 I2C
设备的驱动程序。
/*** struct i2c_client - represent an I2C slave device* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking* @addr: Address used on the I2C bus connected to the parent adapter.* @name: Indicates the type of the device, usually a chip name that's* generic enough to hide second-sourcing and compatible revisions.* @adapter: manages the bus segment hosting this I2C device* @dev: Driver model device node for the slave.* @irq: indicates the IRQ generated by this device (if any)* @detected: member of an i2c_driver.clients list or i2c-core's* userspace_devices list* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter* calls it to pass on slave events to the slave driver.** An i2c_client identifies a single device (i.e. chip) connected to an* i2c bus. The behaviour exposed to Linux is defined by the driver* managing the device.*/
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
};
struct i2c_client
结构体描述 I2C
控制器外接的 I2C
设备的相关硬件信息。
/*** struct i2c_msg - an I2C transaction segment beginning with START* @addr: Slave address, either seven or ten bits. When this is a ten* bit address, I2C_M_TEN must be set in @flags and the adapter* must support I2C_FUNC_10BIT_ADDR.* @flags: I2C_M_RD is handled by all adapters. No other flags may be* provided unless the adapter exported the relevant I2C_FUNC_** flags through i2c_check_functionality().* @len: Number of data bytes in @buf being read from or written to the* I2C slave address. For read transactions where I2C_M_RECV_LEN* is set, the caller guarantees that this buffer can hold up to* 32 bytes in addition to the initial length byte sent by the* slave (plus, if used, the SMBus PEC); and this value will be* incremented by the number of block data bytes received.* @buf: The buffer into which data is read, or from which it's written.** An i2c_msg is the low level representation of one segment of an I2C* transaction. It is visible to drivers in the @i2c_transfer() procedure,* to userspace from i2c-dev, and to I2C adapter drivers through the* @i2c_adapter.@master_xfer() method.** Except when I2C "protocol mangling" is used, all I2C adapters implement* the standard rules for I2C transactions. Each transaction begins with a* START. That is followed by the slave address, and a bit encoding read* versus write. Then follow all the data bytes, possibly including a byte* with SMBus PEC. The transfer terminates with a NAK, or when all those* bytes have been transferred and ACKed. If this is the last message in a* group, it is followed by a STOP. Otherwise it is followed by the next* @i2c_msg transaction segment, beginning with a (repeated) START.** Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then* passing certain @flags may have changed those standard protocol behaviors.* Those flags are only for use with broken/nonconforming slaves, and with* adapters which are known to support the specific mangling options they* need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).*/
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_msg
结构的变量,代表着一次单方向的传输。
I2C
驱动有 4
个重要的东西,I2C
总线、I2C
驱动、I2C
设备、I2C
控制器驱动
I2C
总线:维护着两个链表(I2C
驱动、I2C
设备),管理 I2C
设备和 I2C
驱动的匹配和删除等。I2C
驱动:对应的就是 I2C
设备的驱动程序。I2C
设备:是具体硬件设备的一个抽象。I2C
控制器驱动:用于 I2C
驱动和 I2C
设备间的通用,是 SOC
上 I2C
控制器驱动程序。I2C
相关 API
请参考 linux-4.1.15\include\linux\i2c.h
文件。
AP3216C
是由敦南科技推出的一款传感器,其支持环境光强度(ALS
)、接近距离(PS
)和红外线强度(IR
)这三个环境参数检测。该芯片可以通过 IIC
接口与主控制相连,并且支持中断。其他详细信息见 AP3216C
官方参考手册。
#include "linux/init.h"
#include "linux/module.h"
#include "linux/i2c.h"/** @description : i2c驱动的probe函数,当驱动与* 设备匹配以后此函数就会执行* @param - client : i2c设备* @param - id : i2c设备ID* @return : 0,成功;其他负值,失败*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret = 0;return ret;
}/** @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行* @param - client : i2c设备* @return : 0,成功;其他负值,失败*/
static int ap3216c_remove(struct i2c_client *client)
{int ret = 0;return ret;
}/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {{"lsc,ap3216c", 0}, {}
};/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "lsc,ap3216c" },{ /* Sentinel */ }
};/* i2c驱动结构体 */
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,
};/** @description : 驱动入口函数* @param : 无* @return : 无*/
static int __init ap3216c_init(void)
{int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit ap3216c_exit(void)
{i2c_del_driver(&ap3216c_driver);
}/* module_i2c_driver(ap3216c_driver) */module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"#define NEWCHRDEV_MINOR 255 /* 次设备号(让MISC自动分配) */
#define NEWCHRDEV_NAME "ap3216c" /* 名子 */typedef struct{
}newchrdev_t;newchrdev_t newchrdev;/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{printk("ap3216c_open!\r\n");filp->private_data = &newchrdev; /* 设置私有数据 */return 0;
}/** @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{printk("ap3216c_read!\r\n");return 0;
}/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{printk("ap3216c_release!\r\n");return 0;
}static const struct file_operations ap3216c_ops = {.owner = THIS_MODULE,.open = ap3216c_open,.read = ap3216c_read,.release = ap3216c_release,
};/* MISC设备结构体 */
static struct miscdevice ap3216c_miscdev = {.minor = NEWCHRDEV_MINOR,.name = NEWCHRDEV_NAME,.fops = &ap3216c_ops,
};/** @description : i2c驱动的probe函数,当驱动与* 设备匹配以后此函数就会执行* @param - client : i2c设备* @param - id : i2c设备ID* @return : 0,成功;其他负值,失败*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret = 0;ret = misc_register(&ap3216c_miscdev);if(ret < 0){printk("ap3216c misc device register failed!\r\n");ret = -EFAULT;}return ret;
}/** @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行* @param - client : i2c设备* @return : 0,成功;其他负值,失败*/
static int ap3216c_remove(struct i2c_client *client)
{int ret = 0;/* MISC 驱动框架卸载 */misc_deregister(&ap3216c_miscdev);return ret;
}/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {{"lsc,ap3216c", 0}, {}
};/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "lsc,ap3216c" },{ /* Sentinel */ }
};/* i2c驱动结构体 */
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,
};/** @description : 驱动入口函数* @param : 无* @return : 无*/
static int __init ap3216c_init(void)
{int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit ap3216c_exit(void)
{i2c_del_driver(&ap3216c_driver);
}/* module_i2c_driver(ap3216c_driver) */module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
1、初始化 ap3216c
设备。
2、获取 ap3216c
传感器相关数据。
参考:linux-4.1.15/Documentation\devicetree\bindings\i2c\i2c-imx.txt
。
1、必须属性
compatible
:驱动和设备匹配字符串。
reg
:I2C
寄存器地址和长度。
interrupts
:I2C
中断配置。
clocks
:I2C
时钟配置。
clock-frequency
:I2C
总线时钟频率,以 Hz
为单位。
dmas
:DMA
说明符。
dma-names
:必须是 rx-tx
。
1、必须属性
compatible
:驱动和设备匹配字符串。
reg
:I2C
设备地址。
通过原理图分析,AP3216C
连接在 I2C1
上,使用的接口为 UART4_TXD
和 UART4_RXD
。中断连接引脚为 GIO1_IO01
。
pinctrl_i2c1: i2c1grp {fsl,pins = ;
};
&i2c1 {clock-frequency = <100000>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_i2c1>;status = "okay";ap3216c@1e {compatible = "lsc,ap3216c";reg = <0x1e>;};
};
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ make dtbsCHK include/config/kernel.releaseCHK include/generated/uapi/linux/version.hCHK include/generated/utsrelease.h
make[1]: 'include/generated/mach-types.h' is up to date.CHK include/generated/bounds.hCHK include/generated/asm-offsets.hCALL scripts/checksyscalls.shDTC arch/arm/boot/dts/imx6ull-alientek-emmc.dtbDTC arch/arm/boot/dts/imx6ull-alientek-nand.dtb
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$
# pwd
/proc/device-tree/soc/aips-bus@02100000/i2c@021a0000
# ls
#address-cells clock-frequency interrupts pinctrl-names
#size-cells clocks name reg
ap3216c@1e compatible pinctrl-0 status
# cd ap3216c@1e/
# ls
compatible name reg
# cat compatible
lsc,ap3216c#
# cat name
ap3216c#
# pwd
/proc/device-tree/soc/aips-bus@02100000/i2c@021a0000/ap3216c@1e
#
1、Makefile
KERNELDIR := /home/onlylove/my/linux/linux-imx-4.1.15
CURRENT_PATH := $(shell pwd)
obj-m := ap3216c.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
2、ap3216c.c
#include "linux/init.h"
#include "linux/module.h"
#include "linux/printk.h"
#include "linux/i2c.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/delay.h"
#include "asm-generic/uaccess.h"#define AP3216C_ADDR 0X1E /* AP3216C器件地址 *//* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */
#define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */
#define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */
#define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */
#define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */#define NEWCHRDEV_MINOR 255 /* 次设备号(让MISC自动分配) */
#define NEWCHRDEV_NAME "ap3216c" /* 名子 */typedef struct{struct i2c_client *client; /* i2C设备 */unsigned short ir, als, ps; /* 传感器数据 */
}newchrdev_t;newchrdev_t newchrdev;/** @description : 从ap3216c读取多个寄存器数据* @param - dev: ap3216c设备* @param - reg: 要读取的寄存器首地址* @param - val: 读取到的数据* @param - len: 要读取的数据长度* @return : 操作结果*/
static int ap3216c_read_regs(newchrdev_t *dev, unsigned char reg, void *val, int len)
{int ret;struct i2c_msg msg[2];struct i2c_client *client = dev->client;/* msg[0]为发送要读取的首地址 */msg[0].addr = client->addr; /* ap3216c地址 */msg[0].flags = 0; /* 标记为发送数据 */msg[0].buf = ® /* 读取的首地址 */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;
}/** @description : 向ap3216c多个寄存器写入数据* @param - dev: ap3216c设备* @param - reg: 要写入的寄存器首地址* @param - val: 要写入的数据缓冲区* @param - len: 要写入的数据长度* @return : 操作结果*/
static int ap3216c_write_regs(newchrdev_t *dev, unsigned char reg, unsigned char *buf, unsigned char len)
{unsigned char b[256];struct i2c_msg msg;struct i2c_client *client = dev->client;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);
}/** @description : 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!* : 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms* @param - ir : ir数据* @param - ps : ps数据* @param - ps : als数据 * @return : 无。*/
void ap3216c_read_data(newchrdev_t *dev)
{unsigned char i =0;unsigned char buf[6];/* 循环读取所有传感器数据 */for(i = 0; i < 6; i++) {ap3216c_read_regs(dev, AP3216C_IRDATALOW + i, buf+i, 1);}if(buf[0] & 0X80) /* IR_OF位为1,则数据无效 */dev->ir = 0; else /* 读取IR传感器的数据 */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 /* 读取PS传感器的数据 */dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}static int lcs_ap3216c_init(newchrdev_t *dev)
{unsigned char buf = 0;/* 1、复位ap3216c */buf = 0x04;ap3216c_write_regs(dev, AP3216C_SYSTEMCONG, &buf, 1);/* 2、至少延时10ms */mdelay(50);/* 3、开启ALS、PS+IR */buf = 0x03;ap3216c_write_regs(dev, AP3216C_SYSTEMCONG, &buf, 1);return 0;
}/** @description : 打开设备* @param - inode : 传递给驱动的inode* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* @return : 0 成功;其他 失败*/
static int ap3216c_open(struct inode *inode, struct file *filp)
{/* 1、设置私有数据 */filp->private_data = &newchrdev;/* 2、初始化ap3216c */lcs_ap3216c_init(&newchrdev);return 0;
}/** @description : 从设备读取数据 * @param - filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param - offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{short data[3];long err = 0;newchrdev_t *dev = (newchrdev_t*)filp->private_data;ap3216c_read_data(dev);data[0] = dev->ir;data[1] = dev->als;data[2] = dev->ps;err = copy_to_user(buf, data, sizeof(data));return 0;
}/** @description : 关闭/释放设备* @param - filp : 要关闭的设备文件(文件描述符)* @return : 0 成功;其他 失败*/
static int ap3216c_release(struct inode *inode, struct file *filp)
{return 0;
}static const struct file_operations ap3216c_ops = {.owner = THIS_MODULE,.open = ap3216c_open,.read = ap3216c_read,.release = ap3216c_release,
};/* MISC设备结构体 */
static struct miscdevice ap3216c_miscdev = {.minor = NEWCHRDEV_MINOR,.name = NEWCHRDEV_NAME,.fops = &ap3216c_ops,
};/** @description : i2c驱动的probe函数,当驱动与* 设备匹配以后此函数就会执行* @param - client : i2c设备* @param - id : i2c设备ID* @return : 0,成功;其他负值,失败*/
static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{int ret = 0;/* 1、注册 MISC 子系统 */ret = misc_register(&ap3216c_miscdev);if(ret < 0){printk("ap3216c misc device register failed!\r\n");ret = -EFAULT;}/* 2、提取 i2c 设备 */newchrdev.client = client;return ret;
}/** @description : i2c驱动的remove函数,移除i2c驱动的时候此函数会执行* @param - client : i2c设备* @return : 0,成功;其他负值,失败*/
static int ap3216c_remove(struct i2c_client *client)
{int ret = 0;/* MISC 驱动框架卸载 */misc_deregister(&ap3216c_miscdev);return ret;
}/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {{"lsc,ap3216c", 0}, {}
};/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "lsc,ap3216c" },{ /* Sentinel */ }
};/* i2c驱动结构体 */
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,
};/** @description : 驱动入口函数* @param : 无* @return : 无*/
static int __init ap3216c_init(void)
{int ret = 0;ret = i2c_add_driver(&ap3216c_driver);return ret;
}/** @description : 驱动出口函数* @param : 无* @return : 无*/
static void __exit ap3216c_exit(void)
{i2c_del_driver(&ap3216c_driver);
}/* module_i2c_driver(ap3216c_driver) */module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
#include
#include
#include
#include "stdio.h"
#include
#include
#include int main(int argc, char *argv[])
{int fd = 0;int err = 0;unsigned short databuf[3];unsigned short ir, als, ps;if(argc != 2){printf("Error Usage!\r\n");return -1;}fd = open(argv[1],O_RDWR);if(fd < 0){printf("Can't open file %s\r\n", argv[1]);return -1;}while(1){err = read(fd, databuf, sizeof(databuf));if(err == 0){ir = databuf[0];als = databuf[1];ps = databuf[2];printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);}sleep(1);}close(fd);
// return 0;
}
# pwd
/root
# ls
ap3216c.ko ap3216c_app
# ls -l /dev/ap3216c
ls: /dev/ap3216c: No such file or directory
# insmod ap3216c.ko
# ls -l /dev/ap3216c
crw-rw---- 1 root root 10, 56 Jan 1 06:43 /dev/ap3216c
# rmmod ap3216c.ko
# ls -l /dev/ap3216c
ls: /dev/ap3216c: No such file or directory
# insmod ap3216crandom: nonblocking pool is initialized
# insmod ap3216c.ko
# ls -l /dev/ap3216c
crw-rw---- 1 root root 10, 56 Jan 1 06:43 /dev/ap3216c
#
# ./ap3216c_app /dev/ap3216c
ir = 0, als = 0, ps = 0
ir = 6, als = 281, ps = 823
ir = 0, als = 277, ps = 883
ir = 6, als = 0, ps = 1023
ir = 16, als = 0, ps = 1023
ir = 0, als = 0, ps = 1023
ir = 0, als = 0, ps = 1023
ir = 0, als = 284, ps = 856
ir = 12, als = 386, ps = 831
ir = 30, als = 392, ps = 827
ir = 9, als = 388, ps = 828
ir = 0, als = 363, ps = 875
ir = 28, als = 384, ps = 881
ir = 0, als = 381, ps = 848
^C
#