1、使用的原因
- Linux下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 设备的本质都是一样的,通过I2C/SPI接口读写芯片内部寄存器。
- 在前面学习 I2C 和SPI 驱动的时候,针对 I2C 和 SPI 设备寄存器的操作都是通过相关的 API函数进行操作的。这样 Linux 内核中就会充斥着大量的重复、冗余代码,但是这些本质上都是对寄存器的操作,所以为了方便内核开发人员统一访问 I2C/SPI 设备的时候,Linux下使用i2c_transfer来读写I2C设备中的寄存器。SPI接口的话使用 spi_write/spi_read读写SPI设备中的寄存器。因为驱动属于内核的一部分,导致 Linux 内核里面就会充斥了大量的 i2c_transfer 这类的冗余代码,而且由于iic驱动的和spi各自写各自的驱动文件,导致代码的复用性也会降低。
- 为此引入了 Regmap子系统
2、Regmap简介
-
基于代码复用的原则,Linux内核引入了regmap 模型. -
regmap将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用SPI或者 I2C接口 API函数,统一使用 regmap API函数,优点就是上面对应的缺点 -
regmap模型的重点在于: @通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用regmap接口函数来访问 @regmap 是Linux 内核为了减少慢速 I/O 在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。 @regmap在驱动和硬件之间添加了 cache,降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低 -
使用regmap的情况: @)硬件寄存器操作,比如选用通过 I2C/SPI 接口来读写设备的内部寄存器,或者需要读写 SOC 内部的硬件寄存器 @)提高代码复用性和驱动一致性,简化驱动开发过程。 @)减少底层I/O 操作次数,提高访问效率
3、Regmap驱动框架
struct regmap {
union {
struct mutex mutex;
struct {
spinlock_t spinlock;
unsigned long spinlock_flags;
};
};
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
struct device *dev;
void *work_buf;
struct regmap_format format;
const struct regmap_bus *bus;
void *bus_context;
const char *name;
bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;
...
unsigned int max_register;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
...
struct rb_root range_tree;
void *selector_work_buf;
};
struct regmap_config {
const char *name;
int reg_bits;
int reg_stride;
int pad_bits;
int val_bits;
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
int (*reg_read)(void *context, unsigned int reg, unsigned int val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
bool fast_io;
unsigned int max_register;
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
u8 read_flag_mask;
u8 write_flag_mask;
bool use_single_rw;
bool can_multi_write;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
unsigned int num_ranges;
};
struct regmap * regmap_init_spi(struct spi_device *spi, const struct regmap_config *config)
struct regmap * regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config)
void regmap_exit(struct regmap *map)
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
int regmap_write(struct regmap *map, unsigned int reg, unsigned int *val)
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count)
int regmap_bulk_write(struct regmap *map, unsigned int reg, void *val, size_t val_count)
int regmap_update_bits (struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val)
4、实战演练1 对象: imx6ull开发板,传感器ICM20608,总线SPI
#ifndef ICM20608_H
#define ICM20608_H
#define ICM20608G_ID 0XAF
#define ICM20608D_ID 0XAE
#define ICM20_SELF_TEST_X_GYRO 0x00
#define ICM20_SELF_TEST_Y_GYRO 0x01
#define ICM20_SELF_TEST_Z_GYRO 0x02
#define ICM20_SELF_TEST_X_ACCEL 0x0D
#define ICM20_SELF_TEST_Y_ACCEL 0x0E
#define ICM20_SELF_TEST_Z_ACCEL 0x0F
#define ICM20_XG_OFFS_USRH 0x13
#define ICM20_XG_OFFS_USRL 0x14
#define ICM20_YG_OFFS_USRH 0x15
#define ICM20_YG_OFFS_USRL 0x16
#define ICM20_ZG_OFFS_USRH 0x17
#define ICM20_ZG_OFFS_USRL 0x18
#define ICM20_SMPLRT_DIV 0x19
#define ICM20_CONFIG 0x1A
#define ICM20_GYRO_CONFIG 0x1B
#define ICM20_ACCEL_CONFIG 0x1C
#define ICM20_ACCEL_CONFIG2 0x1D
#define ICM20_LP_MODE_CFG 0x1E
#define ICM20_ACCEL_WOM_THR 0x1F
#define ICM20_FIFO_EN 0x23
#define ICM20_FSYNC_INT 0x36
#define ICM20_INT_PIN_CFG 0x37
#define ICM20_INT_ENABLE 0x38
#define ICM20_INT_STATUS 0x3A
#define ICM20_ACCEL_XOUT_H 0x3B
#define ICM20_ACCEL_XOUT_L 0x3C
#define ICM20_ACCEL_YOUT_H 0x3D
#define ICM20_ACCEL_YOUT_L 0x3E
#define ICM20_ACCEL_ZOUT_H 0x3F
#define ICM20_ACCEL_ZOUT_L 0x40
#define ICM20_TEMP_OUT_H 0x41
#define ICM20_TEMP_OUT_L 0x42
#define ICM20_GYRO_XOUT_H 0x43
#define ICM20_GYRO_XOUT_L 0x44
#define ICM20_GYRO_YOUT_H 0x45
#define ICM20_GYRO_YOUT_L 0x46
#define ICM20_GYRO_ZOUT_H 0x47
#define ICM20_GYRO_ZOUT_L 0x48
#define ICM20_SIGNAL_PATH_RESET 0x68
#define ICM20_ACCEL_INTEL_CTRL 0x69
#define ICM20_USER_CTRL 0x6A
#define ICM20_PWR_MGMT_1 0x6B
#define ICM20_PWR_MGMT_2 0x6C
#define ICM20_FIFO_COUNTH 0x72
#define ICM20_FIFO_COUNTL 0x73
#define ICM20_FIFO_R_W 0x74
#define ICM20_WHO_AM_I 0x75
#define ICM20_XA_OFFSET_H 0x77
#define ICM20_XA_OFFSET_L 0x78
#define ICM20_YA_OFFSET_H 0x7A
#define ICM20_YA_OFFSET_L 0x7B
#define ICM20_ZA_OFFSET_H 0x7D
#define ICM20_ZA_OFFSET_L 0x7E
#endif
#include <linux/spi/spi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include "icm20608reg.h"
#include <linux/gpio.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/regmap.h>
#define ICM20608_CNT 1
#define ICM20608_NAME "icm20608"
struct icm20608_dev {
struct spi_device *spi;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
signed int gyro_x_adc;
signed int gyro_y_adc;
signed int gyro_z_adc;
signed int accel_x_adc;
signed int accel_y_adc;
signed int accel_z_adc;
signed int temp_adc;
struct regmap *regmap;
struct regmap_config regmap_config;
};
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{
u8 ret;
unsigned int data;
ret = regmap_read(dev->regmap, reg, &data);
return (u8)data;
}
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{
regmap_write(dev->regmap, reg, value);
}
void icm20608_readdata(struct icm20608_dev *dev)
{
u8 ret;
unsigned char data[14];
ret = regmap_bulk_read(dev->regmap, ICM20_ACCEL_XOUT_H, data, 14);
dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);
dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);
dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);
dev->temp_adc = (signed short)((data[6] << 8) | data[7]);
dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}
static int icm20608_open(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
signed int data[7];
long err = 0;
struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
struct icm20608_dev *dev = container_of(cdev, struct icm20608_dev, cdev);
icm20608_readdata(dev);
data[0] = dev->gyro_x_adc;
data[1] = dev->gyro_y_adc;
data[2] = dev->gyro_z_adc;
data[3] = dev->accel_x_adc;
data[4] = dev->accel_y_adc;
data[5] = dev->accel_z_adc;
data[6] = dev->temp_adc;
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
static int icm20608_release(struct inode *inode, struct file *filp)
{
return 0;
}
static const struct file_operations icm20608_ops = {
.owner = THIS_MODULE,
.open = icm20608_open,
.read = icm20608_read,
.release = icm20608_release,
};
void icm20608_reginit(struct icm20608_dev *dev)
{
u8 value = 0;
icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x80);
mdelay(50);
icm20608_write_onereg(dev, ICM20_PWR_MGMT_1, 0x01);
mdelay(50);
value = icm20608_read_onereg(dev, ICM20_WHO_AM_I);
printk("ICM20608 ID = %#X\r\n", value);
icm20608_write_onereg(dev, ICM20_SMPLRT_DIV, 0x00);
icm20608_write_onereg(dev, ICM20_GYRO_CONFIG, 0x18);
icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG, 0x18);
icm20608_write_onereg(dev, ICM20_CONFIG, 0x04);
icm20608_write_onereg(dev, ICM20_ACCEL_CONFIG2, 0x04);
icm20608_write_onereg(dev, ICM20_PWR_MGMT_2, 0x00);
icm20608_write_onereg(dev, ICM20_LP_MODE_CFG, 0x00);
icm20608_write_onereg(dev, ICM20_FIFO_EN, 0x00);
}
static int icm20608_probe(struct spi_device *spi)
{
int ret;
struct icm20608_dev *icm20608dev;
icm20608dev = devm_kzalloc(&spi->dev, sizeof(*icm20608dev), GFP_KERNEL);
if(!icm20608dev)
return -ENOMEM;
icm20608dev->regmap_config.reg_bits = 8;
icm20608dev->regmap_config.val_bits = 8;
icm20608dev->regmap_config.read_flag_mask = 0x80;
icm20608dev->regmap = regmap_init_spi(spi, &icm20608dev->regmap_config);
if (IS_ERR(icm20608dev->regmap)) {
return PTR_ERR(icm20608dev->regmap);
}
ret = alloc_chrdev_region(&icm20608dev->devid, 0, ICM20608_CNT, ICM20608_NAME);
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", ICM20608_NAME, ret);
goto del_regmap;
}
icm20608dev->cdev.owner = THIS_MODULE;
cdev_init(&icm20608dev->cdev, &icm20608_ops);
ret = cdev_add(&icm20608dev->cdev, icm20608dev->devid, ICM20608_CNT);
if(ret < 0) {
goto del_unregister;
}
icm20608dev->class = class_create(THIS_MODULE, ICM20608_NAME);
if (IS_ERR(icm20608dev->class)) {
goto del_cdev;
}
icm20608dev->device = device_create(icm20608dev->class, NULL, icm20608dev->devid, NULL, ICM20608_NAME);
if (IS_ERR(icm20608dev->device)) {
goto destroy_class;
}
icm20608dev->spi = spi;
spi->mode = SPI_MODE_0;
spi_setup(spi);
icm20608_reginit(icm20608dev);
spi_set_drvdata(spi, icm20608dev);
return 0;
destroy_class:
device_destroy(icm20608dev->class, icm20608dev->devid);
del_cdev:
cdev_del(&icm20608dev->cdev);
del_unregister:
unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
del_regmap:
regmap_exit(icm20608dev->regmap);
return -EIO;
}
static int icm20608_remove(struct spi_device *spi)
{
struct icm20608_dev *icm20608dev = spi_get_drvdata(spi);
cdev_del(&icm20608dev->cdev);
unregister_chrdev_region(icm20608dev->devid, ICM20608_CNT);
device_destroy(icm20608dev->class, icm20608dev->devid);
class_destroy(icm20608dev->class);
regmap_exit(icm20608dev->regmap);
return 0;
}
static const struct spi_device_id icm20608_id[] = {
{"alientek,icm20608", 0},
{}
};
static const struct of_device_id icm20608_of_match[] = {
{ .compatible = "alientek,icm20608" },
{ }
};
static struct spi_driver icm20608_driver = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.owner = THIS_MODULE,
.name = "icm20608",
.of_match_table = icm20608_of_match,
},
.id_table = icm20608_id,
};
static int __init icm20608_init(void)
{
return spi_register_driver(&icm20608_driver);
}
static void __exit icm20608_exit(void)
{
spi_unregister_driver(&icm20608_driver);
}
module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char *filename;
signed int databuf[7];
unsigned char data[14];
signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;
signed int accel_x_adc, accel_y_adc, accel_z_adc;
signed int temp_adc;
float gyro_x_act, gyro_y_act, gyro_z_act;
float accel_x_act, accel_y_act, accel_z_act;
float temp_act;
int ret = 0;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0) {
printf("can't open file %s\r\n", filename);
return -1;
}
while (1) {
ret = read(fd, databuf, sizeof(databuf));
if(ret == 0) {
gyro_x_adc = databuf[0];
gyro_y_adc = databuf[1];
gyro_z_adc = databuf[2];
accel_x_adc = databuf[3];
accel_y_adc = databuf[4];
accel_z_adc = databuf[5];
temp_adc = databuf[6];
gyro_x_act = (float)(gyro_x_adc) / 16.4;
gyro_y_act = (float)(gyro_y_adc) / 16.4;
gyro_z_act = (float)(gyro_z_adc) / 16.4;
accel_x_act = (float)(accel_x_adc) / 2048;
accel_y_act = (float)(accel_y_adc) / 2048;
accel_z_act = (float)(accel_z_adc) / 2048;
temp_act = ((float)(temp_adc) - 25 ) / 326.8 + 25;
printf("\r\n原始值:\r\n");
printf("gx = %d, gy = %d, gz = %d\r\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);
printf("ax = %d, ay = %d, az = %d\r\n", accel_x_adc, accel_y_adc, accel_z_adc);
printf("temp = %d\r\n", temp_adc);
printf("实际值:");
printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\r\n", gyro_x_act, gyro_y_act, gyro_z_act);
printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\r\n", accel_x_act, accel_y_act, accel_z_act);
printf("act temp = %.2f°C\r\n", temp_act);
}
usleep(100000);
}
close(fd);
return 0;
}
5、实战演练2(iic)
对象: imx6ull开发板,ap3216c传感器、IIC总线
#ifndef AP3216C_H
#define AP3216C_H
#define AP3216C_ADDR 0X1E
#define AP3216C_SYSTEMCONG 0x00
#define AP3216C_INTSTATUS 0X01
#define AP3216C_INTCLEAR 0X02
#define AP3216C_IRDATALOW 0x0A
#define AP3216C_IRDATAHIGH 0x0B
#define AP3216C_ALSDATALOW 0x0C
#define AP3216C_ALSDATAHIGH 0X0D
#define AP3216C_PSDATALOW 0X0E
#define AP3216C_PSDATAHIGH 0X0F
#endif
#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"
#include <linux/regmap.h>
#define AP3216C_CNT 1
#define AP3216C_NAME "ap3216c"
struct ap3216c_dev {
struct i2c_client *client;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *nd;
unsigned short ir, als, ps;
struct regmap *regmap;
struct regmap_config regmap_config;
};
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
u8 ret;
unsigned int data;
ret = regmap_read(dev->regmap, reg, &data);
return (u8)data;
}
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
regmap_write(dev->regmap, reg, data);
}
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];
if(buf[4] & 0x40)
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)
{
struct cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
struct ap3216c_dev *ap3216cdev = container_of(cdev, struct ap3216c_dev, cdev);
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 cdev *cdev = filp->f_path.dentry->d_inode->i_cdev;
struct ap3216c_dev *dev = container_of(cdev, struct ap3216c_dev, cdev);
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 const struct file_operations ap3216c_ops = {
.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)
{
int ret;
struct ap3216c_dev *ap3216cdev;
ap3216cdev = devm_kzalloc(&client->dev, sizeof(*ap3216cdev), GFP_KERNEL);
if(!ap3216cdev)
return -ENOMEM;
ap3216cdev->regmap_config.reg_bits = 8;
ap3216cdev->regmap_config.val_bits = 8;
ap3216cdev->regmap = regmap_init_i2c(client, &ap3216cdev->regmap_config);
if (IS_ERR(ap3216cdev->regmap)) {
return PTR_ERR(ap3216cdev->regmap);
}
ret = alloc_chrdev_region(&ap3216cdev->devid, 0, AP3216C_CNT, AP3216C_NAME);
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", AP3216C_NAME, ret);
goto del_regmap;
}
ap3216cdev->cdev.owner = THIS_MODULE;
cdev_init(&ap3216cdev->cdev, &ap3216c_ops);
ret = cdev_add(&ap3216cdev->cdev, ap3216cdev->devid, AP3216C_CNT);
if(ret < 0) {
goto del_unregister;
}
ap3216cdev->class = class_create(THIS_MODULE, AP3216C_NAME);
if (IS_ERR(ap3216cdev->class)) {
goto del_cdev;
}
ap3216cdev->device = device_create(ap3216cdev->class, NULL, ap3216cdev->devid, NULL, AP3216C_NAME);
if (IS_ERR(ap3216cdev->device)) {
goto destroy_class;
}
ap3216cdev->client = client;
i2c_set_clientdata(client,ap3216cdev);
return 0;
destroy_class:
device_destroy(ap3216cdev->class, ap3216cdev->devid);
del_cdev:
cdev_del(&ap3216cdev->cdev);
del_unregister:
unregister_chrdev_region(ap3216cdev->devid, AP3216C_CNT);
del_regmap:
regmap_exit(ap3216cdev->regmap);
return -EIO;
}
static int ap3216c_remove(struct i2c_client *client)
{
struct ap3216c_dev *ap3216cdev = i2c_get_clientdata(client);
cdev_del(&ap3216cdev->cdev);
unregister_chrdev_region(ap3216cdev->devid, AP3216C_CNT);
device_destroy(ap3216cdev->class, ap3216cdev->devid);
class_destroy(ap3216cdev->class);
regmap_exit(ap3216cdev->regmap);
return 0;
}
static const struct i2c_device_id ap3216c_id[] = {
{"alientek,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 __init ap3216c_init(void)
{
int ret = 0;
ret = i2c_add_driver(&ap3216c_driver);
return ret;
}
static void __exit ap3216c_exit(void)
{
i2c_del_driver(&ap3216c_driver);
}
module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include <poll.h>
#include <sys/select.h>
#include <sys/time.h>
#include <signal.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char *filename;
unsigned short databuf[3];
unsigned short ir, als, ps;
int ret = 0;
if (argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0) {
printf("can't open file %s\r\n", filename);
return -1;
}
while (1) {
ret = read(fd, databuf, sizeof(databuf));
if(ret == 0) {
ir = databuf[0];
als = databuf[1];
ps = databuf[2];
printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps);
}
usleep(200000);
}
close(fd);
return 0;
}
|