• I2C(二) linux2.6



    title: I2C(二) linux2.6
    date: 2019/1/28 18:18:42
    toc: true

    I2C(二) linux2.6

    总线驱动

    官方例子是driversi2cussesi2c-s3c2410.c

    关键结构

    static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
    	.master_xfer		= s3c24xx_i2c_xfer,
    	.functionality		= s3c24xx_i2c_func,
    };
    
    static struct s3c24xx_i2c s3c24xx_i2c = {
    	.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
    	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
    	.tx_setup	= 50,
    	.adap		= {
    		.name			= "s3c2410-i2c",
    		.owner			= THIS_MODULE,
    		.algo			= &s3c24xx_i2c_algorithm,
    		.retries		= 2,
    		.class			= I2C_CLASS_HWMON,
    	},
    };
    
    
    struct i2c_adapter {  
     struct module *owner;              //所属模块  
     unsigned int id;                //algorithm的类型,定义于i2c-id.h,  
     unsigned int class;      
     const struct i2c_algorithm *algo;     //总线通信方法结构体指针  
     void *algo_data;               //algorithm数据  
     struct rt_mutex bus_lock;        //控制并发访问的自旋锁  
     int timeout;     
     int retries;                //重试次数  
     struct device dev;             //适配器设备   
     int nr;                          //存放在i2c_adapter_idr里的位置号
     char name[48];              //适配器名称  
     struct completion dev_released;    //用于同步  
     struct list_head userspace_clients;   //client链表头  
    
    };
    
    

    入口

    这里是一个platform总线框架,第一个函数也就是probe

    static struct platform_driver s3c2440_i2c_driver = {
    	.probe		= s3c24xx_i2c_probe,
    	.remove		= s3c24xx_i2c_remove,
    	.resume		= s3c24xx_i2c_resume,
    	.driver		= {
    		.owner	= THIS_MODULE,
    		.name	= "s3c2440-i2c",
    	},
    };
    
    static int s3c24xx_i2c_probe(struct platform_device *pdev)
    {
    	struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
    	struct resource *res;
    	int ret;
    
    	/* find the clock and enable it */
    	i2c->dev = &pdev->dev;
    	i2c->clk = clk_get(&pdev->dev, "i2c");
    	clk_enable(i2c->clk);
    	/* map the registers */
    	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    	i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
    					 pdev->name);
    	i2c->regs = ioremap(res->start, (res->end-res->start)+1);
    
    	// 设置了具体的适配器算法
    	/* setup info block for the i2c core */
    	i2c->adap.algo_data = i2c;
    	i2c->adap.dev.parent = &pdev->dev;
    
    	//寄存器 gpio 初始化
    	/* initialise the i2c controller */
    	ret = s3c24xx_i2c_init(i2c);
    	
    	//设置中断函数,中断里执行具体的读写函数
    	/* find the IRQ for this unit (note, this relies on the init call to
    	 * ensure no current IRQs pending 
    	 */
    	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    	ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
    			  pdev->name, i2c);
    	i2c->irq = res;
    		
    		
    	//添加adapt		
    	ret = i2c_add_adapter(&i2c->adap);
    
    	//pdev.dev.driver_data=i2c
    	platform_set_drvdata(pdev, i2c);
    
    	return 0;
    }
    

    i2c_add_adapter

    1. 设置这个具体的适配器相关的信息
    2. 添加新的适配器,这个情况下可能有新的client能够挂接到dev链表
    3. 调用这个i2c_driverattach_adapter
    int i2c_add_adapter(struct i2c_adapter *adapter)
    {
        res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);
        //调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体
    	i2c_register_adapter(adapter)
    }
    
    static int i2c_register_adapter(struct i2c_adapter *adap)
    {
    	int res = 0;
    	struct list_head   *item;
    	struct i2c_driver  *driver;
    
    	INIT_LIST_HEAD(&adap->clients);
    	list_add_tail(&adap->list, &adapters);
    
    	
    	// 这个parent 在上面的probe 指向了 资源文件指向的platform_device.dev
    	/* Add the adapter to the driver core.
    	 * If the parent pointer is not set up,
    	 * we add this adapter to the host bus.
    	 */
    	if (adap->dev.parent == NULL) {
    		adap->dev.parent = &platform_bus;
    		pr_debug("I2C adapter driver [%s] forgot to specify "
    			 "physical device
    ", adap->name);
    	}
    	sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
    	adap->dev.release = &i2c_adapter_dev_release;
        //这里设置了具体的class 
    	adap->dev.class = &i2c_adapter_class;
    	
    	//注册device 在总线框架中,注册dev 会去寻找driver,执行probe
    	/* for new style drivers, when registration returns the driver core
    	 * will have called probe() for all matching-but-unbound devices.
    	 */
    	res = device_register(&adap->dev);
    
    
    
    	// 扫描静态的单板外设信息,有个全局链表__i2c_board_list 保存着未添加到链表的
    	// 想要加入到的client的相关信息,这个后续可以看 linux3.4
    	// 这里就是根据这个新的适配器添加这个client到链表
    	/* create pre-declared device nodes for new-style drivers */
    	if (adap->nr < __i2c_first_dynamic_bus_num)
    		i2c_scan_static_board_info(adap);
    
    	
    	// 遍历这个drivers ,去执行实际驱动i2c_driver 的attach_adapter
    	/* let legacy drivers scan this bus for matching devices */
    	list_for_each(item,&drivers) {
    		driver = list_entry(item, struct i2c_driver, list);
    		if (driver->attach_adapter)
    			/* We ignore the return code; if it fails, too bad */
    			driver->attach_adapter(adap);
    	}
    }
    

    最后执行attach_adapter,这个在后面的设备驱动再分析

    硬件操作

    硬件操作最后归结到接口master_xfer,I2C的传输到最后可以整理为

    1. 开始信号
    2. 结束信号
    3. ACK/NAK
    4. 数据传输1字节,读写是根据起始信号决定

    在官方的驱动中,使用中断来传输,传输中使用休眠唤醒

    //休眠
    timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
    //完成后唤醒
    wake_up(&i2c->wait);
    // probe 时候申请中断
    request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);
    

    设备驱动

    参考文件driver/i2c/chips/eeprom.c

    入口

    static int __init eeprom_init(void)
    {
    	return i2c_add_driver(&eeprom_driver);
    }
    static inline int i2c_add_driver(struct i2c_driver *driver)
    {
    	return i2c_register_driver(THIS_MODULE, driver);
    }
    

    注册

    注册的时候会加入驱动到链表,最后执行驱动的attach_adapter

    int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
    {
    	int res;
    
    	/* add the driver to the list of i2c drivers in the driver core */
    	driver->driver.owner = owner;
    	driver->driver.bus = &i2c_bus_type;
    
    	//注册driver 会去寻找dev,执行probe
    	/* for new style drivers, when registration returns the driver core
    	 * will have called probe() for all matching-but-unbound devices.
    	 */
    	res = driver_register(&driver->driver);
    
    	//添加链表
    	list_add_tail(&driver->list,&drivers);
    
    	//遍历adapt,执行驱动的attach_adapter
    	//这个和我们最后注册 adapt的时候,遍历 driver 时是差不多的
    	/* legacy drivers scan i2c busses directly */
    	if (driver->attach_adapter) {
    		struct i2c_adapter *adapter;
    		list_for_each_entry(adapter, &adapters, list) {
    			driver->attach_adapter(adapter);
    		}
    	}
    	return 0;
    }
    

    attach_adapter

    这里看下eeprom的是怎样的

    static struct i2c_driver eeprom_driver = {
    	.driver = {
    		.name	= "eeprom",
    	},
    	.id		= I2C_DRIVERID_EEPROM,
    	.attach_adapter	= eeprom_attach_adapter,
    	.detach_client	= eeprom_detach_client,
    };
    

    i2c_probe总结起来就是 使用adapter先去检测地址addr_data,成功后调用eeprom_detect,挂接client,注册我们实际的字符设备驱动等操作

    static int eeprom_attach_adapter(struct i2c_adapter *adapter)
    {
    	return i2c_probe(adapter, &addr_data, eeprom_detect);
    }
    

    我们这里的地址有以下几种,依次处理Force,probe,normal_i2c,ignore

    struct i2c_client_address_data {
    	unsigned short *normal_i2c;
    	unsigned short *probe;
    	unsigned short *ignore;
    	unsigned short **forces;
    };
    
    

    具体流程如下

    int i2c_probe(struct i2c_adapter *adapter,
    	      struct i2c_client_address_data *address_data,
    	      int (*found_proc) (struct i2c_adapter *, int, int))
    {
    	int i, err;
    	int adap_id = i2c_adapter_id(adapter);
    
    	/* Force entries are done first, and are not affected by ignore
    	   entries */
    	if (address_data->forces) {
    		unsigned short **forces = address_data->forces;
    		int kind;
    
    		for (kind = 0; forces[kind]; kind++) {
    			for (i = 0; forces[kind][i] != I2C_CLIENT_END;
    			     i += 2) {
    				if (forces[kind][i] == adap_id
    				 || forces[kind][i] == ANY_I2C_BUS) {
    					dev_dbg(&adapter->dev, "found force "
    						"parameter for adapter %d, "
    						"addr 0x%02x, kind %d
    ",
    						adap_id, forces[kind][i + 1],
    						kind);
    					err = i2c_probe_address(adapter,
    						forces[kind][i + 1],
    						kind, found_proc);
    					if (err)
    						return err;
    				}
    			}
    		}
    	}
    
    	/* Stop here if we can't use SMBUS_QUICK */
    	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {
    		if (address_data->probe[0] == I2C_CLIENT_END
    		 && address_data->normal_i2c[0] == I2C_CLIENT_END)
    			return 0;
    
    		dev_warn(&adapter->dev, "SMBus Quick command not supported, "
    			 "can't probe for chips
    ");
    		return -1;
    	}
    
    	/* Probe entries are done second, and are not affected by ignore
    	   entries either */
    	for (i = 0; address_data->probe[i] != I2C_CLIENT_END; i += 2) {
    		if (address_data->probe[i] == adap_id
    		 || address_data->probe[i] == ANY_I2C_BUS) {
    			dev_dbg(&adapter->dev, "found probe parameter for "
    				"adapter %d, addr 0x%02x
    ", adap_id,
    				address_data->probe[i + 1]);
    			err = i2c_probe_address(adapter,
    						address_data->probe[i + 1],
    						-1, found_proc);
    			if (err)
    				return err;
    		}
    	}
    
    	/* Normal entries are done last, unless shadowed by an ignore entry */
    	for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
    		int j, ignore;
    
    		ignore = 0;
    		for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
    		     j += 2) {
    			if ((address_data->ignore[j] == adap_id ||
    			     address_data->ignore[j] == ANY_I2C_BUS)
    			 && address_data->ignore[j + 1]
    			    == address_data->normal_i2c[i]) {
    				dev_dbg(&adapter->dev, "found ignore "
    					"parameter for adapter %d, "
    					"addr 0x%02x
    ", adap_id,
    					address_data->ignore[j + 1]);
    				ignore = 1;
    				break;
    			}
    		}
    		if (ignore)
    			continue;
    
    		dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
    			"addr 0x%02x
    ", adap_id,
    			address_data->normal_i2c[i]);
    		err = i2c_probe_address(adapter, address_data->normal_i2c[i],
    					-1, found_proc);
    		if (err)
    			return err;
    	}
    
    	return 0;
    }
    
    

    eeprom_detect

    我们在这个驱动中的found_proc实际上是eeprom_detect,这里设置client,设置具体的adapt,driver,执行依附

    /* This function is called by i2c_probe */
    static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
    {
    	struct i2c_client *new_client;
    	struct eeprom_data *data;
    	int err = 0;
    
    	/* There are three ways we can read the EEPROM data:
    	   (1) I2C block reads (faster, but unsupported by most adapters)
    	   (2) Consecutive byte reads (100% overhead)
    	   (3) Regular byte data reads (200% overhead)
    	   The third method is not implemented by this driver because all
    	   known adapters support at least the second. */
    	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
    					    | I2C_FUNC_SMBUS_BYTE))
    		goto exit;
    
    	if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
    		err = -ENOMEM;
    		goto exit;
    	}
    
    	
    	// 设置client
    	new_client = &data->client;
    	memset(data->data, 0xff, EEPROM_SIZE);
    	i2c_set_clientdata(new_client, data);
    	new_client->addr = address;
    	new_client->adapter = adapter;
    	new_client->driver = &eeprom_driver;
    	new_client->flags = 0;
    
    	/* Fill in the remaining client fields */
    	strlcpy(new_client->name, "eeprom", I2C_NAME_SIZE);
    	data->valid = 0;
    	mutex_init(&data->update_lock);
    	data->nature = UNKNOWN;
    
        //i2c_client与适配器进行连接
    	/* Tell the I2C layer a new client has arrived */
    	if ((err = i2c_attach_client(new_client)))
    		goto exit_kfree;
    
    
    	// 这个类似字符设备驱动
    	/* create the sysfs eeprom file */
    	err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
    }
    
    

    i2c_attach_client

    将i2c_client与适配器进行连接

    int i2c_attach_client(struct i2c_client *client)
    {
    	struct i2c_adapter *adapter = client->adapter;
    	int res = 0;
    
    	list_add_tail(&client->list,&adapter->clients);
    
    	client->usage_count = 0;
    	client->dev.parent = &client->adapter->dev;
    	client->dev.bus = &i2c_bus_type;
    	if (client->driver)
    		client->dev.driver = &client->driver->driver;
    	if (client->driver && !is_newstyle_driver(client->driver)) {
    		client->dev.release = i2c_client_release;
    		client->dev.uevent_suppress = 1;
    	} else
    		client->dev.release = i2c_client_dev_release;
    
    	snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
    		"%d-%04x", i2c_adapter_id(adapter), client->addr);
    	dev_dbg(&adapter->dev, "client [%s] registered with bus id %s
    ",
    		client->name, client->dev.bus_id);
    	res = device_register(&client->dev);
    	if (adapter->client_register)  {
    		if (adapter->client_register(client)) {
    			dev_dbg(&adapter->dev, "client_register "
    				"failed for client [%s] at 0x%02x
    ",
    				client->name, client->addr);
    		}
    	}
    }
    

    设备驱动参考代码

    mark

    图片来自 https://www.cnblogs.com/lifexy/p/7816324.html

    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    #include <linux/jiffies.h>
    #include <linux/i2c.h>
    #include <linux/mutex.h>
    #include <linux/fs.h>
    #include <asm/uaccess.h>
    
    static unsigned short ignore[]      = { I2C_CLIENT_END };
    static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
                                            /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */
    
    static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
    static unsigned short * forces[] = {force_addr, NULL};
    										
    static struct i2c_client_address_data addr_data = {
    	.normal_i2c	= normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
    	.probe		= ignore,
    	.ignore		= ignore,
    	//.forces     = forces, /* 强制认为存在这个设备 */
    };
    
    static struct i2c_driver at24cxx_driver;
    
    
    static int major;
    static struct class *cls;
    struct i2c_client *at24cxx_client;
    
    static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
    {
    	unsigned char address;
    	unsigned char data;
    	struct i2c_msg msg[2];
    	int ret;
    	
    	/* address = buf[0] 
    	 * data    = buf[1]
    	 */
    	if (size != 1)
    		return -EINVAL;
    	
    	copy_from_user(&address, buf, 1);
    
    	/* 数据传输三要素: 源,目的,长度 */
    
    	/* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
    	msg[0].addr  = at24cxx_client->addr;  /* 目的 */
    	msg[0].buf   = &address;              /* 源 */
    	msg[0].len   = 1;                     /* 地址=1 byte */
    	msg[0].flags = 0;                     /* 表示写 */
    
    	/* 然后启动读操作 */
    	msg[1].addr  = at24cxx_client->addr;  /* 源 */
    	msg[1].buf   = &data;                 /* 目的 */
    	msg[1].len   = 1;                     /* 数据=1 byte */
    	msg[1].flags = I2C_M_RD;                     /* 表示读 */
    
    
    	ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
    	if (ret == 2)
    	{
    		copy_to_user(buf, &data, 1);
    		return 1;
    	}
    	else
    		return -EIO;
    }
    
    static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
    {
    	unsigned char val[2];
    	struct i2c_msg msg[1];
    	int ret;
    	
    	/* address = buf[0] 
    	 * data    = buf[1]
    	 */
    	if (size != 2)
    		return -EINVAL;
    	
    	copy_from_user(val, buf, 2);
    
    	/* 数据传输三要素: 源,目的,长度 */
    	msg[0].addr  = at24cxx_client->addr;  /* 目的 */
    	msg[0].buf   = val;                   /* 源 */
    	msg[0].len   = 2;                     /* 地址+数据=2 byte */
    	msg[0].flags = 0;                     /* 表示写 */
    
    	ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
    	if (ret == 1)
    		return 2;
    	else
    		return -EIO;
    }
    
    
    static struct file_operations at24cxx_fops = {
    	.owner = THIS_MODULE,
    	.read  = at24cxx_read,
    	.write = at24cxx_write,
    };
    
    static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
    {	
    	printk("at24cxx_detect
    ");
    
    	/* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
    	at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
    	at24cxx_client->addr    = address;
    	at24cxx_client->adapter = adapter;
    	at24cxx_client->driver  = &at24cxx_driver;
    	strcpy(at24cxx_client->name, "at24cxx");
    	i2c_attach_client(at24cxx_client);
    	
    	major = register_chrdev(0, "at24cxx", &at24cxx_fops);
    
    	cls = class_create(THIS_MODULE, "at24cxx");
    	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
    	
    	return 0;
    }
    
    static int at24cxx_attach(struct i2c_adapter *adapter)
    {
    	return i2c_probe(adapter, &addr_data, at24cxx_detect);
    }
    
    static int at24cxx_detach(struct i2c_client *client)
    {
    	printk("at24cxx_detach
    ");
    	class_device_destroy(cls, MKDEV(major, 0));
    	class_destroy(cls);
    	unregister_chrdev(major, "at24cxx");
    
    	i2c_detach_client(client);
    	kfree(i2c_get_clientdata(client));
    
    	return 0;
    }
    
    
    /* 1. 分配一个i2c_driver结构体 */
    /* 2. 设置i2c_driver结构体 */
    static struct i2c_driver at24cxx_driver = {
    	.driver = {
    		.name	= "at24cxx",
    	},
    	.attach_adapter = at24cxx_attach,
    	.detach_client  = at24cxx_detach,
    };
    
    static int at24cxx_init(void)
    {
    	i2c_add_driver(&at24cxx_driver);
    	return 0;
    }
    
    static void at24cxx_exit(void)
    {
    	i2c_del_driver(&at24cxx_driver);
    }
    
    module_init(at24cxx_init);
    module_exit(at24cxx_exit);
    
    MODULE_LICENSE("GPL");
    
    
    
  • 相关阅读:
    Service Discovery
    Spring security框架原理
    Redis作者谈Redis应用场景
    redis持久化RDB和AOF-转载
    MongoDB树形结构表示法
    Tomcat Connector
    ActiveMQ 负载均衡与高可用(转载)
    JS选取DOM元素的方法
    IObit Driver Booster 无法更新驱动的解决办法
    python 学习备忘
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10334607.html
Copyright © 2020-2023  润新知