linux IIC子系统分析(九)——实例分析通过设备节点访问I2c设备

《 linux IIC子系统分析(四)——I2c bus初始化》 中我们创建了I2C 总线驱动,I2C adapter device 和adapter drivers也在这时创建

《linux IIC子系统分析(七)——实例分析通过i2c-dev操作I2C设备》 我们实现了在应用层驱动I2C设备EEPROM

在《linux IIC子系统分析(九)——实例分析通过sysfs访问I2c设备》我们实现了通过sys导出的bin属性访问I2C设备EEPROM

在我们实际开发中,I2C 总线驱动一般芯片原厂会提供,我们开发一般是设计设备驱动。

在访问I2C设备驱动的方法中:

通过i2c-dev 从应用层驱动设备,这样的设计需要对I2C通讯协议非常熟悉,一般比较少使用。

通过sysfs导出的bin属性文件访问,这种方法多用了查看驱动设备的信息,实际使用它来操作设备较少。

下面介绍一种比较通用客户驱动程序开发的方法,其开发步骤为:

(1)注册板载I2C设备信息

(2)定义I2C驱动ID

(3)定义i2c_driver结构并完成其相应的函数

(4)模块初始化时添加/撤销时删除i2c_driver

(5)添加设备节点,sysfs bin属性

注册板载I2C信息,与上一章相同在/linux-2.6.32.2/arch/arm/mach-s3c2440/math-mini2440.c中添加:

#include <linux/i2c/at24.h>
#include <linux/i2c.h>
static struct s3c24xx_mci_pdata mini2440_mmc_cfg = {
   .gpio_detect   = S3C2410_GPG(8),
   .gpio_wprotect = S3C2410_GPH(8),
   .set_power     = NULL,
   .ocr_avail     = MMC_VDD_32_33|MMC_VDD_33_34,
};


static struct at24_platform_data at24c02 = {
	.byte_len	= SZ_2K / 8,
	.page_size	= 8,
	.flags		= 0,
};

static struct i2c_board_info __initdata smdk_i2c_devices[] = {
	/* more devices can be added using expansion connectors */
	{
		I2C_BOARD_INFO("24c02", 0x50),
		.platform_data = &at24c02,
	},
};


在函数static void __init mini2440_machine_init(void) 中添加:

i2c_register_board_info(0, smdk_i2c_devices, ARRAY_SIZE(smdk_i2c_devices));


添加驱动程序如下:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <linux/smp_lock.h>
#include <linux/jiffies.h>
#include <asm/uaccess.h>
#include <linux/delay.h>


#define DEBUG 1
#ifdef DEBUG
#define dbg(x...) printk(x)
#else 
#define dbg(x...) (void)(0)
#endif

#define I2C_MAJOR 89
#define DEVICE_NAME "at24c02"
static struct class *my_dev_class;
static struct i2c_client *my_client;
static struct i2c_driver my_i2c_driver;


static struct i2c_device_id my_ids[] = {
	{"24c01",0x50},
	{"24c02",0x50},
	{"24c08",0x50},
	{}
};

MODULE_DEVICE_TABLE(i2c,my_ids);

static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int res;
	struct device *dev;

	dbg("probe:name = %s,flag =%d,addr = %d,adapter = %d,driver = %s\n",client->name,
		 client->flags,client->addr,client->adapter->nr,client->driver->driver.name );

	dev = device_create(my_dev_class, &client->dev,
				     MKDEV(I2C_MAJOR, 0), NULL,
				     DEVICE_NAME);
	if (IS_ERR(dev))
	{
		dbg("device create error\n");
		goto out;
	}
	my_client = client;
	
	return 0;
out:
	return -1;
}
static int  my_i2c_remove(struct i2c_client *client)
{

	dbg("remove\n");
	return 0;
}

static ssize_t at24c02_read(struct file *fd, char *buf, ssize_t count, loff_t *offset)
{
	char *tmp;
	int ret;
	char data_byte;
	char reg_addr = 0,i;
	struct i2c_client *client = (struct i2c_client*) fd->private_data;
	struct i2c_msg msgs[2];

	dbg("read:count = %d,offset = %ld\n",count,*offset);
	tmp = kmalloc(count,GFP_KERNEL);

	if (!tmp)
	{
		dbg("malloc error in read function\n");
		goto out;
	}

	reg_addr = *offset;
	msgs[0].addr = client->addr;
	msgs[0].flags = client->flags & (I2C_M_TEN|I2C_CLIENT_PEC) ;
	msgs[0].len = 1;
	msgs[0].buf = (char *)®_addr;
	
	msgs[1].addr= client->addr;
	msgs[1].flags = client->flags & (I2C_M_TEN|I2C_CLIENT_PEC);
	msgs[1].flags |= I2C_M_RD;
	msgs[1].len = count;
	msgs[1].buf = (char*)tmp;

	ret = i2c_transfer(client->adapter,&msgs,2);
	if (ret != 2)
		goto out;
	if (copy_to_user(buf, tmp, count))
		goto out;
	
	kfree(tmp);
	return count;
out:
	kfree(tmp);
	return -1;	
	
}


static int at24c02_ioctl(struct file *fd, unsigned int cmd, unsigned long arg)
{
	dbg("ioctl code ...\n");
	return 0;
}

static ssize_t at24c02_write(struct file *fd, char *buf, ssize_t count, loff_t *offset)
{
	int ret,i;
	char *tmp;
	int errflg;
	struct i2c_msg msg;
 	struct i2c_client *client = (struct i2c_client*) fd->private_data;
	char tmp_data[2];

 	dbg("write:count = %d,offset = %ld\n",count,*offset);
	tmp = kmalloc(count, GFP_KERNEL);
	if (!tmp)
		goto out;
	if (copy_from_user(tmp, buf, count))
		goto out;
	msg.addr = client->addr;
	msg.flags = client->flags & (I2C_M_TEN | I2C_CLIENT_PEC);
	for (i = 0; i < count; i++) {
		msg.len = 2;
		tmp_data[0] = *offset + i;
		tmp_data[1] = tmp[i];
		msg.buf = tmp_data;
		ret = i2c_transfer(client->adapter,&msg,1);
		if (ret != 1)
			goto out;
		msleep(1);
	} 
	kfree(tmp);

	return ((ret == 1) ? count:ret);
out:
	kfree(tmp);
	return -1;
	
}
static int at24c02_open(struct inode *inode, struct file *fd)
{

	fd->private_data =(void*)my_client;
	return 0;

}

static int at24c02_release(struct inode *inode, struct file *fd)
{
	dbg("release\n");
	fd->private_data = NULL;
	
	return 0;	

}

static const struct file_operations i2c_fops = {
	.owner = THIS_MODULE,
	.open 	= at24c02_open,
	.read  = at24c02_read,
	.write = at24c02_write,
	.unlocked_ioctl = at24c02_ioctl,
	.release = at24c02_release,
};

static struct i2c_driver my_i2c_driver = {
	.driver = {
		.name = "i2c_demo",
		.owner = THIS_MODULE,
	},
	.probe = my_i2c_probe,
	.remove = my_i2c_remove,
	.id_table = my_ids,
};

static int __init my_i2c_init(void)
{
	int res;
	
	
	res = register_chrdev(I2C_MAJOR,DEVICE_NAME,&i2c_fops);
	if (res)
	{
		dbg("register_chrdev error\n");
		return -1;
	}
	my_dev_class = class_create(THIS_MODULE, DEVICE_NAME);
	if (IS_ERR(my_dev_class))
	{
		dbg("create class error\n");
		unregister_chrdev(I2C_MAJOR, DEVICE_NAME);
		return -1;
	}
	return i2c_add_driver(&my_i2c_driver);
}

static void __exit my_i2c_exit(void)
{
	unregister_chrdev(I2C_MAJOR, DEVICE_NAME);
	class_destroy(my_dev_class);
	i2c_del_driver(&my_i2c_driver);
	
}

MODULE_AUTHOR("itspy<itspy.wei@gmail.com>");
MODULE_DESCRIPTION("i2c client driver demo");
MODULE_LICENSE("GPL");
module_init(my_i2c_init);
module_exit(my_i2c_exit);


将驱动程序编译成驱动模块添加到内核,可以查看到:

[root@FriendlyARM i2c-0]# ls
0-0050         name           subsystem
delete_device  new_device     uevent
[root@FriendlyARM i2c-0]# cd 0-0050/
[root@FriendlyARM 0-0050]# ls
at24c02    driver     modalias   name       subsystem  uevent
[root@FriendlyARM 0-0050]# pwd
/sys/devices/platform/s3c2440-i2c/i2c-0/0-0050
[root@FriendlyARM 0-0050]# ls /dev -l
crw-rw----    1 root     root      10,  59 Jan  1  2000 adc
crw-rw----    1 root     root      89,   0 Jan  1 01:14 at24c02
crw-rw----    1 root     root      14,   4 Jan  1  2000 audio


这样就可以与正常驱动设备一样操作eeprom了。

  由于各种原因,后续文章内容将更新到公众号,本平台将不再做更新。

CSDN上相关文章的测试工程代码,也统一放到了公众号上,可以免费免积分下载

有需要的可以扫下面二维码转到公众号,欢迎关注,欢迎提出您宝贵的意见和建议!

liwen01 2022.08.21

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
LinuxI2C系统是用于在Linux内核中管理和操作I2C总线的系统。它提供了一组API和驱动程序,允许用户空间应用程序与连接到I2C总线上的设备进行通信。 在Linux中,I2C系统由以下几个主要组件组成: 1. I2C核心驱动程序:这是I2C系统的核心部分,负责管理I2C总线和设备注册、协议处理等功能。它提供了一组API供其他驱动程序或用户空间应用程序使用。 2. I2C适配器驱动程序:这些驱动程序用于支持特定的硬件I2C适配器,如FPGA、SOC等。它们与I2C核心驱动程序紧密配合,负责将硬件特定的操作转换为通用的I2C操作。 3. I2C设备驱动程序:这些驱动程序用于支持连接到I2C总线上的具体设备。每个I2C设备都有一个对应的设备驱动程序,负责处理设备的初始化、通信协议等。在Linux中,这些设备驱动程序通常作为内核模块存在。 4. I2C工具和库:除了内核驱动程序外,Linux还提供了一些用户空间工具和库,用于与I2C设备进行交互。例如,`i2cdetect`工具用于检测I2C总线上的设备地址,`i2cget`和`i2cset`工具用于读取和写入I2C设备的寄存器值。 用户空间应用程序可以使用I2C系统提供的API和工具来访问和控制连接到I2C总线上的设备。通过打开适当的设备节点文件,并使用相应的读写操作,可以向设备发送命令和数据,以及从设备读取响应和数据。 总而言之,LinuxI2C系统提供了一套完整的解决方案,使用户能够方便地在Linux环境中操作和管理I2C设备
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

li_wen01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值