【linux iic子系统】i2c驱动示例——tmp75温度传感器驱动代码(七)

9 篇文章 5 订阅

I2C驱动要完成哪些事?

想要编写i2c驱动,就必须先弄清楚i2c驱动是干啥的。i2c驱动对上需要提供给用户操作对象,用户才能通过这个对象最终访问到i2c设备;对下需要能正确操作i2c设备,也就是实现i2c设备的读写操作;对中间能对接i2c核心提供的接口,实现驱动与设备关联,也方便系统统一管理。

综上,i2c驱动需要干以下三件事:

  1. 使用i2c核心提供的接口将驱动注册到系统,并实现驱动与设备匹配成功所需要执行的probe函数,以及设备或驱动卸载时执行的remove函数。
  2. 注册字符驱动,创建设备节点,让用户空间有访问i2c设备的对象。
  3. 实现i2c设备的操作方法集(file_operatin),按i2c设备手册要求,完成读写操作。

i2c设备代码(简要)

……
static struct i2c_board_info my_tmp75_info = {
	I2C_BOARD_INFO("my_tmp75", 0x48),
};        //设备信息描述结构体,用于填充 i2c_client结构体

static struct i2c_client *my_tmp75_client;        //tmp75 i2c设备描述结构体

static int my_tmp75_init(void)
{
	struct i2c_adapter *i2c_adapt;

	i2c_adapt = i2c_get_adapter(6);       //获取总线6(i2c控制器6)描述结构体,获取哪个总线取决i2c设备挂在哪个i2c控制器上

	if (i2c_adapt == NULL)
	{
		printk("get adapter fail!\n");
		goto get_adapter_fail;
	}

	my_tmp75_client = i2c_new_device(i2c_adapt, &my_tmp75_info);        //分配并注册i2c设备

	if (my_tmp75_client == NULL)
	{
		printk("i2c new fail!\n");
		goto i2c_new_fail;
	}

	i2c_put_adapter(i2c_adapt);

	return 0;

i2c_new_fail:
	i2c_put_adapter(i2c_adapt);

get_adapter_fail:
	return -1;
}
……

i2c驱动代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>

#define MY_I2C_MAGIC_NUMBER 'k'
#define GET_TEMP _IO(MY_I2C_MAGIC_NUMBER, 0)
#define GET_CONFIG _IO(MY_I2C_MAGIC_NUMBER, 1)
#define GET_TLOW_REG _IO(MY_I2C_MAGIC_NUMBER, 2)
#define GET_THIGH_REG _IO(MY_I2C_MAGIC_NUMBER, 3)

#define SET_CONFIG _IO(MY_I2C_MAGIC_NUMBER, 4)
#define SET_TLOW_REG _IO(MY_I2C_MAGIC_NUMBER, 5)
#define SET_THIGH_REG _IO(MY_I2C_MAGIC_NUMBER, 6)

#define TMP_REG_ADDR 0x00
#define TMP_CFIG_ADDR 0x01
#define TMP_TLOW_ADDR 0x02
#define TMP_THIGH_ADDR 0x03

#define DEV_NAME "my_i2c"
#define NAME_SIZE 32
#define NINOR_MAX_NUM 4095
#define DEV_ADD_NUM 1

static int my_debug = 1;
module_param(my_debug, int, 0644);
MODULE_PARM_DESC(my_debug, "enable debugging informationg");

#define debug_printk(ftm, args...) if(my_debug){printk(ftm, ##args);}

unsigned char TMP_ADDR[] = {TMP_REG_ADDR, TMP_CFIG_ADDR, \
							TMP_TLOW_ADDR, TMP_THIGH_ADDR, \
							TMP_CFIG_ADDR, TMP_TLOW_ADDR, TMP_THIGH_ADDR};

struct my_tmp75_device
{
	struct i2c_client *client;
	struct cdev tmp75_cdev;
	struct mutex my_mutex;
	char name[NAME_SIZE];
};

struct tmp75_i2c_drv {
	dev_t dev;
	struct class *my_i2c_class;
	unsigned int dev_count;
	unsigned int minor_count;
};

static struct tmp75_i2c_drv my_i2c_drv = {
	.dev = 0,
	.my_i2c_class = NULL,
	.dev_count = 0,
	.minor_count = 0,
};

static int my_tmp75_open(struct inode *inode, struct file *filp)
{
	struct my_tmp75_device *tmp75_device;
	tmp75_device = container_of(inode->i_cdev, struct my_tmp75_device, tmp75_cdev);

	filp->private_data = tmp75_device;

	return 0;
}

static long tmp_read_data(struct i2c_client *client, unsigned char reg_addr)
{
	long ret = 0;
	unsigned char buf[2];
	struct i2c_msg msg[2];

	//判断通信方法是否存在,存在则调用
	if (client->adapter->algo->master_xfer != NULL)
	{
		msg[0].addr = client->addr;
		msg[0].buf = &reg_addr;
		msg[0].len = sizeof(reg_addr);
		msg[0].flags = 0;				//写标志
		
		msg[1].addr = client->addr;
		msg[1].buf = buf;
		msg[1].len = sizeof(buf);
		msg[1].flags = I2C_M_RD;		//读标志

		ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
		if (ARRAY_SIZE(msg) == ret)
		{
			ret = buf[1];
			ret = (ret << 8) | buf[0];
		}
	}
	else if (client->adapter->algo->smbus_xfer != NULL)
	{
		ret = i2c_smbus_read_word_data(client, reg_addr);
	}
	else 
	{
		ret = -EOPNOTSUPP;
	}

	return ret;
}

static long tmp_write_data(struct i2c_client *client, unsigned char reg_addr, unsigned long data)
{
	long ret = 0;
	struct i2c_msg msg;
	char msg_count = 1;
	unsigned char buf[3];

	//判断通信方法是否存在,存在则调用
	if (client->adapter->algo->master_xfer != NULL)
	{
		buf[0] = reg_addr;
		buf[1] = data & 0xff;
		buf[2] = (data >> 8) & 0xff;

		debug_printk("[0]=%x [1]=%x [2]=%c \n", buf[0], buf[1], buf[2]);

		msg.addr = client->addr;
		msg.buf = buf;
		msg.len = sizeof(buf);
		msg.flags = 0;			//写标志

		ret = i2c_transfer(client->adapter, &msg, msg_count);
		if (msg_count ==ret)
		{
			ret = 0;
		}
	}
	else if (client->adapter->algo->smbus_xfer != NULL)
	{
		ret = i2c_smbus_write_word_data(client, reg_addr, data);
	}
	else 
	{
		ret = -EOPNOTSUPP;
	}

	return ret;
}

static long my_tmp75_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	long ret = 0;
	unsigned long data;
	struct my_tmp75_device *tmp75_device;
	tmp75_device = filp->private_data;

	if ((_IOC_TYPE(cmd) != MY_I2C_MAGIC_NUMBER))
	{
		printk("%s:pid = %d, command type [%c] error!\n", __func__, current->pid, _IOC_TYPE(cmd));
		return -EINVAL;
	}

	//加锁,防止多线程操作
	mutex_lock(&tmp75_device->my_mutex);
	switch (_IOC_NR(cmd))
	{
		case _IOC_NR(GET_TEMP):
		case _IOC_NR(GET_CONFIG):
		case _IOC_NR(GET_TLOW_REG):
		case _IOC_NR(GET_THIGH_REG):
		{
			ret = tmp_read_data(tmp75_device->client, TMP_ADDR[_IOC_NR(cmd)]);
			if (ret > 0)
			{
				data = (unsigned long)ret;
				ret = copy_to_user((void *)arg, &data, sizeof(data));
			}
			break;
		}
		case _IOC_NR(SET_CONFIG):
		case _IOC_NR(SET_TLOW_REG):
		case _IOC_NR(SET_THIGH_REG):
		{
			ret = copy_from_user(&data, (void *)arg, sizeof(data));
			if (0 == ret)
			{
				ret = tmp_write_data(tmp75_device->client, TMP_ADDR[_IOC_NR(cmd)], data);
			}
			break;
		}
		default:
		{
			printk("%s:pid = %d, command error!\n", __func__, current->pid);
			ret = -EINVAL;
			break;
		}
	}	
	mutex_unlock(&tmp75_device->my_mutex);

	if (ret < 0)
	{
		printk("read/write failed!\n");
	}

	return ret;
}

//i2c设备的操作方法集
static const struct file_operations my_tmp75_fops = {
	.owner = THIS_MODULE,
	.open = my_tmp75_open,
	.unlocked_ioctl = my_tmp75_ioctl,
};

//驱动与设备的匹配列表
static const struct i2c_device_id my_tmp75_ids[] = {
		{.name = "my_tmp75", 0},
		{.name = "my_tmp175", 1},
		{ },
};
MODULE_DEVICE_TABLE(i2c, my_tmp75_ids);

static int my_tmp75_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	struct my_tmp75_device *tmp75_dev;
	struct device *my_i2c_class_dev;
	dev_t tmep_dev;
	int ret = 0;

	//devm_kzalloc分配的托管内存,当设备或驱动卸载时自动释放
	tmp75_dev = devm_kzalloc(&client->dev, sizeof(struct my_tmp75_device), GFP_KERNEL);
	if (NULL == tmp75_dev)
	{
		printk("devm_kzalloc fail!\n");
		return -ENOMEM;
	}

	tmp75_dev->client = client;
	mutex_init(&(tmp75_dev->my_mutex));

	//设置设备节点名称,根据设备名称、总线号、设备地址来设置,不会相同
	sprintf(tmp75_dev->name, "%s-%d-0x%x", client->name, client->adapter->nr, client->addr);
	i2c_set_clientdata(client, tmp75_dev);//设置tmp75_dev为client中dev的私有数据

	//如果是第一次注册字符驱动,则自动分配设备号,否则在原来主设备号基础上增加新设备
	if (my_i2c_drv.dev)//如果设备号为空,表示第一次注册
	{
		tmep_dev = MKDEV(MAJOR(my_i2c_drv.dev), MINOR(my_i2c_drv.dev) + my_i2c_drv.minor_count);
		ret = register_chrdev_region(tmep_dev, DEV_ADD_NUM, tmp75_dev->name);
	}
	else
	{
		ret = alloc_chrdev_region(&my_i2c_drv.dev, 0, DEV_ADD_NUM, tmp75_dev->name);
		tmep_dev = my_i2c_drv.dev;
	}
	
	if (ret)//字符驱动注册失败
	{
		printk("failed to char device region,minor_count = %d\n", my_i2c_drv.minor_count);
		goto char_region_fail;
	}

	cdev_init(&tmp75_dev->tmp75_cdev, &my_tmp75_fops);
	ret = cdev_add(&tmp75_dev->tmp75_cdev, tmep_dev, DEV_ADD_NUM);
	if (ret)
	{
		printk("cdev_add failed\n");
		goto cdev_add_fail;
	}

	if (NULL == my_i2c_drv.my_i2c_class)//如果设备类指针为空,表示第一次注册
	{
		my_i2c_drv.my_i2c_class = class_create(THIS_MODULE, DEV_NAME);
		if (IS_ERR(my_i2c_drv.my_i2c_class))
		{
			ret = PTR_ERR(my_i2c_drv.my_i2c_class);
			printk("class_create failed\n");
			goto class_create_fail;
		}
	}

	//每一次设备与驱动匹配成功,都创建设备节点
	my_i2c_class_dev = device_create(my_i2c_drv.my_i2c_class, \
						NULL, tmep_dev, NULL, tmp75_dev->name);
	if (IS_ERR(my_i2c_class_dev))
	{
		ret = PTR_ERR(my_i2c_class_dev);
		printk("device_create failed\n");
		goto device_create_fail;
	}
	
	my_i2c_drv.dev_count++;//记录当前存活设备

	//记录次设备号,用于下一次注册设备,最多不超过4095个次设备
	//my_i2c_drv.minor_count = (++my_i2c_drv.minor_count) & NINOR_MAX_NUM;
	my_i2c_drv.minor_count = (my_i2c_drv.minor_count + 1) & NINOR_MAX_NUM;

	debug_printk("register %s successed! dev_count = %d,minor_count = %d\n", \
		tmp75_dev->name, my_i2c_drv.dev_count, my_i2c_drv.minor_count);
	
	return 0;
	
device_create_fail:
	if (!my_i2c_drv.dev_count)
	{
		class_destroy(my_i2c_drv.my_i2c_class);
	}
	my_i2c_drv.my_i2c_class = NULL;
class_create_fail:
	cdev_del(&tmp75_dev->tmp75_cdev);	
cdev_add_fail:
	unregister_chrdev_region(tmep_dev, DEV_ADD_NUM);	
char_region_fail:
	devm_kfree(&client->dev, tmp75_dev);
	tmp75_dev = NULL;

	return ret;
}

static int my_tmp75_remove(struct i2c_client *client)
{
	struct my_tmp75_device *tmp75_dev;

	tmp75_dev = i2c_get_clientdata(client);

	my_i2c_drv.dev_count--;//每卸载一个设备,则设备存活计数减1

	device_destroy(my_i2c_drv.my_i2c_class, tmp75_dev->tmp75_cdev.dev);

	if (!my_i2c_drv.dev_count)
	{
		class_destroy(my_i2c_drv.my_i2c_class);
		my_i2c_drv.my_i2c_class = NULL;
	}

	cdev_del(&tmp75_dev->tmp75_cdev);
	unregister_chrdev_region(tmp75_dev->tmp75_cdev.dev, DEV_ADD_NUM);
	
	debug_printk("%sx is removed!\n", tmp75_dev->name);
	
	return 0;
}

struct i2c_driver my_tmp75_driver = {
		.driver = {
			.name = "my_i2c",
			.owner = THIS_MODULE,
		},
		.probe = my_tmp75_probe,
		.remove = my_tmp75_remove,
		.id_table = my_tmp75_ids,
};

static int __init my_tmp75_driver_init(void)
{
	return i2c_add_driver(&my_tmp75_driver);
}

static void __exit my_tmp75_driver_exit(void)
{
	i2c_del_driver(&my_tmp75_driver);
}

module_init(my_tmp75_driver_init);
module_exit(my_tmp75_driver_exit);

//module_i2c_driver(my_tmp75_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("caodongwang");
MODULE_DESCRIPTION("This i2c driver for my tmp75!");

 

好了,i2c相关讲解告一段落咯~~~

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值