【驱动】第32课、Linux 3.4.2 IIC驱动
主 机:VMWare--Ubuntu-16.04.2-x64-100ask
开发板:Mini2440--256M NandFlash,   2M NorFlash,   64M SDRAM,   LCD-TD35;
    bootlorder:u-boot1.16,        Kernel:2.6.22.6;
编译器:arm-linux-gcc-4.3.2
节一、第一个方法1th源码--Method 1: Declare the I2C devices by bus number
1、专属函数说明
1.1/**
 * i2c_new_device - instantiate an i2c device
 * @adap: the adapter managing the device		;管理设备的适配器
 * @info: describes one I2C device; bus_num is ignored
 * Context: can sleep
 *
 * Create an i2c device. Binding is handled through driver model
 * probe()/remove() methods.  A driver may be bound to this device when we
 * return from this function, or any later moment (e.g. maybe hotplugging will
 * load the driver module).  This call is not appropriate for use by mainboard
 * initialization logic, which usually runs during an arch_initcall() long
 * before any i2c_adapter could exist.
 * 翻译:创建一个i2c设备。绑定是通过驱动程序模型探针()/remove()方法来处理的。当我们从这个函数返回时,驱动程序可能绑定到这个设备,
 * 或者稍后的某个时刻(例如,热插拔可能会加载驱动程序模块)。主板初始化逻辑不适合使用这个调用,它通常在arch_initcall()期间运行,
 * 比任何i2c_adapter都要早得多。
 * This returns the new i2c client, which may be saved for later use with
 * i2c_unregister_device(); or NULL to indicate an error.
 */
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{...}
1.2/**
 * struct i2c_client - represent an I2C slave device; 表示一个I2C从设备
 * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
 *	I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
 * @addr: Address used on the I2C bus connected to the parent adapter.
 * @name: Indicates the type of the device, usually a chip name that's
 *	generic enough to hide second-sourcing and compatible revisions.
 * @adapter: manages the bus segment hosting this I2C device
 * @driver: device's driver, hence pointer to access routines
 * @dev: Driver model device node for the slave.
 * @irq: indicates the IRQ generated by this device (if any)
 * @detected: member of an i2c_driver.clients list or i2c-core's
 *	userspace_devices list
 *
 * An i2c_client identifies a single device (i.e. chip) connected to an
 * i2c bus. The behaviour exposed to Linux is defined by the driver
 * managing the device.
 翻译: i2c_client标识连接到i2c总线的单个设备(即芯片)。向Linux公开的行为由管理设备的驱动程序定义。
 */
struct i2c_client {
	unsigned short flags;		/* div., see below		*/
	unsigned short addr;		/* chip address - NOTE: 7bit	*/
					/* addresses are stored in the	*/
					/* _LOWER_7 bits		*/
	char name[I2C_NAME_SIZE];
	struct i2c_adapter *adapter;	/* the adapter we sit on	*/
	struct i2c_driver *driver;	/* and our access routines	*/
	struct device dev;		/* the device structure		*/
	int irq;			/* irq issued by device		*/
	struct list_head detected;
};
1.3/**
 * struct i2c_board_info - template for device creation  ;设备创建模板
 * @type: chip type, to initialize i2c_client.name
 * @flags: to initialize i2c_client.flags
 * @addr: stored in i2c_client.addr
 * @platform_data: stored in i2c_client.dev.platform_data
 * @archdata: copied into i2c_client.dev.archdata
 * @of_node: pointer to OpenFirmware device node
 * @irq: stored in i2c_client.irq
 *
 * I2C doesn't actually support hardware probing, although controllers and
 * devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's
 * a device at a given address.  Drivers commonly need more information than
 * that, such as chip type, configuration, associated IRQ, and so on.
 *
 * i2c_board_info is used to build tables of information listing I2C devices
 * that are present.  This information is used to grow the driver model tree.
 * For mainboards this is done statically using i2c_register_board_info();
 * bus numbers identify adapters that aren't yet available.  For add-on boards,
 * i2c_new_device() does this dynamically with the adapter already known.
 */
struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;
	unsigned short	addr;
	void		*platform_data;
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	int		irq;
};
1.4、头文件说明
#include <linux/init.h>		/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
#include <linux/module.h>	/*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
 
/*定义module_param module_param_array的头文件*/
#include <linux/moduleparam.h>
 
/*定义module_param module_param_array中perm的头文件*/
#include <linux/stat.h>
 
/*三个字符设备函数*/
#include <linux/fs.h>
 
/*MKDEV转换设备号数据类型的宏定义*/
#include <linux/kdev_t.h>
 
/*定义字符设备的结构体*/
#include <linux/cdev.h>
 
/*分配内存空间函数头文件*/
#include <linux/slab.h>
 
/*包含函数device_create 结构体class等头文件*/
#include <linux/device.h>
2、问题
2.1 知识问题
2.2 调试问题及修改
2.2.1 驱动模块加载失败
# insmod at24cxx_drv.ko
# insmod at24cxx_dev.ko
i2c i2c-0: Failed to register i2c client at24c08 at 0x50 (-16)
Failed to get new i2c device.		//at24cxx_client = i2c_new_device(adap, &at24cxx_info)函数的返回值判断打印;
insmod: can't insert 'at24cxx_dev12.ko': No such device
解决:参考https://blog.csdn.net/guocaigao/article/details/8011537
打log跟踪,是i2c_new_device函数中i2c_check_addr_busy后打出来的,发现将地址改为0x60可以正常加载,看来是有人占用了0x50这个地址
修改后:
# insmod at24cxx_drv.ko
# insmod at24cxx_dev.ko
/home/book/workbook/mini2440/2to3th_drivers/32_i2c/32_1th_i2c_new_device_2/at24cxx_drv12.c,  at24cxx_drv_probe,   16
# rmmod at24cxx_dev12
/home/book/workbook/mini2440/2to3th_drivers/32_i2c/32_1th_i2c_new_device_2/at24cxx_drv12.c,  at24cxx_drv_remove,  21
Successfully cancel the new i2c device at24cxx_client.		//i2c_unregister_device(at24cxx_client);函数之后的打印说明;
# rmmod at24cxx_drv12
<结束>
内核配置:
--I2C Hardware Bus support 
*** I2C system bus drivers (mostly embedded / system-on-chip) ***           
  < > Synopsys DesignWare Platfrom                                              
  < > GPIO-based bitbanging I2C                                                 
  < > PCA9564/PCA9665 as platform device                                        
  <*> S3C2410 I2C Driver                                                        
  <*> Simtec Generic I2C interface                                              
      *** External I2C/SMBus adapter drivers ***                                
  < > Diolan U2C-12 USB adapter                                                 
  < > Parallel port adapter (light)                                             
  < > Tiny-USB adapter                                                          
      *** Other I2C/SMBus bus drivers ***   
一种处理同一个I2C总线上,器件地址冲突的方法:
最近做项目,某个器件需要二供,可能出现混贴,不巧的是硬件将这些二供器件的I2C地址都做成了一样的,这样就导致,I2C器件在board文件的
注册,只会注册第一次出现的地方,解决的办法是,在I2C_BOARD_INFO注册时,为相同地址的器件填写不同的地址,在probe的时候在初始化到正
确的地址。比如混贴的两个器件地址都是0x0D,I2C_BOARD_INFO中将其中一个(后一个)改为,0x0E, probe的时候,再把0x0D,重新赋值过来。
目前只在混贴的情况下测试是正常的,同时出现还没有测试过。
2.2.2 卸载问题
# rmmod at24cxx_drv
# rmmod at24cxx_dev
Unable to handle kernel NULL pointer dereference at virtual address 0000006c
...
打印的错误信息是:Unable to handle kernel NULL pointer dereference at virtual address 0000006c
应该是代码中,使用了某个结构体指针,但是这上指针未初始化。多加些调试信息,可以很快确定在哪一行代码中出错
---------------------
第二节、第二个方法2th源码--Method 2: Instantiate the devices explicitly
1、专属函数说明
1.1
/**
 * memset - Fill a region of memory with the given value
 * @s: Pointer to the start of the area.
 * @c: The byte to fill the area with
 * @count: The size of the area.
 *
 * Do not use memset() to access IO space, use memset_io() instead.
 */
void *memset(void *s, int c, size_t count)
{
	char *xs = s;
	while (count--)
		*xs++ = c;
	return s;
}
2、问题
2.1知识问题
2.2编译问题
问题1:
# insmod at24cxx_drv.ko
# insmod at24cxx_dev.ko
insmod: can't insert 'at24cxx_dev.ko': No such device
解决方法1:配置内核,去掉内核自带的i2c总线驱动
Device Drivers
    I2C support
        I2C Hardware Bus support
            < > S3C2410 I2C Driver		//原来为:<*> S3C2410 I2C Driver	
SMDK2410 # nfs 30000000 192.168.1.105:/work/nfs_root/uImage_noi2c_3.4.2; bootm 30000000
# insmod at24cxx_drv21.ko
# insmod at24cxx_dev21.ko
Failed to get i2c adapter 0.
insmod: can't insert 'at24cxx_dev21.ko': No such device
解决失败!原因??
解决方法2:修改Mach-mini2440.c (arch\arm\mach-s3c24xx)文件:
屏蔽下面的模块
#if 0
/*
 * I2C devices
 */
#endif
执行 $ make uImage 命令时,将出现内核编译错误:
arch/arm/mach-s3c24xx/mach-mini2440.c: In function 'mini2440_init':
arch/arm/mach-s3c24xx/mach-mini2440.c:688: error: 'mini2440_i2c_devs' undeclared (first use in this function)
arch/arm/mach-s3c24xx/mach-mini2440.c:688: error: (Each undeclared identifier is reported only once
arch/arm/mach-s3c24xx/mach-mini2440.c:688: error: for each function it appears in.)
... ...
解决方法3:修改Mach-mini2440.c (arch\arm\mach-s3c24xx)文件:
/* I2C devices */
...
static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
	{
		I2C_BOARD_INFO("24c08", 0x60),		//修改内核源码为0x50为0x60;
		.platform_data = &at24c08,
	},
};
...		;重新加载i2c设备驱动模块:
# insmod at24cxx_drv21.ko
# insmod at24cxx_dev21.ko
/home/book/workbook/mini2440/2to3th_drivers/32_i2c/32_2th_i2c_new_probe_device_1/at24cxx_drv21.c,  at24cxx_drv_probe,  16
如此,加载模块at24cxx_drv.ko 和 at24cxx_dev.ko成功!
总结:应该是设备号冲突,但内部的具体冲突原因及其作用机理是什么?有时间求证一下。
节三、第四个方法(实验无源码)--Method 4: Instantiate from user-space
Example:
# echo eeprom 0x50 > /sys/bus/i2c/devices/i2c-3/new_device
1、创建设备
# echo at24c08 0x50 > /sys/bus/i2c/devices/i2c-0/new_device
上机演示如下:
# echo at24c08 0x50 > /sys/bus/i2c/devices/i2c-0/new_device
/home/book/workbook/mini2440/2to3th_drivers/32_i2c/32_2th_i2c_new_probe_device_2/at24cxx_drv22.c,  at24cxx_drv_probe,  16
i2c i2c-0: new_device: Instantiated device at24c08 at 0x50
2、注销设备
# echo 0x50 > /sys/bus/i2c/devices/i2c-0/delete_device
演示如下:
# echo 0x50 > /sys/bus/i2c/devices/i2c-0/delete_device
i2c i2c-0: delete_device: Deleting device at24c08 at 0x50
/home/book/workbook/mini2440/2to3th_drivers/32_i2c/32_2th_i2c_new_probe_device_2/at24cxx_drv22.c,  at24cxx_drv_remove,  21
节四、第三个方法--Method 3: Probe an I2C bus for certain devices
Example:
See lm90_driver and lm90_detect() in drivers/hwmon/lm90.c
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号