Android上层与驱动交互完整篇(一)I2C设备驱动篇

Android上层与驱动交互完整篇(一)

驱动篇

以I2C设备驱动为例,创建与上层交互节点,解析数据并与设备进行通讯。
kernel中编程如同站在巨人肩膀上,有时候,我们并不需要理解I2C总线是如何工作的,也照样可以编写I2C的设备驱动。因为大多数平台厂商已经为我们编写好了I2C的板级驱动,配置好了I2C管脚的pinmux,话不多说,直接上I2C收发代码。
一般收发数据如下:
写数据: deviceaddr[0] + dataaddr + data[0]+data[2]+data[…]
读数据: deviceaddr[0] + dataaddr + deviceaddr[1] + data[0]+data[1]+data[…]
deviceaddr在kernel中为7为器件地址,在示波器或者逻辑分析仪中抓取到的数据为八位地址,最后一位0或者1 分别代表写与读。

写函数代码,可用于大多数平台

#if 1
static int i2c_write(struct i2c_adapter *i2c_adap,
			    unsigned char address,
			    unsigned int len, unsigned char const *data)
{
	struct i2c_msg msgs[1];
	int res;

	if (!data || !i2c_adap) {
		memc_error("%s:line=%d,error\n",__func__,__LINE__);
		return -EINVAL;
	}

	msgs[0].addr = address;
	msgs[0].flags = 0;
	msgs[0].buf = (unsigned char *)data;
	msgs[0].len = len;

	res = i2c_transfer(i2c_adap, msgs, 1);
	if (res == 1)
		return 0;
	else if(res == 0)
		return -EBUSY;
	else
		return res;

}

int i2c_tx_data(struct i2c_client *client, char *txData, int length)
{
	int ret = 0;
	int i = 0;

	for (i = 0; i < 3; i++) {
		ret = i2c_write(client->adapter, client->addr, length, txData);
		if (!ret)
			break;
	}
	return ret;
}

#endif

i2c_msg 这个结构体比较关键,我们将deviceaddr赋予msgs[0].addr,然后将器件地址 + data[]数据合并赋予msgs[0].buf,而msgs[0].flag=0,代表这是写数据。最后通过i2c_transfer函数将这个msgs发到设备。

读数据代码,可用于大多数平台

static int i2c_read(struct i2c_adapter *i2c_adap,
			   unsigned char address, unsigned char *wdata,
			   unsigned int wlen, unsigned char *rdata,unsigned int rlen)
{
	struct i2c_msg msgs[2];
	int res;

	if (!rdata || !i2c_adap) {
		memc_error("%s:line=%d,error\n",__func__,__LINE__);
		return -EINVAL;
	}

	msgs[0].addr = address;
	msgs[0].flags = 0;	/* write */
	msgs[0].buf = wdata;
	msgs[0].len = wlen;

	msgs[1].addr = address;
	msgs[1].flags = I2C_M_RD;
	msgs[1].buf = rdata;
	msgs[1].len = rlen;

	res = i2c_transfer(i2c_adap, msgs, 2);
	if (res == 2)
		return 0;
	else if(res == 0)
		return -EBUSY;
	else
		return res;

	return res;
}

static int i2c_rx_data(struct i2c_client *client, char * txData,int tlen,char *rxData, int rlen)
{
	int ret = 0;
	int i = 0;

	for (i = 0; i < 2; i++) {

		ret = i2c_read(client->adapter, client->addr, txData,tlen,rxData,rlen);
		if (ret < 0){
			memc_error("i2c_rx_data error\n");
		}else{
			break;
		}
	}
	return ret;
}

上面讲过,读数据,先要写数据,然后再去读数据,所以这里就需要发送两个msgs,第一个msgs与写数据相同,第二个msgs的flag为1,也就是代码中的I2C_M_RD,而读到的数据存在msgs[1].buf中。

收发函数有了,接下我们要创建节点,代码中创建的节点为/sys/class/memc/memc/cmd,我们可以通过这个节点来于设备通信,例如:
写数据:
echo 7 0 0xc3 0x01 0x00 0x00 0x00 0x00 0x00 > cmd ; 7代表写7个数据,0代表没有读的数据。
读数据:echo 0 1 0xc2 0x1c 0x00 0x00 0x00 0x00 0x00 >cmd ;cat cmd ; 0告诉驱动这是读,1 是读一个数据。 然后读到的数据存在cmd buff中,通过cat显示。

创建节点代码

static ssize_t memc_write(struct device *dev,
                                            struct device_attribute *attr,
                                            const char *buf, size_t count)
{
	struct memc_driver *memc_driver = dev_get_drvdata(dev);
	return i2c_write_cmd(memc_driver->client,buf,count);
}

static ssize_t memc_read(struct device *dev, struct device_attribute *attr,
											char *buf)
{
	struct memc_driver *memc_driver = dev_get_drvdata(dev);
	return i2c_read_cmd(memc_driver->client,buf);
}

static DEVICE_ATTR(cmd, 0644,
                   memc_read, memc_write);


static struct attribute *attrs[] = {
	&dev_attr_name.attr,
	//&dev_attr_cmd.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

static struct attribute_group attr_group = {
	.attrs = attrs,
};

memc_class = class_create(THIS_MODULE, DRIVER_NAME);
	if (IS_ERR(memc_class)) {
		ret= PTR_ERR(memc_class);
		memc_error("memc_class: class create failed\n");
		goto malloc_error;
	}

memc_driver->dev = device_create(memc_class,
			 &client->dev, 0, NULL, DEVICE_NAME);
	if (unlikely(IS_ERR(memc_driver->dev))) {
		ret = PTR_ERR(memc_driver->dev);
		memc_driver->dev = NULL;
		goto destory_class;
	}
	
ret = sysfs_create_group(&memc_driver->dev->kobj, &attr_group);
	if(ret){
		memc_error("sysfs_create_group in memc driver\n");
		goto destory_device;
	}
	

上面的class_create会创建名字为DRIVER_NAME的目录,存在于/sys/class/中,而device_create会创建DEVICE_NAME的目录,存在于/sys/class/DRIVER_NAME中。
最后sysfs_create_group会创建目录下的需要的节点。
echo … > cmd 命令 调用memc_write函数。
cat cmd 命令 调用 上面memc_read()函数。

节点创建完成,收发函数也编写完成,那么数据该如何解析呢,怎么将echo 进来的字符串转成 一个一个字节的数据呢?这个也是关键。

static int bytes_to_string(char *buf, u8 *data, int data_size, int base)
{
	int pos = 0, i;

	/*if(base == 10){ // denary format
		pos += snprintf(buf + pos, PAGE_SIZE - pos,"denary:	");
	}else{ //default hex format
		pos += snprintf(buf + pos, PAGE_SIZE - pos,"hex: ");	
	}*/
		
	for(i = 0; i < data_size; i++){
		if(base == 10){ // denary format
			//KLOGD("data[%d] = %d \n",i,data[i]);
			pos += snprintf(buf + pos, PAGE_SIZE - pos,"%d ",data[i]);
		}else{ //default hex format
			//KLOGD("data[%d] = 0x%02x \n",i,data[i]);
			pos += snprintf(buf + pos, PAGE_SIZE - pos,"0x%02x ",data[i]);	
		}
	}
	pos += snprintf(buf + pos, PAGE_SIZE - pos,"\n");
	//delete end space
	//data[pos-1] = '\0';
	return pos;
}

static int strings_parse(char *kbuf, char *argv[], const char *cchar)
{
	int argn;

	for(argn = 0; argn < MAX_ARG_NUM + 1 ; argn++){
		argv[argn] = strsep(&kbuf, cchar);
		if (argv[argn] == NULL){
				break;
		}
	}

	if(argn > MAX_ARG_NUM + 1){
		memc_notice("get argn is %d ,but max is %d\n", argn, MAX_ARG_NUM);
		return -EINVAL;
	}
	return argn;
}

static int cmd_parse(struct i2c_client *client, int argn, char *argv[])
{
	ssize_t	ret;
	int num = 0,i;
	u8 value;
	u8 wbuf[CMD_W_MAX_SIZE] = {0};
	u8 rbuf[CMD_R_MAX_SIZE] = {0};
	u8 wnum = 0,rnum = 0;

	if ((argn < CMD_MIN_SIZE) || (argn > CMD_MAX_SIZE + 3)){

		memc_info("input format error, argn = %d \n",argn);
		haier_cmd_help();
		goto err;
	}
	
	ret = kstrtou8(argv[0], 0, &value);// auto data format
	if (ret == 0)
		wnum = value;
	ret = kstrtou8(argv[1], 0, &value);// auto data format
	if (ret == 0)
		rnum = value;  	

	//memc_info("wnum=%d ,rnum=%d\n",wnum,rnum);
	
	if((!(wnum | rnum))||(wnum > CMD_W_MAX_SIZE)||(rnum > CMD_R_MAX_SIZE)
		||((argn-CMD_MIN_SIZE) > CMD_W_MAX_SIZE))
	{
		memc_error("input arguments error \n");
		goto err;
	}

	if(argn > CMD_MIN_SIZE){// get data
		for(i = 0; i < argn-CMD_MIN_SIZE; i++) {
			ret = kstrtou8(argv[i+CMD_MIN_SIZE], 0, &value);//auto data format
			if (ret == 0){

				wbuf[i] = value;
				//memc_info("data[%d]=0x%02x\n",i,wbuf[i]);
			}
		}
		num = i;
	}
	if(wnum > 0){
		if(wnum!=num){

			memc_error("data not match,wnum %d ,input num %d \n",wnum,num);
			goto err;
		}else{

			if(haier_i2c_tx_data(client,wbuf,wnum)){

				memc_error("write  failed !!! \n");
				goto err;
			}else{
				//here need store write data to dlp struct member !!!!!!
				memc_info("write successful \n");
			}
		}
	}
	mdelay(10);

	if (rnum > 0){
		if((argn > CMD_MIN_SIZE)&&(wnum == 0)){
			ret = haier_i2c_rx_data(client,wbuf,num,rbuf,rnum);
		}

		if(ret < 0){
			memc_error("read cmd ailed \n");
			goto err;
		}else{
			for(i=0;i<rnum;i++){
				//if(ddata->debug_enable)
				g_rbuf[i] = rbuf[i];
				g_rnum = rnum;
				memc_info("read data[%d]=0x%02x \n",i, rbuf[i]);
			}
		}
	}
err:
	return -EINVAL;
}

static int i2c_write_cmd(struct i2c_client *client,const char *buf, size_t size)
{
	char *argv[MAX_ARG_NUM];
	int argn;
	char *kbuf;
	
	if(!client){
        memc_error("no driver device \n");
        return -EINVAL;;
    }

	memc_info("wbuf %s \n",buf);

	kbuf = kstrdup(buf, GFP_KERNEL);
	argn = haier_strings_parse(kbuf,argv," ");
	if(argn <= 0){
		goto out;
	}

	if (argn == 1){
		if (!strncmp(argv[0], "help",4) || !strncmp(argv[0], "h", 1)){
			haier_cmd_help();
			goto out;
		}
	}
	cmd_parse(client,argn,argv);
	
out:
	kfree(kbuf);
	return size;
}

static int i2c_read_cmd(struct i2c_client *client,char *buf)
{
	int pos = 0;
	int i = 0;
	if (g_rnum == 0){

		haier_cmd_help();
		pos = snprintf(buf, PAGE_SIZE, "read data error");
	}else{
		pos = bytes_to_string(buf,g_rbuf,g_rnum,16);

	}

	//clear g_rnum and clear g_rbuf to 0xff
	for(i=0;i<g_rnum;i++){
		g_rbuf[i]=0xff;
	}
	g_rnum = 0;

	return pos;
}

上面字符串的处理有点复杂,繁琐,当然有更好的算法。
接下来看平台注册代码,因为是I2C设备,所以注册函数为i2c_add_driver

static int memc_probe(struct i2c_client *client,
		     	 const struct i2c_device_id *id)
{
	int ret = 0;
	struct memc_driver *memc_driver = NULL;
	static struct class *memc_class;


	memc_driver = kzalloc(sizeof(struct memc_driver), GFP_KERNEL);
	if (memc_driver == NULL) {
		printk("failed to create our memc_driver\n");
		return -ENOMEM;
	}

	memc_driver->client = client;
	i2c_set_clientdata(client,memc_driver);

	ret = dts_parse(client);
	if (ret){
		memc_error("dts parse error\n");
		goto malloc_error;
	}

	memc_class = class_create(THIS_MODULE, DRIVER_NAME);
	if (IS_ERR(memc_class)) {
		ret= PTR_ERR(memc_class);
		memc_error("memc_class: class create failed\n");
		goto malloc_error;
	}

	memc_driver->dev = device_create(memc_class,
			 &client->dev, 0, NULL, DEVICE_NAME);
	if (unlikely(IS_ERR(memc_driver->dev))) {
		ret = PTR_ERR(memc_driver->dev);
		memc_driver->dev = NULL;
		goto destory_class;
	}
	dev_set_drvdata(memc_driver->dev, memc_driver);
	ret = sysfs_create_group(&memc_driver->dev->kobj, &attr_group);
	if(ret){
		memc_error("sysfs_create_group in memc driver\n");
		goto destory_device;
	}
	
	memc_info("haier_memc_probe\n");
	return 0;

destory_device:
	device_destroy(memc_class,0);
destory_class:
	class_destroy(memc_class);
malloc_error:
	kfree(memc_driver);
	return -ENODEV;

}

static int memc_remove(struct i2c_client *client)
{
	struct memc_driver *memc_driver = i2c_get_clientdata(client);
	kfree(memc_driver);
	return 0;
}

static const struct of_device_id i2c_memc_of_match[] = {
	{ .compatible = "max,memc" },
	{ }
};

static const struct i2c_device_id memc_device_id[] = {
	{DEVICE_NAME, 0 },
	{ }
};

MODULE_DEVICE_TABLE(i2c, memc_device_id);


static struct i2c_driver memc_driver = {
	.driver = {
		.name	= DRIVER_NAME,
		.owner  = THIS_MODULE,
		.of_match_table = of_match_ptr(i2c_memc_of_match),
	},
	.probe = memc_probe,
	.remove = memc_remove,
	.id_table	= memc_device_id,
};

static int __init memc_init(void)
{
	memc_info("memc_init\n");
	i2c_add_driver(&memc_driver);
	return 0;
}

static void __exit memc_exit(void)
{
	i2c_del_driver(&memc_driver);
}

module_init(memc_init);
module_exit(memc_exit);

MODULE_DESCRIPTION("3251 Memc Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("shangbaorun@haier.com");

Dts注册就比较简单了,先弄明白平台使用的是那一路I2C,然后在I2
c节点点下添加与compatible 对应的字符串即可。

&i2c1 {
	status = "okay";
	clock-frequency = <300000>;
	pinctrl-names="default";
	pinctrl-0=<&i2c1_h_pins>;
	memc@1c{
		compatible = "max,memc";
		reg = <0x1c>;
		status = "okay";
	};
};

其中0x1c即deviceaddr 的7位地址,i2c1_h_pins即GPIO的pinmux配置,这里已经将其配置作为i2c管脚使用。

全部代码在此链接,有需要的可以下载。
代码链接
共同进步!

  • 10
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux 内核I2C 驱动中,通常会提供设备读写节点,以便用户空间程序可以通过文件系统接口来访问 I2C 设备。这些节点一般位于 `/dev` 目录下,命名规则为 `i2c-X`,其中 X 是 I2C 控制器的编号。 例如,如果系统中有一个名为 `i2c-1` 的 I2C 控制器,那么对应的设备节点为 `/dev/i2c-1`。 在 Android 上层,可以使用 Java 的 `FileInputStream` 和 `FileOutputStream` 类来读写 I2C 设备。需要先打开对应的设备节点,然后通过文件流进行读写操作。 以下是一个简单的读写示例: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class I2CExample { private static final String I2C_DEVICE = "/dev/i2c-1"; private static final int I2C_ADDRESS = 0x50; public static void main(String[] args) { try { // 打开 I2C 设备节点 FileInputStream input = new FileInputStream(I2C_DEVICE); FileOutputStream output = new FileOutputStream(I2C_DEVICE); // 写入数据 byte[] writeBuffer = { 0x00, 0x01, 0x02 }; output.write(writeBuffer); // 读取数据 byte[] readBuffer = new byte[3]; input.read(readBuffer); // 关闭文件流 input.close(); output.close(); System.out.println("Read data: " + toHexString(readBuffer)); } catch (IOException e) { e.printStackTrace(); } } private static String toHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X ", b)); } return sb.toString(); } } ``` 以上示例中,我们打开了 `/dev/i2c-1` 设备节点,并且使用地址 `0x50` 进行了一次写操作和一次读操作。读取到的数据会以十六进制字符串的形式输出到控制台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值