【驱动】第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

 

posted @ 2019-07-21 18:20  大秦长剑  阅读(646)  评论(0编辑  收藏  举报